2 Distributed under both the W3C Test Suite License [1] and the W3C
3 3-clause BSD License [2]. To contribute to a W3C Test Suite, see the
4 policies and contribution forms [3].
6 [1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license
7 [2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license
8 [3] http://www.w3.org/2004/10/27-testcases
11 /* For user documentation see docs/idlharness.md */
14 * Notes for people who want to edit this file (not just use it as a library):
16 * Most of the interesting stuff happens in the derived classes of IdlObject,
17 * especially IdlInterface. The entry point for all IdlObjects is .test(),
18 * which is called by IdlArray.test(). An IdlObject is conceptually just
19 * "thing we want to run tests on", and an IdlArray is an array of IdlObjects
20 * with some additional data thrown in.
22 * The object model is based on what WebIDLParser.js produces, which is in turn
23 * based on its pegjs grammar. If you want to figure out what properties an
24 * object will have from WebIDLParser.js, the best way is to look at the
27 * https://github.com/darobin/webidl.js/blob/master/lib/grammar.peg
31 * // interface definition
33 * = extAttrs:extendedAttributeList? S? "interface" S name:identifier w herit:ifInheritance? w "{" w mem:ifMember* w "}" w ";" w
34 * { return { type: "interface", name: name, inheritance: herit, members: mem, extAttrs: extAttrs }; }
36 * This means that an "interface" object will have a .type property equal to
37 * the string "interface", a .name property equal to the identifier that the
38 * parser found, an .inheritance property equal to either null or the result of
39 * the "ifInheritance" production found elsewhere in the grammar, and so on.
40 * After each grammatical production is a JavaScript function in curly braces
41 * that gets called with suitable arguments and returns some JavaScript value.
43 * (Note that the version of WebIDLParser.js we use might sometimes be
44 * out-of-date or forked.)
46 * The members and methods of the classes defined by this file are all at least
47 * briefly documented, hopefully.
52 function constValue (cnt) {
53 if (cnt.type === "null") return null;
54 if (cnt.type === "NaN") return NaN;
55 if (cnt.type === "Infinity") return cnt.negative ? -Infinity : Infinity;
61 window.IdlArray = function()
65 * A map from strings to the corresponding named IdlObject, such as
66 * IdlInterface or IdlException. These are the things that test() will run
72 * A map from strings to arrays of strings. The keys are interface or
73 * exception names, and are expected to also exist as keys in this.members
74 * (otherwise they'll be ignored). This is populated by add_objects() --
75 * see documentation at the start of the file. The actual tests will be
76 * run by calling this.members[name].test_object(obj) for each obj in
77 * this.objects[name]. obj is a string that will be eval'd to produce a
78 * JavaScript value, which is supposed to be an object implementing the
79 * given IdlObject (interface, exception, etc.).
84 * When adding multiple collections of IDLs one at a time, an earlier one
85 * might contain a partial interface or implements statement that depends
86 * on a later one. Save these up and handle them right before we run
89 * .partials is simply an array of objects from WebIDLParser.js'
90 * "partialinterface" production. .implements maps strings to arrays of
97 * results in { A: ["B", "C"], D: ["E"] }.
100 this["implements"] = {};
104 IdlArray.prototype.add_idls = function(raw_idls)
107 /** Entry point. See documentation at beginning of file. */
108 this.internal_add_idls(WebIDL2.parse(raw_idls));
112 IdlArray.prototype.add_untested_idls = function(raw_idls)
115 /** Entry point. See documentation at beginning of file. */
116 var parsed_idls = WebIDL2.parse(raw_idls);
117 for (var i = 0; i < parsed_idls.length; i++)
119 parsed_idls[i].untested = true;
120 if ("members" in parsed_idls[i])
122 for (var j = 0; j < parsed_idls[i].members.length; j++)
124 parsed_idls[i].members[j].untested = true;
128 this.internal_add_idls(parsed_idls);
132 IdlArray.prototype.internal_add_idls = function(parsed_idls)
136 * Internal helper called by add_idls() and add_untested_idls().
137 * parsed_idls is an array of objects that come from WebIDLParser.js's
138 * "definitions" production. The add_untested_idls() entry point
139 * additionally sets an .untested property on each object (and its
140 * .members) so that they'll be skipped by test() -- they'll only be
141 * used for base interfaces of tested interfaces, return types, etc.
143 parsed_idls.forEach(function(parsed_idl)
145 if (parsed_idl.type == "interface" && parsed_idl.partial)
147 this.partials.push(parsed_idl);
151 if (parsed_idl.type == "implements")
153 if (!(parsed_idl.target in this["implements"]))
155 this["implements"][parsed_idl.target] = [];
157 this["implements"][parsed_idl.target].push(parsed_idl["implements"]);
161 parsed_idl.array = this;
162 if (parsed_idl.name in this.members)
164 throw "Duplicate identifier " + parsed_idl.name;
166 switch(parsed_idl.type)
169 this.members[parsed_idl.name] = new IdlInterface(parsed_idl);
173 this.members[parsed_idl.name] = new IdlException(parsed_idl);
177 // Nothing to test, but we need the dictionary info around for type
179 this.members[parsed_idl.name] = new IdlDictionary(parsed_idl);
183 this.members[parsed_idl.name] = new IdlTypedef(parsed_idl);
188 console.log("callback not yet supported");
192 this.members[parsed_idl.name] = new IdlEnum(parsed_idl);
195 case "callback interface":
197 console.log("callback interface not yet supported");
201 throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported";
207 IdlArray.prototype.add_objects = function(dict)
210 /** Entry point. See documentation at beginning of file. */
213 if (k in this.objects)
215 this.objects[k] = this.objects[k].concat(dict[k]);
219 this.objects[k] = dict[k];
225 IdlArray.prototype.prevent_multiple_testing = function(name)
228 /** Entry point. See documentation at beginning of file. */
229 this.members[name].prevent_multiple_testing = true;
233 IdlArray.prototype.recursively_get_implements = function(interface_name)
237 * Helper function for test(). Returns an array of things that implement
238 * interface_name, so if the IDL contains
244 * then recursively_get_implements("A") should return ["B", "C", "D"].
246 var ret = this["implements"][interface_name];
247 if (ret === undefined)
251 for (var i = 0; i < this["implements"][interface_name].length; i++)
253 ret = ret.concat(this.recursively_get_implements(ret[i]));
254 if (ret.indexOf(ret[i]) != ret.lastIndexOf(ret[i]))
256 throw "Circular implements statements involving " + ret[i];
263 IdlArray.prototype.test = function()
266 /** Entry point. See documentation at beginning of file. */
268 // First merge in all the partial interfaces and implements statements we
270 this.partials.forEach(function(parsed_idl)
272 if (!(parsed_idl.name in this.members)
273 || !(this.members[parsed_idl.name] instanceof IdlInterface))
275 throw "Partial interface " + parsed_idl.name + " with no original interface";
277 if (parsed_idl.extAttrs)
279 parsed_idl.extAttrs.forEach(function(extAttr)
281 this.members[parsed_idl.name].extAttrs.push(extAttr);
284 parsed_idl.members.forEach(function(member)
286 this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member));
291 for (var lhs in this["implements"])
293 this.recursively_get_implements(lhs).forEach(function(rhs)
295 var errStr = lhs + " implements " + rhs + ", but ";
296 if (!(lhs in this.members)) throw errStr + lhs + " is undefined.";
297 if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface.";
298 if (!(rhs in this.members)) throw errStr + rhs + " is undefined.";
299 if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface.";
300 this.members[rhs].members.forEach(function(member)
302 this.members[lhs].members.push(new IdlInterfaceMember(member));
306 this["implements"] = {};
308 // Now run test() on every member, and test_object() for every object.
309 for (var name in this.members)
311 this.members[name].test();
312 if (name in this.objects)
314 this.objects[name].forEach(function(str)
316 this.members[name].test_object(str);
323 IdlArray.prototype.assert_type_is = function(value, type)
327 * Helper function that tests that value is an instance of type according
328 * to the rules of WebIDL. value is any JavaScript value, and type is an
329 * object produced by WebIDLParser.js' "type" production. That production
330 * is fairly elaborate due to the complexity of WebIDL's types, so it's
331 * best to look at the grammar to figure out what properties it might have.
333 if (type.idlType == "any")
335 // No assertions to make
339 if (type.nullable && value === null)
347 // TODO: not supported yet
353 assert_true(Array.isArray(value), "is not array");
356 // Nothing we can do.
359 this.assert_type_is(value[0], type.idlType.idlType);
368 assert_equals(value, undefined);
372 assert_equals(typeof value, "boolean");
376 assert_equals(typeof value, "number");
377 assert_equals(value, Math.floor(value), "not an integer");
378 assert_true(-128 <= value && value <= 127, "byte " + value + " not in range [-128, 127]");
382 assert_equals(typeof value, "number");
383 assert_equals(value, Math.floor(value), "not an integer");
384 assert_true(0 <= value && value <= 255, "octet " + value + " not in range [0, 255]");
388 assert_equals(typeof value, "number");
389 assert_equals(value, Math.floor(value), "not an integer");
390 assert_true(-32768 <= value && value <= 32767, "short " + value + " not in range [-32768, 32767]");
393 case "unsigned short":
394 assert_equals(typeof value, "number");
395 assert_equals(value, Math.floor(value), "not an integer");
396 assert_true(0 <= value && value <= 65535, "unsigned short " + value + " not in range [0, 65535]");
400 assert_equals(typeof value, "number");
401 assert_equals(value, Math.floor(value), "not an integer");
402 assert_true(-2147483648 <= value && value <= 2147483647, "long " + value + " not in range [-2147483648, 2147483647]");
405 case "unsigned long":
406 assert_equals(typeof value, "number");
407 assert_equals(value, Math.floor(value), "not an integer");
408 assert_true(0 <= value && value <= 4294967295, "unsigned long " + value + " not in range [0, 4294967295]");
412 assert_equals(typeof value, "number");
415 case "unsigned long long":
416 assert_equals(typeof value, "number");
417 assert_true(0 <= value, "unsigned long long is negative");
422 case "unrestricted float":
423 case "unrestricted double":
424 // TODO: distinguish these cases
425 assert_equals(typeof value, "number");
431 // TODO: https://github.com/w3c/testharness.js/issues/92
432 assert_equals(typeof value, "string");
436 assert_true(typeof value == "object" || typeof value == "function", "wrong type: not object or function");
440 if (!(type in this.members))
442 throw "Unrecognized type " + type;
445 if (this.members[type] instanceof IdlInterface)
447 // We don't want to run the full
448 // IdlInterface.prototype.test_instance_of, because that could result
449 // in an infinite loop. TODO: This means we don't have tests for
450 // NoInterfaceObject interfaces, and we also can't test objects that
451 // come from another window.
452 assert_true(typeof value == "object" || typeof value == "function", "wrong type: not object or function");
453 if (value instanceof Object
454 && !this.members[type].has_extended_attribute("NoInterfaceObject")
457 assert_true(value instanceof window[type], "not instanceof " + type);
460 else if (this.members[type] instanceof IdlEnum)
462 assert_equals(typeof value, "string");
464 else if (this.members[type] instanceof IdlDictionary)
466 // TODO: Test when we actually have something to test this on
468 else if (this.members[type] instanceof IdlTypedef)
470 // TODO: Test when we actually have something to test this on
474 throw "Type " + type + " isn't an interface or dictionary";
480 function IdlObject() {}
481 IdlObject.prototype.test = function()
485 * By default, this does nothing, so no actual tests are run for IdlObjects
486 * that don't define any (e.g., IdlDictionary at the time of this writing).
491 IdlObject.prototype.has_extended_attribute = function(name)
495 * This is only meaningful for things that support extended attributes,
496 * such as interfaces, exceptions, and members.
498 return this.extAttrs.some(function(o)
500 return o.name == name;
506 /// IdlDictionary ///
507 // Used for IdlArray.prototype.assert_type_is
508 function IdlDictionary(obj)
512 * obj is an object produced by the WebIDLParser.js "dictionary"
516 /** Self-explanatory. */
517 this.name = obj.name;
519 /** An array of objects produced by the "dictionaryMember" production. */
520 this.members = obj.members;
523 * The name (as a string) of the dictionary type we inherit from, or null
526 this.base = obj.inheritance;
530 IdlDictionary.prototype = Object.create(IdlObject.prototype);
532 /// IdlExceptionOrInterface ///
534 function IdlExceptionOrInterface(obj)
538 * obj is an object produced by the WebIDLParser.js "exception" or
539 * "interface" production, as appropriate.
542 /** Self-explanatory. */
543 this.name = obj.name;
545 /** A back-reference to our IdlArray. */
546 this.array = obj.array;
549 * An indicator of whether we should run tests on the (exception) interface
550 * object and (exception) interface prototype object. Tests on members are
551 * controlled by .untested on each member, not this.
553 this.untested = obj.untested;
555 /** An array of objects produced by the "ExtAttr" production. */
556 this.extAttrs = obj.extAttrs;
558 /** An array of IdlInterfaceMembers. */
559 this.members = obj.members.map(function(m){return new IdlInterfaceMember(m); });
562 * The name (as a string) of the type we inherit from, or null if there is
565 this.base = obj.inheritance;
569 IdlExceptionOrInterface.prototype = Object.create(IdlObject.prototype);
570 IdlExceptionOrInterface.prototype.test = function()
573 if (this.has_extended_attribute("NoInterfaceObject"))
575 // No tests to do without an instance. TODO: We should still be able
576 // to run tests on the prototype object, if we obtain one through some
583 // First test things to do with the exception/interface object and
584 // exception/interface prototype object.
587 // Then test things to do with its members (constants, fields, attributes,
588 // operations, . . .). These are run even if .untested is true, because
589 // members might themselves be marked as .untested. This might happen to
590 // interfaces if the interface itself is untested but a partial interface
591 // that extends it is tested -- then the interface itself and its initial
592 // members will be marked as untested, but the members added by the partial
593 // interface are still tested.
600 function IdlException(obj) { IdlExceptionOrInterface.call(this, obj); }
601 IdlException.prototype = Object.create(IdlExceptionOrInterface.prototype);
602 IdlException.prototype.test_self = function()
607 // "For every exception that is not declared with the
608 // [NoInterfaceObject] extended attribute, a corresponding property
609 // must exist on the exception’s relevant namespace object. The name of
610 // the property is the identifier of the exception, and its value is an
611 // object called the exception interface object, which provides access
612 // to any constants that have been associated with the exception. The
613 // property has the attributes { [[Writable]]: true, [[Enumerable]]:
614 // false, [[Configurable]]: true }."
615 assert_own_property(window, this.name,
616 "window does not have own property " + format_value(this.name));
617 var desc = Object.getOwnPropertyDescriptor(window, this.name);
618 assert_false("get" in desc, "window's property " + format_value(this.name) + " has getter");
619 assert_false("set" in desc, "window's property " + format_value(this.name) + " has setter");
620 assert_true(desc.writable, "window's property " + format_value(this.name) + " is not writable");
621 assert_false(desc.enumerable, "window's property " + format_value(this.name) + " is enumerable");
622 assert_true(desc.configurable, "window's property " + format_value(this.name) + " is not configurable");
624 // "The exception interface object for a given exception must be a
626 // "If an object is defined to be a function object, then it has
627 // characteristics as follows:"
628 // "Its [[Prototype]] internal property is the Function prototype
630 // Note: This doesn't match browsers as of December 2011, see
631 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=14813
632 assert_equals(Object.getPrototypeOf(window[this.name]), Function.prototype,
633 "prototype of window's property " + format_value(this.name) + " is not Function.prototype");
634 // "Its [[Get]] internal property is set as described in ECMA-262
635 // section 15.3.5.4."
636 // Not much to test for this.
637 // "Its [[Construct]] internal property is set as described in ECMA-262
640 // "Its [[HasInstance]] internal property is set as described in
641 // ECMA-262 section 15.3.5.3, unless otherwise specified."
643 // "Its [[Class]] internal property is “Function”."
644 // String() returns something implementation-dependent, because it
645 // calls Function#toString.
646 assert_class_string(window[this.name], "Function",
647 "class string of " + this.name);
649 // TODO: Test 4.9.1.1. Exception interface object [[Call]] method
650 // (which does not match browsers:
651 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=14885)
652 }.bind(this), this.name + " exception: existence and properties of exception interface object");
656 assert_own_property(window, this.name,
657 "window does not have own property " + format_value(this.name));
659 // "The exception interface object must also have a property named
660 // “prototype” with attributes { [[Writable]]: false, [[Enumerable]]:
661 // false, [[Configurable]]: false } whose value is an object called the
662 // exception interface prototype object. This object also provides
663 // access to the constants that are declared on the exception."
664 assert_own_property(window[this.name], "prototype",
665 'exception "' + this.name + '" does not have own property "prototype"');
666 var desc = Object.getOwnPropertyDescriptor(window[this.name], "prototype");
667 assert_false("get" in desc, this.name + ".prototype has getter");
668 assert_false("set" in desc, this.name + ".prototype has setter");
669 assert_false(desc.writable, this.name + ".prototype is writable");
670 assert_false(desc.enumerable, this.name + ".prototype is enumerable");
671 assert_false(desc.configurable, this.name + ".prototype is configurable");
673 // "The exception interface prototype object for a given exception must
674 // have an internal [[Prototype]] property whose value is as follows:
676 // "If the exception is declared to inherit from another exception,
677 // then the value of the internal [[Prototype]] property is the
678 // exception interface prototype object for the inherited exception.
679 // "Otherwise, the exception is not declared to inherit from another
680 // exception. The value of the internal [[Prototype]] property is the
681 // Error prototype object ([ECMA-262], section 15.11.3.1)."
683 // Note: This doesn't match browsers as of December 2011, see
684 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=14887.
685 var inherit_exception = this.base ? this.base : "Error";
686 assert_own_property(window, inherit_exception,
687 'should inherit from ' + inherit_exception + ', but window has no such property');
688 assert_own_property(window[inherit_exception], "prototype",
689 'should inherit from ' + inherit_exception + ', but that object has no "prototype" property');
690 assert_equals(Object.getPrototypeOf(window[this.name].prototype),
691 window[inherit_exception].prototype,
692 'prototype of ' + this.name + '.prototype is not ' + inherit_exception + '.prototype');
694 // "The class string of an exception interface prototype object is the
695 // concatenation of the exception’s identifier and the string
697 assert_class_string(window[this.name].prototype, this.name + "Prototype",
698 "class string of " + this.name + ".prototype");
699 // TODO: Test String(), based on ES definition of
700 // Error.prototype.toString?
701 }.bind(this), this.name + " exception: existence and properties of exception interface prototype object");
705 assert_own_property(window, this.name,
706 "window does not have own property " + format_value(this.name));
707 assert_own_property(window[this.name], "prototype",
708 'interface "' + this.name + '" does not have own property "prototype"');
710 // "There must be a property named “name” on the exception interface
711 // prototype object with attributes { [[Writable]]: true,
712 // [[Enumerable]]: false, [[Configurable]]: true } and whose value is
713 // the identifier of the exception."
714 assert_own_property(window[this.name].prototype, "name",
715 'prototype object does not have own property "name"');
716 var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, "name");
717 assert_false("get" in desc, this.name + ".prototype.name has getter");
718 assert_false("set" in desc, this.name + ".prototype.name has setter");
719 assert_true(desc.writable, this.name + ".prototype.name is not writable");
720 assert_false(desc.enumerable, this.name + ".prototype.name is enumerable");
721 assert_true(desc.configurable, this.name + ".prototype.name is not configurable");
722 assert_equals(desc.value, this.name, this.name + ".prototype.name has incorrect value");
723 }.bind(this), this.name + " exception: existence and properties of exception interface prototype object's \"name\" property");
727 assert_own_property(window, this.name,
728 "window does not have own property " + format_value(this.name));
729 assert_own_property(window[this.name], "prototype",
730 'interface "' + this.name + '" does not have own property "prototype"');
732 // "If the [NoInterfaceObject] extended attribute was not specified on
733 // the exception, then there must also be a property named
734 // “constructor” on the exception interface prototype object with
735 // attributes { [[Writable]]: true, [[Enumerable]]: false,
736 // [[Configurable]]: true } and whose value is a reference to the
737 // exception interface object for the exception."
738 assert_own_property(window[this.name].prototype, "constructor",
739 this.name + '.prototype does not have own property "constructor"');
740 var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, "constructor");
741 assert_false("get" in desc, this.name + ".prototype.constructor has getter");
742 assert_false("set" in desc, this.name + ".prototype.constructor has setter");
743 assert_true(desc.writable, this.name + ".prototype.constructor is not writable");
744 assert_false(desc.enumerable, this.name + ".prototype.constructor is enumerable");
745 assert_true(desc.configurable, this.name + ".prototype.constructor in not configurable");
746 assert_equals(window[this.name].prototype.constructor, window[this.name],
747 this.name + '.prototype.constructor is not the same object as ' + this.name);
748 }.bind(this), this.name + " exception: existence and properties of exception interface prototype object's \"constructor\" property");
752 IdlException.prototype.test_members = function()
755 for (var i = 0; i < this.members.length; i++)
757 var member = this.members[i];
762 if (member.type == "const" && member.name != "prototype")
766 assert_own_property(window, this.name,
767 "window does not have own property " + format_value(this.name));
769 // "For each constant defined on the exception, there must be a
770 // corresponding property on the exception interface object, if
771 // it exists, if the identifier of the constant is not
773 assert_own_property(window[this.name], member.name);
774 // "The value of the property is the ECMAScript value that is
775 // equivalent to the constant’s IDL value, according to the
776 // rules in section 4.2 above."
777 assert_equals(window[this.name][member.name], constValue(member.value),
778 "property has wrong value");
779 // "The property has attributes { [[Writable]]: false,
780 // [[Enumerable]]: true, [[Configurable]]: false }."
781 var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name);
782 assert_false("get" in desc, "property has getter");
783 assert_false("set" in desc, "property has setter");
784 assert_false(desc.writable, "property is writable");
785 assert_true(desc.enumerable, "property is not enumerable");
786 assert_false(desc.configurable, "property is configurable");
787 }.bind(this), this.name + " exception: constant " + member.name + " on exception interface object");
788 // "In addition, a property with the same characteristics must
789 // exist on the exception interface prototype object."
792 assert_own_property(window, this.name,
793 "window does not have own property " + format_value(this.name));
794 assert_own_property(window[this.name], "prototype",
795 'exception "' + this.name + '" does not have own property "prototype"');
797 assert_own_property(window[this.name].prototype, member.name);
798 assert_equals(window[this.name].prototype[member.name], constValue(member.value),
799 "property has wrong value");
800 var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, member.name);
801 assert_false("get" in desc, "property has getter");
802 assert_false("set" in desc, "property has setter");
803 assert_false(desc.writable, "property is writable");
804 assert_true(desc.enumerable, "property is not enumerable");
805 assert_false(desc.configurable, "property is configurable");
806 }.bind(this), this.name + " exception: constant " + member.name + " on exception interface prototype object");
808 else if (member.type == "field")
812 assert_own_property(window, this.name,
813 "window does not have own property " + format_value(this.name));
814 assert_own_property(window[this.name], "prototype",
815 'exception "' + this.name + '" does not have own property "prototype"');
817 // "For each exception field, there must be a corresponding
818 // property on the exception interface prototype object, whose
819 // characteristics are as follows:
820 // "The name of the property is the identifier of the exception
822 assert_own_property(window[this.name].prototype, member.name);
823 // "The property has attributes { [[Get]]: G, [[Enumerable]]:
824 // true, [[Configurable]]: true }, where G is the exception
825 // field getter, defined below."
826 var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, member.name);
827 assert_false("value" in desc, "property descriptor has value but is supposed to be accessor");
828 assert_false("writable" in desc, 'property descriptor has "writable" field but is supposed to be accessor');
829 // TODO: ES5 doesn't seem to say whether desc should have a
831 assert_true(desc.enumerable, "property is not enumerable");
832 assert_true(desc.configurable, "property is not configurable");
833 // "The exception field getter is a Function object whose
834 // behavior when invoked is as follows:"
835 assert_equals(typeof desc.get, "function", "typeof getter");
836 // "The value of the Function object’s “length” property is the
838 // This test is before the TypeError tests so that it's easiest
839 // to see that Firefox 11a1 only fails one assert in this test.
840 assert_equals(desc.get.length, 0, "getter length");
841 // "Let O be the result of calling ToObject on the this value.
842 // "If O is not a platform object representing an exception for
843 // the exception on which the exception field was declared,
844 // then throw a TypeError."
845 // TODO: Test on a platform object representing an exception.
846 assert_throws(new TypeError(), function()
848 window[this.name].prototype[member.name];
849 }.bind(this), "getting property on prototype object must throw TypeError");
850 assert_throws(new TypeError(), function()
853 }.bind(this), "calling getter on wrong object type must throw TypeError");
854 }.bind(this), this.name + " exception: field " + member.name + " on exception interface prototype object");
860 IdlException.prototype.test_object = function(desc)
863 var obj, exception = null;
875 assert_equals(exception, null, "Unexpected exception when evaluating object");
876 assert_equals(typeof obj, "object", "wrong typeof object");
878 // We can't easily test that its prototype is correct if there's no
879 // interface object, or the object is from a different global
880 // environment (not instanceof Object). TODO: test in this case that
881 // its prototype at least looks correct, even if we can't test that
882 // it's actually correct.
883 if (!this.has_extended_attribute("NoInterfaceObject")
884 && (typeof obj != "object" || obj instanceof Object))
886 assert_own_property(window, this.name,
887 "window does not have own property " + format_value(this.name));
888 assert_own_property(window[this.name], "prototype",
889 'exception "' + this.name + '" does not have own property "prototype"');
891 // "The value of the internal [[Prototype]] property of the
892 // exception object must be the exception interface prototype
893 // object from the global environment the exception object is
895 assert_equals(Object.getPrototypeOf(obj),
896 window[this.name].prototype,
897 desc + "'s prototype is not " + this.name + ".prototype");
900 // "The class string of the exception object must be the identifier of
902 assert_class_string(obj, this.name, "class string of " + desc);
903 // Stringifier is not defined for DOMExceptions, because message isn't
905 }.bind(this), this.name + " must be represented by " + desc);
907 for (var i = 0; i < this.members.length; i++)
909 var member = this.members[i];
912 assert_equals(exception, null, "Unexpected exception when evaluating object");
913 assert_equals(typeof obj, "object", "wrong typeof object");
914 assert_inherits(obj, member.name);
915 if (member.type == "const")
917 assert_equals(obj[member.name], constValue(member.value));
919 if (member.type == "field")
921 this.array.assert_type_is(obj[member.name], member.idlType);
923 }.bind(this), this.name + " exception: " + desc + ' must inherit property "' + member.name + '" with the proper type');
929 function IdlInterface(obj) { IdlExceptionOrInterface.call(this, obj); }
930 IdlInterface.prototype = Object.create(IdlExceptionOrInterface.prototype);
931 IdlInterface.prototype.is_callback = function()
934 return this.has_extended_attribute("Callback");
938 IdlInterface.prototype.has_constants = function()
941 return this.members.some(function(member) {
942 return member.type === "const";
947 IdlInterface.prototype.test_self = function()
952 // This function tests WebIDL as of 2012-11-28.
954 // "For every interface that:
955 // * is a callback interface that has constants declared on it, or
956 // * is a non-callback interface that is not declared with the
957 // [NoInterfaceObject] extended attribute,
958 // a corresponding property MUST exist on the ECMAScript global object.
959 // The name of the property is the identifier of the interface, and its
960 // value is an object called the interface object.
961 // The property has the attributes { [[Writable]]: true,
962 // [[Enumerable]]: false, [[Configurable]]: true }."
963 if (this.is_callback() && !this.has_constants()) {
967 // TODO: Should we test here that the property is actually writable
968 // etc., or trust getOwnPropertyDescriptor?
969 assert_own_property(window, this.name,
970 "window does not have own property " + format_value(this.name));
971 var desc = Object.getOwnPropertyDescriptor(window, this.name);
972 assert_false("get" in desc, "window's property " + format_value(this.name) + " has getter");
973 assert_false("set" in desc, "window's property " + format_value(this.name) + " has setter");
974 assert_true(desc.writable, "window's property " + format_value(this.name) + " is not writable");
975 assert_false(desc.enumerable, "window's property " + format_value(this.name) + " is enumerable");
976 assert_true(desc.configurable, "window's property " + format_value(this.name) + " is not configurable");
978 if (this.is_callback()) {
979 // "The internal [[Prototype]] property of an interface object for
980 // a callback interface MUST be the Object.prototype object."
981 assert_equals(Object.getPrototypeOf(window[this.name]), Object.prototype,
982 "prototype of window's property " + format_value(this.name) + " is not Object.prototype");
987 // "The interface object for a given non-callback interface is a
989 // "If an object is defined to be a function object, then it has
990 // characteristics as follows:"
992 // "* Its [[Prototype]] internal property is the Function prototype
994 assert_equals(Object.getPrototypeOf(window[this.name]), Function.prototype,
995 "prototype of window's property " + format_value(this.name) + " is not Function.prototype");
997 // "* Its [[Get]] internal property is set as described in ECMA-262
998 // section 15.3.5.4."
999 // Not much to test for this.
1001 // "* Its [[Construct]] internal property is set as described in
1002 // ECMA-262 section 13.2.2."
1003 // Tested below if no constructor is defined. TODO: test constructors
1006 // "* Its [[HasInstance]] internal property is set as described in
1007 // ECMA-262 section 15.3.5.3, unless otherwise specified."
1010 // "* Its [[NativeBrand]] internal property is “Function”."
1011 // String() returns something implementation-dependent, because it calls
1012 // Function#toString.
1013 assert_class_string(window[this.name], "Function", "class string of " + this.name);
1015 if (!this.has_extended_attribute("Constructor")) {
1016 // "The internal [[Call]] method of the interface object behaves as
1019 // "If I was not declared with a [Constructor] extended attribute,
1020 // then throw a TypeError."
1021 assert_throws(new TypeError(), function() {
1022 window[this.name]();
1023 }.bind(this), "interface object didn't throw TypeError when called as a function");
1024 assert_throws(new TypeError(), function() {
1025 new window[this.name]();
1026 }.bind(this), "interface object didn't throw TypeError when called as a constructor");
1028 }.bind(this), this.name + " interface: existence and properties of interface object");
1030 if (!this.is_callback()) {
1032 // This function tests WebIDL as of 2014-10-25.
1033 // https://heycam.github.io/webidl/#es-interface-call
1035 assert_own_property(window, this.name,
1036 "window does not have own property " + format_value(this.name));
1038 // "Interface objects for non-callback interfaces MUST have a
1039 // property named “length” with attributes { [[Writable]]: false,
1040 // [[Enumerable]]: false, [[Configurable]]: true } whose value is
1042 assert_own_property(window[this.name], "length");
1043 var desc = Object.getOwnPropertyDescriptor(window[this.name], "length");
1044 assert_false("get" in desc, this.name + ".length has getter");
1045 assert_false("set" in desc, this.name + ".length has setter");
1046 assert_false(desc.writable, this.name + ".length is writable");
1047 assert_false(desc.enumerable, this.name + ".length is enumerable");
1048 assert_true(desc.configurable, this.name + ".length is not configurable");
1050 var constructors = this.extAttrs
1051 .filter(function(attr) { return attr.name == "Constructor"; });
1052 var expected_length;
1053 if (!constructors.length) {
1054 // "If the [Constructor] extended attribute, does not appear on
1055 // the interface definition, then the value is 0."
1056 expected_length = 0;
1058 // "Otherwise, the value is determined as follows: . . .
1059 // "Return the length of the shortest argument list of the
1061 expected_length = constructors.map(function(attr) {
1062 return attr.arguments ? attr.arguments.filter(function(arg) {
1063 return !arg.optional;
1066 .reduce(function(m, n) { return Math.min(m, n); });
1068 assert_equals(window[this.name].length, expected_length, "wrong value for " + this.name + ".length");
1069 }.bind(this), this.name + " interface object length");
1072 // TODO: Test named constructors if I find any interfaces that have them.
1076 assert_own_property(window, this.name,
1077 "window does not have own property " + format_value(this.name));
1079 if (this.has_extended_attribute("Callback")) {
1080 assert_false("prototype" in window[this.name],
1081 this.name + ' should not have a "prototype" property');
1085 // "The interface object must also have a property named “prototype”
1086 // with attributes { [[Writable]]: false, [[Enumerable]]: false,
1087 // [[Configurable]]: false } whose value is an object called the
1088 // interface prototype object. This object has properties that
1089 // correspond to the attributes and operations defined on the
1090 // interface, and is described in more detail in section 4.5.3 below."
1091 assert_own_property(window[this.name], "prototype",
1092 'interface "' + this.name + '" does not have own property "prototype"');
1093 var desc = Object.getOwnPropertyDescriptor(window[this.name], "prototype");
1094 assert_false("get" in desc, this.name + ".prototype has getter");
1095 assert_false("set" in desc, this.name + ".prototype has setter");
1096 assert_false(desc.writable, this.name + ".prototype is writable");
1097 assert_false(desc.enumerable, this.name + ".prototype is enumerable");
1098 assert_false(desc.configurable, this.name + ".prototype is configurable");
1100 // Next, test that the [[Prototype]] of the interface prototype object
1101 // is correct. (This is made somewhat difficult by the existence of
1102 // [NoInterfaceObject].)
1103 // TODO: Aryeh thinks there's at least other place in this file where
1104 // we try to figure out if an interface prototype object is
1105 // correct. Consolidate that code.
1107 // "The interface prototype object for a given interface A must have an
1108 // internal [[Prototype]] property whose value is as follows:
1109 // "If A is not declared to inherit from another interface, then the
1110 // value of the internal [[Prototype]] property of A is the Array
1111 // prototype object ([ECMA-262], section 15.4.4) if the interface was
1112 // declared with ArrayClass, or the Object prototype object otherwise
1113 // ([ECMA-262], section 15.2.4).
1114 // "Otherwise, A does inherit from another interface. The value of the
1115 // internal [[Prototype]] property of A is the interface prototype
1116 // object for the inherited interface."
1117 var inherit_interface, inherit_interface_has_interface_object;
1119 inherit_interface = this.base;
1120 inherit_interface_has_interface_object =
1122 .members[inherit_interface]
1123 .has_extended_attribute("NoInterfaceObject");
1124 } else if (this.has_extended_attribute('ArrayClass')) {
1125 inherit_interface = 'Array';
1126 inherit_interface_has_interface_object = true;
1128 inherit_interface = 'Object';
1129 inherit_interface_has_interface_object = true;
1131 if (inherit_interface_has_interface_object) {
1132 assert_own_property(window, inherit_interface,
1133 'should inherit from ' + inherit_interface + ', but window has no such property');
1134 assert_own_property(window[inherit_interface], 'prototype',
1135 'should inherit from ' + inherit_interface + ', but that object has no "prototype" property');
1136 assert_equals(Object.getPrototypeOf(window[this.name].prototype),
1137 window[inherit_interface].prototype,
1138 'prototype of ' + this.name + '.prototype is not ' + inherit_interface + '.prototype');
1140 // We can't test that we get the correct object, because this is the
1141 // only way to get our hands on it. We only test that its class
1142 // string, at least, is correct.
1143 assert_class_string(Object.getPrototypeOf(window[this.name].prototype),
1144 inherit_interface + 'Prototype',
1145 'Class name for prototype of ' + this.name +
1146 '.prototype is not "' + inherit_interface + 'Prototype"');
1149 // "The class string of an interface prototype object is the
1150 // concatenation of the interface’s identifier and the string
1152 assert_class_string(window[this.name].prototype, this.name + "Prototype",
1153 "class string of " + this.name + ".prototype");
1154 // String() should end up calling {}.toString if nothing defines a
1156 if (!this.has_stringifier()) {
1157 assert_equals(String(window[this.name].prototype), "[object " + this.name + "Prototype]",
1158 "String(" + this.name + ".prototype)");
1160 }.bind(this), this.name + " interface: existence and properties of interface prototype object");
1164 assert_own_property(window, this.name,
1165 "window does not have own property " + format_value(this.name));
1167 if (this.has_extended_attribute("Callback")) {
1168 assert_false("prototype" in window[this.name],
1169 this.name + ' should not have a "prototype" property');
1173 assert_own_property(window[this.name], "prototype",
1174 'interface "' + this.name + '" does not have own property "prototype"');
1176 // "If the [NoInterfaceObject] extended attribute was not specified on
1177 // the interface, then the interface prototype object must also have a
1178 // property named “constructor” with attributes { [[Writable]]: true,
1179 // [[Enumerable]]: false, [[Configurable]]: true } whose value is a
1180 // reference to the interface object for the interface."
1181 assert_own_property(window[this.name].prototype, "constructor",
1182 this.name + '.prototype does not have own property "constructor"');
1183 var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, "constructor");
1184 assert_false("get" in desc, this.name + ".prototype.constructor has getter");
1185 assert_false("set" in desc, this.name + ".prototype.constructor has setter");
1186 assert_true(desc.writable, this.name + ".prototype.constructor is not writable");
1187 assert_false(desc.enumerable, this.name + ".prototype.constructor is enumerable");
1188 assert_true(desc.configurable, this.name + ".prototype.constructor in not configurable");
1189 assert_equals(window[this.name].prototype.constructor, window[this.name],
1190 this.name + '.prototype.constructor is not the same object as ' + this.name);
1191 }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property');
1195 IdlInterface.prototype.test_member_const = function(member)
1200 assert_own_property(window, this.name,
1201 "window does not have own property " + format_value(this.name));
1203 // "For each constant defined on an interface A, there must be
1204 // a corresponding property on the interface object, if it
1206 assert_own_property(window[this.name], member.name);
1207 // "The value of the property is that which is obtained by
1208 // converting the constant’s IDL value to an ECMAScript
1210 assert_equals(window[this.name][member.name], constValue(member.value),
1211 "property has wrong value");
1212 // "The property has attributes { [[Writable]]: false,
1213 // [[Enumerable]]: true, [[Configurable]]: false }."
1214 var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name);
1215 assert_false("get" in desc, "property has getter");
1216 assert_false("set" in desc, "property has setter");
1217 assert_false(desc.writable, "property is writable");
1218 assert_true(desc.enumerable, "property is not enumerable");
1219 assert_false(desc.configurable, "property is configurable");
1220 }.bind(this), this.name + " interface: constant " + member.name + " on interface object");
1221 // "In addition, a property with the same characteristics must
1222 // exist on the interface prototype object."
1225 assert_own_property(window, this.name,
1226 "window does not have own property " + format_value(this.name));
1228 if (this.has_extended_attribute("Callback")) {
1229 assert_false("prototype" in window[this.name],
1230 this.name + ' should not have a "prototype" property');
1234 assert_own_property(window[this.name], "prototype",
1235 'interface "' + this.name + '" does not have own property "prototype"');
1237 assert_own_property(window[this.name].prototype, member.name);
1238 assert_equals(window[this.name].prototype[member.name], constValue(member.value),
1239 "property has wrong value");
1240 var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name);
1241 assert_false("get" in desc, "property has getter");
1242 assert_false("set" in desc, "property has setter");
1243 assert_false(desc.writable, "property is writable");
1244 assert_true(desc.enumerable, "property is not enumerable");
1245 assert_false(desc.configurable, "property is configurable");
1246 }.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object");
1251 IdlInterface.prototype.test_member_attribute = function(member)
1256 assert_own_property(window, this.name,
1257 "window does not have own property " + format_value(this.name));
1258 assert_own_property(window[this.name], "prototype",
1259 'interface "' + this.name + '" does not have own property "prototype"');
1261 if (member["static"]) {
1262 assert_own_property(window[this.name], member.name,
1263 "The interface object must have a property " +
1264 format_value(member.name));
1268 assert_true(member.name in window[this.name].prototype,
1269 "The prototype object must have a property " +
1270 format_value(member.name));
1272 if (!member.has_extended_attribute("LenientThis")) {
1273 assert_throws(new TypeError(), function() {
1274 window[this.name].prototype[member.name];
1275 }.bind(this), "getting property on prototype object must throw TypeError");
1277 assert_equals(window[this.name].prototype[member.name], undefined,
1278 "getting property on prototype object must return undefined");
1280 do_interface_attribute_asserts(window[this.name].prototype, member);
1282 }.bind(this), this.name + " interface: attribute " + member.name);
1286 IdlInterface.prototype.test_member_operation = function(member)
1291 assert_own_property(window, this.name,
1292 "window does not have own property " + format_value(this.name));
1294 if (this.has_extended_attribute("Callback")) {
1295 assert_false("prototype" in window[this.name],
1296 this.name + ' should not have a "prototype" property');
1300 assert_own_property(window[this.name], "prototype",
1301 'interface "' + this.name + '" does not have own property "prototype"');
1303 // "For each unique identifier of an operation defined on the
1304 // interface, there must be a corresponding property on the
1305 // interface prototype object (if it is a regular operation) or
1306 // the interface object (if it is a static operation), unless
1307 // the effective overload set for that identifier and operation
1308 // and with an argument count of 0 (for the ECMAScript language
1309 // binding) has no entries."
1311 var prototypeOrInterfaceObject;
1312 if (member["static"]) {
1313 assert_own_property(window[this.name], member.name,
1314 "interface prototype object missing static operation");
1315 prototypeOrInterfaceObject = window[this.name];
1319 assert_own_property(window[this.name].prototype, member.name,
1320 "interface prototype object missing non-static operation");
1321 prototypeOrInterfaceObject = window[this.name].prototype;
1324 var desc = Object.getOwnPropertyDescriptor(prototypeOrInterfaceObject, member.name);
1325 // "The property has attributes { [[Writable]]: true,
1326 // [[Enumerable]]: true, [[Configurable]]: true }."
1327 assert_false("get" in desc, "property has getter");
1328 assert_false("set" in desc, "property has setter");
1329 assert_true(desc.writable, "property is not writable");
1330 assert_true(desc.enumerable, "property is not enumerable");
1331 assert_true(desc.configurable, "property is not configurable");
1332 // "The value of the property is a Function object whose
1333 // behavior is as follows . . ."
1334 assert_equals(typeof prototypeOrInterfaceObject[member.name], "function",
1335 "property must be a function");
1336 // "The value of the Function object’s “length” property is
1337 // a Number determined as follows:
1339 // "Return the length of the shortest argument list of the
1342 // TODO: Doesn't handle overloading or variadic arguments.
1343 assert_equals(prototypeOrInterfaceObject[member.name].length,
1344 member.arguments.filter(function(arg) {
1345 return !arg.optional;
1347 "property has wrong .length");
1349 // Make some suitable arguments
1350 var args = member.arguments.map(function(arg) {
1351 return create_suitable_object(arg.idlType);
1354 // "Let O be a value determined as follows:
1356 // "Otherwise, throw a TypeError."
1357 // This should be hit if the operation is not static, there is
1358 // no [ImplicitThis] attribute, and the this value is null.
1360 // TODO: We currently ignore the [ImplicitThis] case.
1361 if (!member["static"]) {
1362 assert_throws(new TypeError(), function() {
1363 window[this.name].prototype[member.name].apply(null, args);
1364 }, "calling operation with this = null didn't throw TypeError");
1367 // ". . . If O is not null and is also not a platform object
1368 // that implements interface I, throw a TypeError."
1370 // TODO: Test a platform object that implements some other
1371 // interface. (Have to be sure to get inheritance right.)
1372 assert_throws(new TypeError(), function() {
1373 window[this.name].prototype[member.name].apply({}, args);
1374 }, "calling operation with this = {} didn't throw TypeError");
1375 }.bind(this), this.name + " interface: operation " + member.name +
1376 "(" + member.arguments.map(function(m) { return m.idlType.idlType; }) +
1381 IdlInterface.prototype.test_member_stringifier = function(member)
1386 assert_own_property(window, this.name,
1387 "window does not have own property " + format_value(this.name));
1389 if (this.has_extended_attribute("Callback")) {
1390 assert_false("prototype" in window[this.name],
1391 this.name + ' should not have a "prototype" property');
1395 assert_own_property(window[this.name], "prototype",
1396 'interface "' + this.name + '" does not have own property "prototype"');
1398 // ". . . the property exists on the interface prototype object."
1399 var interfacePrototypeObject = window[this.name].prototype;
1400 assert_own_property(window[this.name].prototype, "toString",
1401 "interface prototype object missing non-static operation");
1403 var desc = Object.getOwnPropertyDescriptor(interfacePrototypeObject, "toString");
1404 // "The property has attributes { [[Writable]]: B,
1405 // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the
1406 // stringifier is unforgeable on the interface, and true otherwise."
1407 assert_false("get" in desc, "property has getter");
1408 assert_false("set" in desc, "property has setter");
1409 assert_true(desc.writable, "property is not writable");
1410 assert_true(desc.enumerable, "property is not enumerable");
1411 assert_true(desc.configurable, "property is not configurable");
1412 // "The value of the property is a Function object, which behaves as
1414 assert_equals(typeof interfacePrototypeObject.toString, "function",
1415 "property must be a function");
1416 // "The value of the Function object’s “length” property is the Number
1418 assert_equals(interfacePrototypeObject.toString.length, 0,
1419 "property has wrong .length");
1421 // "Let O be the result of calling ToObject on the this value."
1422 assert_throws(new TypeError(), function() {
1423 window[this.name].prototype.toString.apply(null, []);
1424 }, "calling stringifier with this = null didn't throw TypeError");
1426 // "If O is not an object that implements the interface on which the
1427 // stringifier was declared, then throw a TypeError."
1429 // TODO: Test a platform object that implements some other
1430 // interface. (Have to be sure to get inheritance right.)
1431 assert_throws(new TypeError(), function() {
1432 window[this.name].prototype.toString.apply({}, []);
1433 }, "calling stringifier with this = {} didn't throw TypeError");
1434 }.bind(this), this.name + " interface: stringifier");
1438 IdlInterface.prototype.test_members = function()
1441 for (var i = 0; i < this.members.length; i++)
1443 var member = this.members[i];
1444 if (member.untested) {
1448 switch (member.type) {
1450 this.test_member_const(member);
1454 // For unforgeable attributes, we do the checks in
1455 // test_interface_of instead.
1456 if (!member.has_extended_attribute("Unforgeable")) {
1457 this.test_member_attribute(member);
1462 // TODO: Need to correctly handle multiple operations with the same
1465 this.test_member_operation(member);
1466 } else if (member.stringifier) {
1467 this.test_member_stringifier(member);
1472 // TODO: check more member types.
1479 IdlInterface.prototype.test_object = function(desc)
1482 var obj, exception = null;
1492 // TODO: WebIDLParser doesn't currently support named legacycallers, so I'm
1493 // not sure what those would look like in the AST
1494 var expected_typeof = this.members.some(function(member)
1496 return member.legacycaller
1497 || ("idlType" in member && member.idlType.legacycaller)
1498 || ("idlType" in member && typeof member.idlType == "object"
1499 && "idlType" in member.idlType && member.idlType.idlType == "legacycaller");
1500 }) ? "function" : "object";
1502 this.test_primary_interface_of(desc, obj, exception, expected_typeof);
1503 var current_interface = this;
1504 while (current_interface)
1506 if (!(current_interface.name in this.array.members))
1508 throw "Interface " + current_interface.name + " not found (inherited by " + this.name + ")";
1510 if (current_interface.prevent_multiple_testing && current_interface.already_tested)
1514 current_interface.test_interface_of(desc, obj, exception, expected_typeof);
1515 current_interface = this.array.members[current_interface.base];
1520 IdlInterface.prototype.test_primary_interface_of = function(desc, obj, exception, expected_typeof)
1523 // We can't easily test that its prototype is correct if there's no
1524 // interface object, or the object is from a different global environment
1525 // (not instanceof Object). TODO: test in this case that its prototype at
1526 // least looks correct, even if we can't test that it's actually correct.
1527 if (!this.has_extended_attribute("NoInterfaceObject")
1528 && (typeof obj != expected_typeof || obj instanceof Object))
1532 assert_equals(exception, null, "Unexpected exception when evaluating object");
1533 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1534 assert_own_property(window, this.name,
1535 "window does not have own property " + format_value(this.name));
1536 assert_own_property(window[this.name], "prototype",
1537 'interface "' + this.name + '" does not have own property "prototype"');
1539 // "The value of the internal [[Prototype]] property of the
1540 // platform object is the interface prototype object of the primary
1541 // interface from the platform object’s associated global
1543 assert_equals(Object.getPrototypeOf(obj),
1544 window[this.name].prototype,
1545 desc + "'s prototype is not " + this.name + ".prototype");
1546 }.bind(this), this.name + " must be primary interface of " + desc);
1549 // "The class string of a platform object that implements one or more
1550 // interfaces must be the identifier of the primary interface of the
1551 // platform object."
1554 assert_equals(exception, null, "Unexpected exception when evaluating object");
1555 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1556 assert_class_string(obj, this.name, "class string of " + desc);
1557 if (!this.has_stringifier())
1559 assert_equals(String(obj), "[object " + this.name + "]", "String(" + desc + ")");
1561 }.bind(this), "Stringification of " + desc);
1565 IdlInterface.prototype.test_interface_of = function(desc, obj, exception, expected_typeof)
1568 // TODO: Indexed and named properties, more checks on interface members
1569 this.already_tested = true;
1571 for (var i = 0; i < this.members.length; i++)
1573 var member = this.members[i];
1574 if (member.has_extended_attribute("Unforgeable"))
1578 assert_equals(exception, null, "Unexpected exception when evaluating object");
1579 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1580 do_interface_attribute_asserts(obj, member);
1581 }.bind(this), this.name + " interface: " + desc + ' must have own property "' + member.name + '"');
1583 else if ((member.type == "const"
1584 || member.type == "attribute"
1585 || member.type == "operation")
1590 assert_equals(exception, null, "Unexpected exception when evaluating object");
1591 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1592 if (!member["static"]) {
1593 assert_inherits(obj, member.name);
1594 if (member.type == "const")
1596 assert_equals(obj[member.name], constValue(member.value));
1598 if (member.type == "attribute")
1600 // Attributes are accessor properties, so they might
1601 // legitimately throw an exception rather than returning
1603 var property, thrown = false;
1606 property = obj[member.name];
1614 this.array.assert_type_is(property, member.idlType);
1617 if (member.type == "operation")
1619 assert_equals(typeof obj[member.name], "function");
1622 }.bind(this), this.name + " interface: " + desc + ' must inherit property "' + member.name + '" with the proper type (' + i + ')');
1624 // TODO: This is wrong if there are multiple operations with the same
1626 // TODO: Test passing arguments of the wrong type.
1627 if (member.type == "operation" && member.name && member.arguments.length)
1631 assert_equals(exception, null, "Unexpected exception when evaluating object");
1632 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1633 if (!member["static"]) {
1634 assert_inherits(obj, member.name);
1638 assert_false(member.name in obj);
1641 for (var i = 0; i < member.arguments.length; i++)
1643 if (member.arguments[i].optional)
1647 assert_throws(new TypeError(), function()
1649 obj[member.name].apply(obj, args);
1650 }.bind(this), "Called with " + i + " arguments");
1652 args.push(create_suitable_object(member.arguments[i].idlType));
1654 }.bind(this), this.name + " interface: calling " + member.name +
1655 "(" + member.arguments.map(function(m) { return m.idlType.idlType; }) +
1656 ") on " + desc + " with too few arguments must throw TypeError");
1662 IdlInterface.prototype.has_stringifier = function()
1665 if (this.members.some(function(member) { return member.stringifier; })) {
1669 this.array.members[this.base].has_stringifier()) {
1676 function do_interface_attribute_asserts(obj, member)
1679 // "For each attribute defined on the interface, there must exist a
1680 // corresponding property. If the attribute was declared with the
1681 // [Unforgeable] extended attribute, then the property exists on every
1682 // object that implements the interface. Otherwise, it exists on the
1683 // interface’s interface prototype object."
1685 // This is called by test_self() with the prototype as obj, and by
1686 // test_interface_of() with the object as obj.
1687 assert_own_property(obj, member.name);
1689 // "The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
1690 // true, [[Configurable]]: configurable }, where:
1691 // "configurable is false if the attribute was declared with the
1692 // [Unforgeable] extended attribute and true otherwise;
1693 // "G is the attribute getter, defined below; and
1694 // "S is the attribute setter, also defined below."
1695 var desc = Object.getOwnPropertyDescriptor(obj, member.name);
1696 assert_false("value" in desc, 'property descriptor has value but is supposed to be accessor');
1697 assert_false("writable" in desc, 'property descriptor has "writable" field but is supposed to be accessor');
1698 assert_true(desc.enumerable, "property is not enumerable");
1699 if (member.has_extended_attribute("Unforgeable"))
1701 assert_false(desc.configurable, "[Unforgeable] property must not be configurable");
1705 assert_true(desc.configurable, "property must be configurable");
1708 // "The attribute getter is a Function object whose behavior when invoked
1711 // "The value of the Function object’s “length” property is the Number
1713 assert_equals(typeof desc.get, "function", "getter must be Function");
1714 assert_equals(desc.get.length, 0, "getter length must be 0");
1715 if (!member.has_extended_attribute("LenientThis")) {
1716 assert_throws(new TypeError(), function() {
1718 }.bind(this), "calling getter on wrong object type must throw TypeError");
1720 assert_equals(desc.get.call({}), undefined,
1721 "calling getter on wrong object type must return undefined");
1724 // TODO: Test calling setter on the interface prototype (should throw
1725 // TypeError in most cases).
1727 // "The attribute setter is undefined if the attribute is declared readonly
1728 // and has neither a [PutForwards] nor a [Replaceable] extended attribute
1729 // declared on it. Otherwise, it is a Function object whose behavior when
1730 // invoked is as follows:
1732 // "The value of the Function object’s “length” property is the Number
1735 && !member.has_extended_attribute("PutForwards")
1736 && !member.has_extended_attribute("Replaceable"))
1738 assert_equals(desc.set, undefined, "setter must be undefined for readonly attributes");
1742 assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes");
1743 assert_equals(desc.set.length, 1, "setter length must be 1");
1744 if (!member.has_extended_attribute("LenientThis")) {
1745 assert_throws(new TypeError(), function() {
1747 }.bind(this), "calling setter on wrong object type must throw TypeError");
1749 assert_equals(desc.set.call({}), undefined,
1750 "calling setter on wrong object type must return undefined");
1756 /// IdlInterfaceMember ///
1757 function IdlInterfaceMember(obj)
1761 * obj is an object produced by the WebIDLParser.js "ifMember" production.
1762 * We just forward all properties to this object without modification,
1763 * except for special extAttrs handling.
1769 if (!("extAttrs" in this))
1776 IdlInterfaceMember.prototype = Object.create(IdlObject.prototype);
1778 /// Internal helper functions ///
1779 function create_suitable_object(type)
1783 * type is an object produced by the WebIDLParser.js "type" production. We
1784 * return a JavaScript value that matches the type, if we can figure out
1791 switch (type.idlType)
1797 case "byte": case "octet": case "short": case "unsigned short":
1798 case "long": case "unsigned long": case "long long":
1799 case "unsigned long long": case "float": case "double":
1800 case "unrestricted float": case "unrestricted double":
1812 return document.createTextNode("abc");
1819 // Used for IdlArray.prototype.assert_type_is
1820 function IdlEnum(obj)
1824 * obj is an object produced by the WebIDLParser.js "dictionary"
1828 /** Self-explanatory. */
1829 this.name = obj.name;
1831 /** An array of values produced by the "enum" production. */
1832 this.values = obj.values;
1837 IdlEnum.prototype = Object.create(IdlObject.prototype);
1840 // Used for IdlArray.prototype.assert_type_is
1841 function IdlTypedef(obj)
1845 * obj is an object produced by the WebIDLParser.js "typedef"
1849 /** Self-explanatory. */
1850 this.name = obj.name;
1852 /** An array of values produced by the "typedef" production. */
1853 this.values = obj.values;
1858 IdlTypedef.prototype = Object.create(IdlObject.prototype);
1861 // vim: set expandtab shiftwidth=4 tabstop=4 foldmarker=@{,@} foldmethod=marker: