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
12 * This file automatically generates browser tests for WebIDL interfaces, using
13 * the testharness.js framework. To use, first include the following:
15 * <script src=/resources/testharness.js></script>
16 * <script src=/resources/testharnessreport.js></script>
17 * <script src=/resources/WebIDLParser.js></script>
18 * <script src=/resources/idlharness.js></script>
20 * Then you'll need some type of IDLs. Here's some script that can be run on a
21 * spec written in HTML, which will grab all the elements with class="idl",
22 * concatenate them, and replace the body so you can copy-paste:
25 [].forEach.call(document.getElementsByClassName("idl"), function(idl) {
26 //https://www.w3.org/Bugs/Public/show_bug.cgi?id=14914
27 if (!idl.classList.contains("extract"))
29 s += idl.textContent + "\n\n";
32 document.body.innerHTML = '<pre></pre>';
33 document.body.firstChild.textContent = s;
35 * (TODO: write this in Python or something so that it can be done from the
36 * command line instead.)
38 * Once you have that, put it in your script somehow. The easiest way is to
39 * embed it literally in an HTML file with <script type=text/plain> or similar,
40 * so that you don't have to do any escaping. Another possibility is to put it
41 * in a separate .idl file that's fetched via XHR or similar. Sample usage:
43 * var idl_array = new IdlArray();
44 * idl_array.add_untested_idls("interface Node { readonly attribute DOMString nodeName; };");
45 * idl_array.add_idls("interface Document : Node { readonly attribute DOMString URL; };");
46 * idl_array.add_objects({Document: ["document"]});
49 * This tests that window.Document exists and meets all the requirements of
50 * WebIDL. It also tests that window.document (the result of evaluating the
51 * string "document") has URL and nodeName properties that behave as they
52 * should, and otherwise meets WebIDL's requirements for an object whose
53 * primary interface is Document. It does not test that window.Node exists,
54 * which is what you want if the Node interface is already tested in some other
55 * specification's suite and your specification only extends or refers to it.
56 * Of course, each IDL string can define many different things, and calls to
57 * add_objects() can register many different objects for different interfaces:
58 * this is a very simple example.
60 * TODO: Write assert_writable, assert_enumerable, assert_configurable and
61 * their inverses, and use those instead of just checking
62 * getOwnPropertyDescriptor.
64 * == Public methods of IdlArray ==
66 * IdlArray objects can be obtained with new IdlArray(). Anything not
67 * documented in this section should be considered an implementation detail,
68 * and outside callers should not use it.
70 * add_idls(idl_string):
71 * Parses idl_string (throwing on parse error) and adds the results to the
72 * IdlArray. All the definitions will be tested when you run test(). If
73 * some of the definitions refer to other definitions, those must be present
74 * too. For instance, if idl_string says that Document inherits from Node,
75 * the Node interface must also have been provided in some call to add_idls()
76 * or add_untested_idls().
78 * add_untested_idls(idl_string):
79 * Like add_idls(), but the definitions will not be tested. If an untested
80 * interface is added and then extended with a tested partial interface, the
81 * members of the partial interface will still be tested. Also, all the
82 * members will still be tested for objects added with add_objects(), because
83 * you probably want to test that (for instance) window.document has all the
84 * properties from Node, not just Document, even if the Node interface itself
85 * is tested in a different test suite.
88 * dict should be an object whose keys are the names of interfaces or
89 * exceptions, and whose values are arrays of strings. When an interface or
90 * exception is tested, every string registered for it with add_objects()
91 * will be evaluated, and tests will be run on the result to verify that it
92 * correctly implements that interface or exception. This is the only way to
93 * test anything about [NoInterfaceObject] interfaces, and there are many
94 * tests that can't be run on any interface without an object to fiddle with.
96 * The interface has to be the *primary* interface of all the objects
97 * provided. For example, don't pass {Node: ["document"]}, but rather
98 * {Document: ["document"]}. Assuming the Document interface was declared to
99 * inherit from Node, this will automatically test that document implements
100 * the Node interface too.
102 * Warning: methods will be called on any provided objects, in a manner that
103 * WebIDL requires be safe. For instance, if a method has mandatory
104 * arguments, the test suite will try calling it with too few arguments to
105 * see if it throws an exception. If an implementation incorrectly runs the
106 * function instead of throwing, this might have side effects, possibly even
107 * preventing the test suite from running correctly.
109 * prevent_multiple_testing(name):
110 * This is a niche method for use in case you're testing many objects that
111 * implement the same interfaces, and don't want to retest the same
112 * interfaces every single time. For instance, HTML defines many interfaces
113 * that all inherit from HTMLElement, so the HTML test suite has something
116 * HTMLHtmlElement: ['document.documentElement'],
117 * HTMLHeadElement: ['document.head'],
118 * HTMLBodyElement: ['document.body'],
121 * and so on for dozens of element types. This would mean that it would
122 * retest that each and every one of those elements implements HTMLElement,
123 * Element, and Node, which would be thousands of basically redundant tests.
124 * The test suite therefore calls prevent_multiple_testing("HTMLElement").
125 * This means that once one object has been tested to implement HTMLElement
126 * and its ancestors, no other object will be. Thus in the example code
127 * above, the harness would test that document.documentElement correctly
128 * implements HTMLHtmlElement, HTMLElement, Element, and Node; but
129 * document.head would only be tested for HTMLHeadElement, and so on for
133 * Run all tests. This should be called after you've called all other
134 * methods to add IDLs and objects.
138 * Notes for people who want to edit this file (not just use it as a library):
140 * Most of the interesting stuff happens in the derived classes of IdlObject,
141 * especially IdlInterface. The entry point for all IdlObjects is .test(),
142 * which is called by IdlArray.test(). An IdlObject is conceptually just
143 * "thing we want to run tests on", and an IdlArray is an array of IdlObjects
144 * with some additional data thrown in.
146 * The object model is based on what WebIDLParser.js produces, which is in turn
147 * based on its pegjs grammar. If you want to figure out what properties an
148 * object will have from WebIDLParser.js, the best way is to look at the
151 * https://github.com/darobin/webidl.js/blob/master/lib/grammar.peg
155 * // interface definition
157 * = extAttrs:extendedAttributeList? S? "interface" S name:identifier w herit:ifInheritance? w "{" w mem:ifMember* w "}" w ";" w
158 * { return { type: "interface", name: name, inheritance: herit, members: mem, extAttrs: extAttrs }; }
160 * This means that an "interface" object will have a .type property equal to
161 * the string "interface", a .name property equal to the identifier that the
162 * parser found, an .inheritance property equal to either null or the result of
163 * the "ifInheritance" production found elsewhere in the grammar, and so on.
164 * After each grammatical production is a JavaScript function in curly braces
165 * that gets called with suitable arguments and returns some JavaScript value.
167 * (Note that the version of WebIDLParser.js we use might sometimes be
168 * out-of-date or forked.)
170 * The members and methods of the classes defined by this file are all at least
171 * briefly documented, hopefully.
176 function constValue (cnt) {
177 if (cnt.type === "null") return null;
178 if (cnt.type === "NaN") return NaN;
179 if (cnt.type === "Infinity") return cnt.negative ? -Infinity : Infinity;
185 window.IdlArray = function()
189 * A map from strings to the corresponding named IdlObject, such as
190 * IdlInterface or IdlException. These are the things that test() will run
196 * A map from strings to arrays of strings. The keys are interface or
197 * exception names, and are expected to also exist as keys in this.members
198 * (otherwise they'll be ignored). This is populated by add_objects() --
199 * see documentation at the start of the file. The actual tests will be
200 * run by calling this.members[name].test_object(obj) for each obj in
201 * this.objects[name]. obj is a string that will be eval'd to produce a
202 * JavaScript value, which is supposed to be an object implementing the
203 * given IdlObject (interface, exception, etc.).
208 * When adding multiple collections of IDLs one at a time, an earlier one
209 * might contain a partial interface or implements statement that depends
210 * on a later one. Save these up and handle them right before we run
213 * .partials is simply an array of objects from WebIDLParser.js'
214 * "partialinterface" production. .implements maps strings to arrays of
221 * results in { A: ["B", "C"], D: ["E"] }.
224 this["implements"] = {};
228 IdlArray.prototype.add_idls = function(raw_idls)
231 /** Entry point. See documentation at beginning of file. */
232 this.internal_add_idls(WebIDL2.parse(raw_idls));
236 IdlArray.prototype.add_untested_idls = function(raw_idls)
239 /** Entry point. See documentation at beginning of file. */
240 var parsed_idls = WebIDL2.parse(raw_idls);
241 for (var i = 0; i < parsed_idls.length; i++)
243 parsed_idls[i].untested = true;
244 if ("members" in parsed_idls[i])
246 for (var j = 0; j < parsed_idls[i].members.length; j++)
248 parsed_idls[i].members[j].untested = true;
252 this.internal_add_idls(parsed_idls);
256 IdlArray.prototype.internal_add_idls = function(parsed_idls)
260 * Internal helper called by add_idls() and add_untested_idls().
261 * parsed_idls is an array of objects that come from WebIDLParser.js's
262 * "definitions" production. The add_untested_idls() entry point
263 * additionally sets an .untested property on each object (and its
264 * .members) so that they'll be skipped by test() -- they'll only be
265 * used for base interfaces of tested interfaces, return types, etc.
267 parsed_idls.forEach(function(parsed_idl)
269 if (parsed_idl.type == "interface" && parsed_idl.partial)
271 this.partials.push(parsed_idl);
275 if (parsed_idl.type == "implements")
277 if (!(parsed_idl.target in this["implements"]))
279 this["implements"][parsed_idl.target] = [];
281 this["implements"][parsed_idl.target].push(parsed_idl["implements"]);
285 parsed_idl.array = this;
286 if (parsed_idl.name in this.members)
288 throw "Duplicate identifier " + parsed_idl.name;
290 switch(parsed_idl.type)
293 this.members[parsed_idl.name] = new IdlInterface(parsed_idl);
297 this.members[parsed_idl.name] = new IdlException(parsed_idl);
301 // Nothing to test, but we need the dictionary info around for type
303 this.members[parsed_idl.name] = new IdlDictionary(parsed_idl);
307 this.members[parsed_idl.name] = new IdlTypedef(parsed_idl);
312 console.log("callback not yet supported");
316 this.members[parsed_idl.name] = new IdlEnum(parsed_idl);
319 case "callback interface":
321 console.log("callback interface not yet supported");
325 throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported";
331 IdlArray.prototype.add_objects = function(dict)
334 /** Entry point. See documentation at beginning of file. */
337 if (k in this.objects)
339 this.objects[k] = this.objects[k].concat(dict[k]);
343 this.objects[k] = dict[k];
349 IdlArray.prototype.prevent_multiple_testing = function(name)
352 /** Entry point. See documentation at beginning of file. */
353 this.members[name].prevent_multiple_testing = true;
357 IdlArray.prototype.recursively_get_implements = function(interface_name)
361 * Helper function for test(). Returns an array of things that implement
362 * interface_name, so if the IDL contains
368 * then recursively_get_implements("A") should return ["B", "C", "D"].
370 var ret = this["implements"][interface_name];
371 if (ret === undefined)
375 for (var i = 0; i < this["implements"][interface_name].length; i++)
377 ret = ret.concat(this.recursively_get_implements(ret[i]));
378 if (ret.indexOf(ret[i]) != ret.lastIndexOf(ret[i]))
380 throw "Circular implements statements involving " + ret[i];
387 IdlArray.prototype.test = function()
390 /** Entry point. See documentation at beginning of file. */
392 // First merge in all the partial interfaces and implements statements we
394 this.partials.forEach(function(parsed_idl)
396 if (!(parsed_idl.name in this.members)
397 || !(this.members[parsed_idl.name] instanceof IdlInterface))
399 throw "Partial interface " + parsed_idl.name + " with no original interface";
401 if (parsed_idl.extAttrs)
403 parsed_idl.extAttrs.forEach(function(extAttr)
405 this.members[parsed_idl.name].extAttrs.push(extAttr);
408 parsed_idl.members.forEach(function(member)
410 this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member));
415 for (var lhs in this["implements"])
417 this.recursively_get_implements(lhs).forEach(function(rhs)
419 var errStr = lhs + " implements " + rhs + ", but ";
420 if (!(lhs in this.members)) throw errStr + lhs + " is undefined.";
421 if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface.";
422 if (!(rhs in this.members)) throw errStr + rhs + " is undefined.";
423 if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface.";
424 this.members[rhs].members.forEach(function(member)
426 this.members[lhs].members.push(new IdlInterfaceMember(member));
430 this["implements"] = {};
432 // Now run test() on every member, and test_object() for every object.
433 for (var name in this.members)
435 this.members[name].test();
436 if (name in this.objects)
438 this.objects[name].forEach(function(str)
440 this.members[name].test_object(str);
447 IdlArray.prototype.assert_type_is = function(value, type)
451 * Helper function that tests that value is an instance of type according
452 * to the rules of WebIDL. value is any JavaScript value, and type is an
453 * object produced by WebIDLParser.js' "type" production. That production
454 * is fairly elaborate due to the complexity of WebIDL's types, so it's
455 * best to look at the grammar to figure out what properties it might have.
457 if (type.idlType == "any")
459 // No assertions to make
463 if (type.nullable && value === null)
471 // TODO: not supported yet
477 assert_true(Array.isArray(value), "is not array");
480 // Nothing we can do.
483 this.assert_type_is(value[0], type.idlType.idlType);
492 assert_equals(value, undefined);
496 assert_equals(typeof value, "boolean");
500 assert_equals(typeof value, "number");
501 assert_equals(value, Math.floor(value), "not an integer");
502 assert_true(-128 <= value && value <= 127, "byte " + value + " not in range [-128, 127]");
506 assert_equals(typeof value, "number");
507 assert_equals(value, Math.floor(value), "not an integer");
508 assert_true(0 <= value && value <= 255, "octet " + value + " not in range [0, 255]");
512 assert_equals(typeof value, "number");
513 assert_equals(value, Math.floor(value), "not an integer");
514 assert_true(-32768 <= value && value <= 32767, "short " + value + " not in range [-32768, 32767]");
517 case "unsigned short":
518 assert_equals(typeof value, "number");
519 assert_equals(value, Math.floor(value), "not an integer");
520 assert_true(0 <= value && value <= 65535, "unsigned short " + value + " not in range [0, 65535]");
524 assert_equals(typeof value, "number");
525 assert_equals(value, Math.floor(value), "not an integer");
526 assert_true(-2147483648 <= value && value <= 2147483647, "long " + value + " not in range [-2147483648, 2147483647]");
529 case "unsigned long":
530 assert_equals(typeof value, "number");
531 assert_equals(value, Math.floor(value), "not an integer");
532 assert_true(0 <= value && value <= 4294967295, "unsigned long " + value + " not in range [0, 4294967295]");
536 assert_equals(typeof value, "number");
539 case "unsigned long long":
540 assert_equals(typeof value, "number");
541 assert_true(0 <= value, "unsigned long long is negative");
546 case "unrestricted float":
547 case "unrestricted double":
548 // TODO: distinguish these cases
549 assert_equals(typeof value, "number");
553 assert_equals(typeof value, "string");
557 assert_true(typeof value == "object" || typeof value == "function", "wrong type: not object or function");
561 if (!(type in this.members))
563 throw "Unrecognized type " + type;
566 if (this.members[type] instanceof IdlInterface)
568 // We don't want to run the full
569 // IdlInterface.prototype.test_instance_of, because that could result
570 // in an infinite loop. TODO: This means we don't have tests for
571 // NoInterfaceObject interfaces, and we also can't test objects that
572 // come from another window.
573 assert_true(typeof value == "object" || typeof value == "function", "wrong type: not object or function");
574 if (value instanceof Object
575 && !this.members[type].has_extended_attribute("NoInterfaceObject")
578 assert_true(value instanceof window[type], "not instanceof " + type);
581 else if (this.members[type] instanceof IdlEnum)
583 assert_equals(typeof value, "string");
585 else if (this.members[type] instanceof IdlDictionary)
587 // TODO: Test when we actually have something to test this on
589 else if (this.members[type] instanceof IdlTypedef)
591 // TODO: Test when we actually have something to test this on
595 throw "Type " + type + " isn't an interface or dictionary";
601 function IdlObject() {}
602 IdlObject.prototype.test = function()
606 * By default, this does nothing, so no actual tests are run for IdlObjects
607 * that don't define any (e.g., IdlDictionary at the time of this writing).
612 IdlObject.prototype.has_extended_attribute = function(name)
616 * This is only meaningful for things that support extended attributes,
617 * such as interfaces, exceptions, and members.
619 return this.extAttrs.some(function(o)
621 return o.name == name;
627 /// IdlDictionary ///
628 // Used for IdlArray.prototype.assert_type_is
629 function IdlDictionary(obj)
633 * obj is an object produced by the WebIDLParser.js "dictionary"
637 /** Self-explanatory. */
638 this.name = obj.name;
640 /** An array of objects produced by the "dictionaryMember" production. */
641 this.members = obj.members;
644 * The name (as a string) of the dictionary type we inherit from, or null
647 this.base = obj.inheritance;
651 IdlDictionary.prototype = Object.create(IdlObject.prototype);
653 /// IdlExceptionOrInterface ///
655 function IdlExceptionOrInterface(obj)
659 * obj is an object produced by the WebIDLParser.js "exception" or
660 * "interface" production, as appropriate.
663 /** Self-explanatory. */
664 this.name = obj.name;
666 /** A back-reference to our IdlArray. */
667 this.array = obj.array;
670 * An indicator of whether we should run tests on the (exception) interface
671 * object and (exception) interface prototype object. Tests on members are
672 * controlled by .untested on each member, not this.
674 this.untested = obj.untested;
676 /** An array of objects produced by the "ExtAttr" production. */
677 this.extAttrs = obj.extAttrs;
679 /** An array of IdlInterfaceMembers. */
680 this.members = obj.members.map(function(m){return new IdlInterfaceMember(m); });
683 * The name (as a string) of the type we inherit from, or null if there is
686 this.base = obj.inheritance;
690 IdlExceptionOrInterface.prototype = Object.create(IdlObject.prototype);
691 IdlExceptionOrInterface.prototype.test = function()
694 if (this.has_extended_attribute("NoInterfaceObject"))
696 // No tests to do without an instance. TODO: We should still be able
697 // to run tests on the prototype object, if we obtain one through some
704 // First test things to do with the exception/interface object and
705 // exception/interface prototype object.
708 // Then test things to do with its members (constants, fields, attributes,
709 // operations, . . .). These are run even if .untested is true, because
710 // members might themselves be marked as .untested. This might happen to
711 // interfaces if the interface itself is untested but a partial interface
712 // that extends it is tested -- then the interface itself and its initial
713 // members will be marked as untested, but the members added by the partial
714 // interface are still tested.
721 function IdlException(obj) { IdlExceptionOrInterface.call(this, obj); }
722 IdlException.prototype = Object.create(IdlExceptionOrInterface.prototype);
723 IdlException.prototype.test_self = function()
728 // "For every exception that is not declared with the
729 // [NoInterfaceObject] extended attribute, a corresponding property
730 // must exist on the exception’s relevant namespace object. The name of
731 // the property is the identifier of the exception, and its value is an
732 // object called the exception interface object, which provides access
733 // to any constants that have been associated with the exception. The
734 // property has the attributes { [[Writable]]: true, [[Enumerable]]:
735 // false, [[Configurable]]: true }."
736 assert_own_property(window, this.name,
737 "window does not have own property " + format_value(this.name));
738 var desc = Object.getOwnPropertyDescriptor(window, this.name);
739 assert_false("get" in desc, "window's property " + format_value(this.name) + " has getter");
740 assert_false("set" in desc, "window's property " + format_value(this.name) + " has setter");
741 assert_true(desc.writable, "window's property " + format_value(this.name) + " is not writable");
742 assert_false(desc.enumerable, "window's property " + format_value(this.name) + " is enumerable");
743 assert_true(desc.configurable, "window's property " + format_value(this.name) + " is not configurable");
745 // "The exception interface object for a given exception must be a
747 // "If an object is defined to be a function object, then it has
748 // characteristics as follows:"
749 // "Its [[Prototype]] internal property is the Function prototype
751 // Note: This doesn't match browsers as of December 2011, see
752 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=14813
753 assert_equals(Object.getPrototypeOf(window[this.name]), Function.prototype,
754 "prototype of window's property " + format_value(this.name) + " is not Function.prototype");
755 // "Its [[Get]] internal property is set as described in ECMA-262
756 // section 15.3.5.4."
757 // Not much to test for this.
758 // "Its [[Construct]] internal property is set as described in ECMA-262
761 // "Its [[HasInstance]] internal property is set as described in
762 // ECMA-262 section 15.3.5.3, unless otherwise specified."
764 // "Its [[Class]] internal property is “Function”."
765 // String() returns something implementation-dependent, because it
766 // calls Function#toString.
767 assert_class_string(window[this.name], "Function",
768 "class string of " + this.name);
770 // TODO: Test 4.9.1.1. Exception interface object [[Call]] method
771 // (which does not match browsers:
772 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=14885)
773 }.bind(this), this.name + " exception: existence and properties of exception interface object");
777 assert_own_property(window, this.name,
778 "window does not have own property " + format_value(this.name));
780 // "The exception interface object must also have a property named
781 // “prototype” with attributes { [[Writable]]: false, [[Enumerable]]:
782 // false, [[Configurable]]: false } whose value is an object called the
783 // exception interface prototype object. This object also provides
784 // access to the constants that are declared on the exception."
785 assert_own_property(window[this.name], "prototype",
786 'exception "' + this.name + '" does not have own property "prototype"');
787 var desc = Object.getOwnPropertyDescriptor(window[this.name], "prototype");
788 assert_false("get" in desc, this.name + ".prototype has getter");
789 assert_false("set" in desc, this.name + ".prototype has setter");
790 assert_false(desc.writable, this.name + ".prototype is writable");
791 assert_false(desc.enumerable, this.name + ".prototype is enumerable");
792 assert_false(desc.configurable, this.name + ".prototype is configurable");
794 // "The exception interface prototype object for a given exception must
795 // have an internal [[Prototype]] property whose value is as follows:
797 // "If the exception is declared to inherit from another exception,
798 // then the value of the internal [[Prototype]] property is the
799 // exception interface prototype object for the inherited exception.
800 // "Otherwise, the exception is not declared to inherit from another
801 // exception. The value of the internal [[Prototype]] property is the
802 // Error prototype object ([ECMA-262], section 15.11.3.1)."
804 // Note: This doesn't match browsers as of December 2011, see
805 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=14887.
806 var inherit_exception = this.base ? this.base : "Error";
807 assert_own_property(window, inherit_exception,
808 'should inherit from ' + inherit_exception + ', but window has no such property');
809 assert_own_property(window[inherit_exception], "prototype",
810 'should inherit from ' + inherit_exception + ', but that object has no "prototype" property');
811 assert_equals(Object.getPrototypeOf(window[this.name].prototype),
812 window[inherit_exception].prototype,
813 'prototype of ' + this.name + '.prototype is not ' + inherit_exception + '.prototype');
815 // "The class string of an exception interface prototype object is the
816 // concatenation of the exception’s identifier and the string
818 assert_class_string(window[this.name].prototype, this.name + "Prototype",
819 "class string of " + this.name + ".prototype");
820 // TODO: Test String(), based on ES definition of
821 // Error.prototype.toString?
822 }.bind(this), this.name + " exception: existence and properties of exception interface prototype object");
826 assert_own_property(window, this.name,
827 "window does not have own property " + format_value(this.name));
828 assert_own_property(window[this.name], "prototype",
829 'interface "' + this.name + '" does not have own property "prototype"');
831 // "There must be a property named “name” on the exception interface
832 // prototype object with attributes { [[Writable]]: true,
833 // [[Enumerable]]: false, [[Configurable]]: true } and whose value is
834 // the identifier of the exception."
835 assert_own_property(window[this.name].prototype, "name",
836 'prototype object does not have own property "name"');
837 var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, "name");
838 assert_false("get" in desc, this.name + ".prototype.name has getter");
839 assert_false("set" in desc, this.name + ".prototype.name has setter");
840 assert_true(desc.writable, this.name + ".prototype.name is not writable");
841 assert_false(desc.enumerable, this.name + ".prototype.name is enumerable");
842 assert_true(desc.configurable, this.name + ".prototype.name is not configurable");
843 assert_equals(desc.value, this.name, this.name + ".prototype.name has incorrect value");
844 }.bind(this), this.name + " exception: existence and properties of exception interface prototype object's \"name\" property");
848 assert_own_property(window, this.name,
849 "window does not have own property " + format_value(this.name));
850 assert_own_property(window[this.name], "prototype",
851 'interface "' + this.name + '" does not have own property "prototype"');
853 // "If the [NoInterfaceObject] extended attribute was not specified on
854 // the exception, then there must also be a property named
855 // “constructor” on the exception interface prototype object with
856 // attributes { [[Writable]]: true, [[Enumerable]]: false,
857 // [[Configurable]]: true } and whose value is a reference to the
858 // exception interface object for the exception."
859 assert_own_property(window[this.name].prototype, "constructor",
860 this.name + '.prototype does not have own property "constructor"');
861 var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, "constructor");
862 assert_false("get" in desc, this.name + ".prototype.constructor has getter");
863 assert_false("set" in desc, this.name + ".prototype.constructor has setter");
864 assert_true(desc.writable, this.name + ".prototype.constructor is not writable");
865 assert_false(desc.enumerable, this.name + ".prototype.constructor is enumerable");
866 assert_true(desc.configurable, this.name + ".prototype.constructor in not configurable");
867 assert_equals(window[this.name].prototype.constructor, window[this.name],
868 this.name + '.prototype.constructor is not the same object as ' + this.name);
869 }.bind(this), this.name + " exception: existence and properties of exception interface prototype object's \"constructor\" property");
873 IdlException.prototype.test_members = function()
876 for (var i = 0; i < this.members.length; i++)
878 var member = this.members[i];
883 if (member.type == "const" && member.name != "prototype")
887 assert_own_property(window, this.name,
888 "window does not have own property " + format_value(this.name));
890 // "For each constant defined on the exception, there must be a
891 // corresponding property on the exception interface object, if
892 // it exists, if the identifier of the constant is not
894 assert_own_property(window[this.name], member.name);
895 // "The value of the property is the ECMAScript value that is
896 // equivalent to the constant’s IDL value, according to the
897 // rules in section 4.2 above."
898 assert_equals(window[this.name][member.name], constValue(member.value),
899 "property has wrong value");
900 // "The property has attributes { [[Writable]]: false,
901 // [[Enumerable]]: true, [[Configurable]]: false }."
902 var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name);
903 assert_false("get" in desc, "property has getter");
904 assert_false("set" in desc, "property has setter");
905 assert_false(desc.writable, "property is writable");
906 assert_true(desc.enumerable, "property is not enumerable");
907 assert_false(desc.configurable, "property is configurable");
908 }.bind(this), this.name + " exception: constant " + member.name + " on exception interface object");
909 // "In addition, a property with the same characteristics must
910 // exist on the exception interface prototype object."
913 assert_own_property(window, this.name,
914 "window does not have own property " + format_value(this.name));
915 assert_own_property(window[this.name], "prototype",
916 'exception "' + this.name + '" does not have own property "prototype"');
918 assert_own_property(window[this.name].prototype, member.name);
919 assert_equals(window[this.name].prototype[member.name], constValue(member.value),
920 "property has wrong value");
921 var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, member.name);
922 assert_false("get" in desc, "property has getter");
923 assert_false("set" in desc, "property has setter");
924 assert_false(desc.writable, "property is writable");
925 assert_true(desc.enumerable, "property is not enumerable");
926 assert_false(desc.configurable, "property is configurable");
927 }.bind(this), this.name + " exception: constant " + member.name + " on exception interface prototype object");
929 else if (member.type == "field")
933 assert_own_property(window, this.name,
934 "window does not have own property " + format_value(this.name));
935 assert_own_property(window[this.name], "prototype",
936 'exception "' + this.name + '" does not have own property "prototype"');
938 // "For each exception field, there must be a corresponding
939 // property on the exception interface prototype object, whose
940 // characteristics are as follows:
941 // "The name of the property is the identifier of the exception
943 assert_own_property(window[this.name].prototype, member.name);
944 // "The property has attributes { [[Get]]: G, [[Enumerable]]:
945 // true, [[Configurable]]: true }, where G is the exception
946 // field getter, defined below."
947 var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, member.name);
948 assert_false("value" in desc, "property descriptor has value but is supposed to be accessor");
949 assert_false("writable" in desc, 'property descriptor has "writable" field but is supposed to be accessor');
950 // TODO: ES5 doesn't seem to say whether desc should have a
952 assert_true(desc.enumerable, "property is not enumerable");
953 assert_true(desc.configurable, "property is not configurable");
954 // "The exception field getter is a Function object whose
955 // behavior when invoked is as follows:"
956 assert_equals(typeof desc.get, "function", "typeof getter");
957 // "The value of the Function object’s “length” property is the
959 // This test is before the TypeError tests so that it's easiest
960 // to see that Firefox 11a1 only fails one assert in this test.
961 assert_equals(desc.get.length, 0, "getter length");
962 // "Let O be the result of calling ToObject on the this value.
963 // "If O is not a platform object representing an exception for
964 // the exception on which the exception field was declared,
965 // then throw a TypeError."
966 // TODO: Test on a platform object representing an exception.
967 assert_throws(new TypeError(), function()
969 window[this.name].prototype[member.name];
970 }.bind(this), "getting property on prototype object must throw TypeError");
971 assert_throws(new TypeError(), function()
974 }.bind(this), "calling getter on wrong object type must throw TypeError");
975 }.bind(this), this.name + " exception: field " + member.name + " on exception interface prototype object");
981 IdlException.prototype.test_object = function(desc)
984 var obj, exception = null;
996 assert_equals(exception, null, "Unexpected exception when evaluating object");
997 assert_equals(typeof obj, "object", "wrong typeof object");
999 // We can't easily test that its prototype is correct if there's no
1000 // interface object, or the object is from a different global
1001 // environment (not instanceof Object). TODO: test in this case that
1002 // its prototype at least looks correct, even if we can't test that
1003 // it's actually correct.
1004 if (!this.has_extended_attribute("NoInterfaceObject")
1005 && (typeof obj != "object" || obj instanceof Object))
1007 assert_own_property(window, this.name,
1008 "window does not have own property " + format_value(this.name));
1009 assert_own_property(window[this.name], "prototype",
1010 'exception "' + this.name + '" does not have own property "prototype"');
1012 // "The value of the internal [[Prototype]] property of the
1013 // exception object must be the exception interface prototype
1014 // object from the global environment the exception object is
1015 // associated with."
1016 assert_equals(Object.getPrototypeOf(obj),
1017 window[this.name].prototype,
1018 desc + "'s prototype is not " + this.name + ".prototype");
1021 // "The class string of the exception object must be the identifier of
1023 assert_class_string(obj, this.name, "class string of " + desc);
1024 // Stringifier is not defined for DOMExceptions, because message isn't
1026 }.bind(this), this.name + " must be represented by " + desc);
1028 for (var i = 0; i < this.members.length; i++)
1030 var member = this.members[i];
1033 assert_equals(exception, null, "Unexpected exception when evaluating object");
1034 assert_equals(typeof obj, "object", "wrong typeof object");
1035 assert_inherits(obj, member.name);
1036 if (member.type == "const")
1038 assert_equals(obj[member.name], constValue(member.value));
1040 if (member.type == "field")
1042 this.array.assert_type_is(obj[member.name], member.idlType);
1044 }.bind(this), this.name + " exception: " + desc + ' must inherit property "' + member.name + '" with the proper type');
1049 /// IdlInterface ///
1050 function IdlInterface(obj) { IdlExceptionOrInterface.call(this, obj); }
1051 IdlInterface.prototype = Object.create(IdlExceptionOrInterface.prototype);
1052 IdlInterface.prototype.is_callback = function()
1055 return this.has_extended_attribute("Callback");
1059 IdlInterface.prototype.has_constants = function()
1062 return this.members.some(function(member) {
1063 return member.type === "const";
1068 IdlInterface.prototype.test_self = function()
1073 // This function tests WebIDL as of 2012-11-28.
1075 // "For every interface that:
1076 // * is a callback interface that has constants declared on it, or
1077 // * is a non-callback interface that is not declared with the
1078 // [NoInterfaceObject] extended attribute,
1079 // a corresponding property MUST exist on the ECMAScript global object.
1080 // The name of the property is the identifier of the interface, and its
1081 // value is an object called the interface object.
1082 // The property has the attributes { [[Writable]]: true,
1083 // [[Enumerable]]: false, [[Configurable]]: true }."
1084 if (this.is_callback() && !this.has_constants()) {
1088 // TODO: Should we test here that the property is actually writable
1089 // etc., or trust getOwnPropertyDescriptor?
1090 assert_own_property(window, this.name,
1091 "window does not have own property " + format_value(this.name));
1092 var desc = Object.getOwnPropertyDescriptor(window, this.name);
1093 assert_false("get" in desc, "window's property " + format_value(this.name) + " has getter");
1094 assert_false("set" in desc, "window's property " + format_value(this.name) + " has setter");
1095 assert_true(desc.writable, "window's property " + format_value(this.name) + " is not writable");
1096 assert_false(desc.enumerable, "window's property " + format_value(this.name) + " is enumerable");
1097 assert_true(desc.configurable, "window's property " + format_value(this.name) + " is not configurable");
1099 if (this.is_callback()) {
1100 // "The internal [[Prototype]] property of an interface object for
1101 // a callback interface MUST be the Object.prototype object."
1102 assert_equals(Object.getPrototypeOf(window[this.name]), Object.prototype,
1103 "prototype of window's property " + format_value(this.name) + " is not Object.prototype");
1108 // "The interface object for a given non-callback interface is a
1109 // function object."
1110 // "If an object is defined to be a function object, then it has
1111 // characteristics as follows:"
1113 // "* Its [[Prototype]] internal property is the Function prototype
1115 assert_equals(Object.getPrototypeOf(window[this.name]), Function.prototype,
1116 "prototype of window's property " + format_value(this.name) + " is not Function.prototype");
1118 // "* Its [[Get]] internal property is set as described in ECMA-262
1119 // section 15.3.5.4."
1120 // Not much to test for this.
1122 // "* Its [[Construct]] internal property is set as described in
1123 // ECMA-262 section 13.2.2."
1124 // Tested below if no constructor is defined. TODO: test constructors
1127 // "* Its [[HasInstance]] internal property is set as described in
1128 // ECMA-262 section 15.3.5.3, unless otherwise specified."
1131 // "* Its [[NativeBrand]] internal property is “Function”."
1132 // String() returns something implementation-dependent, because it calls
1133 // Function#toString.
1134 assert_class_string(window[this.name], "Function", "class string of " + this.name);
1136 if (!this.has_extended_attribute("Constructor")) {
1137 // "The internal [[Call]] method of the interface object behaves as
1140 // "If I was not declared with a [Constructor] extended attribute,
1141 // then throw a TypeError."
1142 assert_throws(new TypeError(), function() {
1143 window[this.name]();
1144 }.bind(this), "interface object didn't throw TypeError when called as a function");
1145 assert_throws(new TypeError(), function() {
1146 new window[this.name]();
1147 }.bind(this), "interface object didn't throw TypeError when called as a constructor");
1149 }.bind(this), this.name + " interface: existence and properties of interface object");
1151 if (!this.is_callback()) {
1153 // This function tests WebIDL as of 2013-08-25.
1154 // http://dev.w3.org/2006/webapi/WebIDL/#es-interface-call
1156 assert_own_property(window, this.name,
1157 "window does not have own property " + format_value(this.name));
1159 // "Interface objects for non-callback interfaces MUST have a
1160 // property named “length” with attributes { [[Writable]]: false,
1161 // [[Enumerable]]: false, [[Configurable]]: false } whose value is
1163 assert_own_property(window[this.name], "length");
1164 var desc = Object.getOwnPropertyDescriptor(window[this.name], "length");
1165 assert_false("get" in desc, this.name + ".length has getter");
1166 assert_false("set" in desc, this.name + ".length has setter");
1167 assert_false(desc.writable, this.name + ".length is writable");
1168 assert_false(desc.enumerable, this.name + ".length is enumerable");
1169 assert_false(desc.configurable, this.name + ".length is configurable");
1171 var constructors = this.extAttrs
1172 .filter(function(attr) { return attr.name == "Constructor"; });
1173 var expected_length;
1174 if (!constructors.length) {
1175 // "If the [Constructor] extended attribute, does not appear on
1176 // the interface definition, then the value is 0."
1177 expected_length = 0;
1179 // "Otherwise, the value is determined as follows: . . .
1180 // "Return the length of the shortest argument list of the
1182 expected_length = constructors.map(function(attr) {
1183 return attr.arguments ? attr.arguments.filter(function(arg) {
1184 return !arg.optional;
1187 .reduce(function(m, n) { return Math.min(m, n); });
1189 assert_equals(window[this.name].length, expected_length, "wrong value for " + this.name + ".length");
1190 }.bind(this), this.name + " interface object length");
1193 // TODO: Test named constructors if I find any interfaces that have them.
1197 assert_own_property(window, this.name,
1198 "window does not have own property " + format_value(this.name));
1200 if (this.has_extended_attribute("Callback")) {
1201 assert_false("prototype" in window[this.name],
1202 this.name + ' should not have a "prototype" property');
1206 // "The interface object must also have a property named “prototype”
1207 // with attributes { [[Writable]]: false, [[Enumerable]]: false,
1208 // [[Configurable]]: false } whose value is an object called the
1209 // interface prototype object. This object has properties that
1210 // correspond to the attributes and operations defined on the
1211 // interface, and is described in more detail in section 4.5.3 below."
1212 assert_own_property(window[this.name], "prototype",
1213 'interface "' + this.name + '" does not have own property "prototype"');
1214 var desc = Object.getOwnPropertyDescriptor(window[this.name], "prototype");
1215 assert_false("get" in desc, this.name + ".prototype has getter");
1216 assert_false("set" in desc, this.name + ".prototype has setter");
1217 assert_false(desc.writable, this.name + ".prototype is writable");
1218 assert_false(desc.enumerable, this.name + ".prototype is enumerable");
1219 assert_false(desc.configurable, this.name + ".prototype is configurable");
1221 // Next, test that the [[Prototype]] of the interface prototype object
1222 // is correct. (This is made somewhat difficult by the existence of
1223 // [NoInterfaceObject].)
1224 // TODO: Aryeh thinks there's at least other place in this file where
1225 // we try to figure out if an interface prototype object is
1226 // correct. Consolidate that code.
1228 // "The interface prototype object for a given interface A must have an
1229 // internal [[Prototype]] property whose value is as follows:
1230 // "If A is not declared to inherit from another interface, then the
1231 // value of the internal [[Prototype]] property of A is the Array
1232 // prototype object ([ECMA-262], section 15.4.4) if the interface was
1233 // declared with ArrayClass, or the Object prototype object otherwise
1234 // ([ECMA-262], section 15.2.4).
1235 // "Otherwise, A does inherit from another interface. The value of the
1236 // internal [[Prototype]] property of A is the interface prototype
1237 // object for the inherited interface."
1238 var inherit_interface, inherit_interface_has_interface_object;
1240 inherit_interface = this.base;
1241 inherit_interface_has_interface_object =
1243 .members[inherit_interface]
1244 .has_extended_attribute("NoInterfaceObject");
1245 } else if (this.has_extended_attribute('ArrayClass')) {
1246 inherit_interface = 'Array';
1247 inherit_interface_has_interface_object = true;
1249 inherit_interface = 'Object';
1250 inherit_interface_has_interface_object = true;
1252 if (inherit_interface_has_interface_object) {
1253 assert_own_property(window, inherit_interface,
1254 'should inherit from ' + inherit_interface + ', but window has no such property');
1255 assert_own_property(window[inherit_interface], 'prototype',
1256 'should inherit from ' + inherit_interface + ', but that object has no "prototype" property');
1257 assert_equals(Object.getPrototypeOf(window[this.name].prototype),
1258 window[inherit_interface].prototype,
1259 'prototype of ' + this.name + '.prototype is not ' + inherit_interface + '.prototype');
1261 // We can't test that we get the correct object, because this is the
1262 // only way to get our hands on it. We only test that its class
1263 // string, at least, is correct.
1264 assert_class_string(Object.getPrototypeOf(window[this.name].prototype),
1265 inherit_interface + 'Prototype',
1266 'Class name for prototype of ' + this.name +
1267 '.prototype is not "' + inherit_interface + 'Prototype"');
1270 // "The class string of an interface prototype object is the
1271 // concatenation of the interface’s identifier and the string
1273 assert_class_string(window[this.name].prototype, this.name + "Prototype",
1274 "class string of " + this.name + ".prototype");
1275 // String() should end up calling {}.toString if nothing defines a
1277 if (!this.has_stringifier()) {
1278 assert_equals(String(window[this.name].prototype), "[object " + this.name + "Prototype]",
1279 "String(" + this.name + ".prototype)");
1281 }.bind(this), this.name + " interface: existence and properties of interface prototype object");
1285 assert_own_property(window, this.name,
1286 "window does not have own property " + format_value(this.name));
1288 if (this.has_extended_attribute("Callback")) {
1289 assert_false("prototype" in window[this.name],
1290 this.name + ' should not have a "prototype" property');
1294 assert_own_property(window[this.name], "prototype",
1295 'interface "' + this.name + '" does not have own property "prototype"');
1297 // "If the [NoInterfaceObject] extended attribute was not specified on
1298 // the interface, then the interface prototype object must also have a
1299 // property named “constructor” with attributes { [[Writable]]: true,
1300 // [[Enumerable]]: false, [[Configurable]]: true } whose value is a
1301 // reference to the interface object for the interface."
1302 assert_own_property(window[this.name].prototype, "constructor",
1303 this.name + '.prototype does not have own property "constructor"');
1304 var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, "constructor");
1305 assert_false("get" in desc, this.name + ".prototype.constructor has getter");
1306 assert_false("set" in desc, this.name + ".prototype.constructor has setter");
1307 assert_true(desc.writable, this.name + ".prototype.constructor is not writable");
1308 assert_false(desc.enumerable, this.name + ".prototype.constructor is enumerable");
1309 assert_true(desc.configurable, this.name + ".prototype.constructor in not configurable");
1310 assert_equals(window[this.name].prototype.constructor, window[this.name],
1311 this.name + '.prototype.constructor is not the same object as ' + this.name);
1312 }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property');
1316 IdlInterface.prototype.test_members = function()
1319 for (var i = 0; i < this.members.length; i++)
1321 var member = this.members[i];
1322 if (member.untested)
1326 if (member.type == "const")
1330 assert_own_property(window, this.name,
1331 "window does not have own property " + format_value(this.name));
1333 // "For each constant defined on an interface A, there must be
1334 // a corresponding property on the interface object, if it
1336 assert_own_property(window[this.name], member.name);
1337 // "The value of the property is that which is obtained by
1338 // converting the constant’s IDL value to an ECMAScript
1340 assert_equals(window[this.name][member.name], constValue(member.value),
1341 "property has wrong value");
1342 // "The property has attributes { [[Writable]]: false,
1343 // [[Enumerable]]: true, [[Configurable]]: false }."
1344 var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name);
1345 assert_false("get" in desc, "property has getter");
1346 assert_false("set" in desc, "property has setter");
1347 assert_false(desc.writable, "property is writable");
1348 assert_true(desc.enumerable, "property is not enumerable");
1349 assert_false(desc.configurable, "property is configurable");
1350 }.bind(this), this.name + " interface: constant " + member.name + " on interface object");
1351 // "In addition, a property with the same characteristics must
1352 // exist on the interface prototype object."
1355 assert_own_property(window, this.name,
1356 "window does not have own property " + format_value(this.name));
1358 if (this.has_extended_attribute("Callback")) {
1359 assert_false("prototype" in window[this.name],
1360 this.name + ' should not have a "prototype" property');
1364 assert_own_property(window[this.name], "prototype",
1365 'interface "' + this.name + '" does not have own property "prototype"');
1367 assert_own_property(window[this.name].prototype, member.name);
1368 assert_equals(window[this.name].prototype[member.name], constValue(member.value),
1369 "property has wrong value");
1370 var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name);
1371 assert_false("get" in desc, "property has getter");
1372 assert_false("set" in desc, "property has setter");
1373 assert_false(desc.writable, "property is writable");
1374 assert_true(desc.enumerable, "property is not enumerable");
1375 assert_false(desc.configurable, "property is configurable");
1376 }.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object");
1378 else if (member.type == "attribute")
1380 if (member.has_extended_attribute("Unforgeable"))
1382 // We do the checks in test_interface_of instead
1387 assert_own_property(window, this.name,
1388 "window does not have own property " + format_value(this.name));
1389 assert_own_property(window[this.name], "prototype",
1390 'interface "' + this.name + '" does not have own property "prototype"');
1392 if (member["static"]) {
1393 assert_own_property(window[this.name], member.name,
1394 "The interface object must have a property " +
1395 format_value(member.name));
1399 assert_true(member.name in window[this.name].prototype,
1400 "The prototype object must have a property " +
1401 format_value(member.name));
1403 // TODO: Needs to test for LenientThis.
1404 assert_throws(new TypeError(), function() {
1405 window[this.name].prototype[member.name];
1406 }.bind(this), "getting property on prototype object must throw TypeError");
1407 do_interface_attribute_asserts(window[this.name].prototype, member);
1409 }.bind(this), this.name + " interface: attribute " + member.name);
1411 else if (member.type == "operation")
1413 // TODO: Need to correctly handle multiple operations with the same
1417 // Unnamed getter or such
1422 assert_own_property(window, this.name,
1423 "window does not have own property " + format_value(this.name));
1425 if (this.has_extended_attribute("Callback")) {
1426 assert_false("prototype" in window[this.name],
1427 this.name + ' should not have a "prototype" property');
1431 assert_own_property(window[this.name], "prototype",
1432 'interface "' + this.name + '" does not have own property "prototype"');
1434 // "For each unique identifier of an operation defined on the
1435 // interface, there must be a corresponding property on the
1436 // interface prototype object (if it is a regular operation) or
1437 // the interface object (if it is a static operation), unless
1438 // the effective overload set for that identifier and operation
1439 // and with an argument count of 0 (for the ECMAScript language
1440 // binding) has no entries."
1442 var prototypeOrInterfaceObject;
1443 if (member["static"]) {
1444 assert_own_property(window[this.name], member.name,
1445 "interface prototype object missing static operation");
1446 prototypeOrInterfaceObject = window[this.name];
1450 assert_own_property(window[this.name].prototype, member.name,
1451 "interface prototype object missing non-static operation");
1452 prototypeOrInterfaceObject = window[this.name].prototype;
1455 var desc = Object.getOwnPropertyDescriptor(prototypeOrInterfaceObject, member.name);
1456 // "The property has attributes { [[Writable]]: true,
1457 // [[Enumerable]]: true, [[Configurable]]: true }."
1458 assert_false("get" in desc, "property has getter");
1459 assert_false("set" in desc, "property has setter");
1460 assert_true(desc.writable, "property is not writable");
1461 assert_true(desc.enumerable, "property is not enumerable");
1462 assert_true(desc.configurable, "property is not configurable");
1463 // "The value of the property is a Function object whose
1464 // behavior is as follows . . ."
1465 assert_equals(typeof prototypeOrInterfaceObject[member.name], "function",
1466 "property must be a function");
1467 // "The value of the Function object’s “length” property is
1468 // a Number determined as follows:
1470 // "Return the length of the shortest argument list of the
1473 // TODO: Doesn't handle overloading or variadic arguments.
1474 assert_equals(prototypeOrInterfaceObject[member.name].length,
1475 member.arguments.filter(function(arg) {
1476 return !arg.optional;
1478 "property has wrong .length");
1480 // Make some suitable arguments
1481 var args = member.arguments.map(function(arg) {
1482 return create_suitable_object(arg.idlType);
1485 // "Let O be a value determined as follows:
1487 // "Otherwise, throw a TypeError."
1488 // This should be hit if the operation is not static, there is
1489 // no [ImplicitThis] attribute, and the this value is null.
1491 // TODO: We currently ignore the [ImplicitThis] case.
1492 if (!member["static"]) {
1493 assert_throws(new TypeError(), function() {
1494 window[this.name].prototype[member.name].apply(null, args);
1495 }, "calling operation with this = null didn't throw TypeError");
1498 // ". . . If O is not null and is also not a platform object
1499 // that implements interface I, throw a TypeError."
1501 // TODO: Test a platform object that implements some other
1502 // interface. (Have to be sure to get inheritance right.)
1503 assert_throws(new TypeError(), function() {
1504 window[this.name].prototype[member.name].apply({}, args);
1505 }, "calling operation with this = {} didn't throw TypeError");
1506 }.bind(this), this.name + " interface: operation " + member.name +
1507 "(" + member.arguments.map(function(m) { return m.idlType.idlType; }) +
1510 // TODO: check more member types, like stringifier
1515 IdlInterface.prototype.test_object = function(desc)
1518 var obj, exception = null;
1528 // TODO: WebIDLParser doesn't currently support named legacycallers, so I'm
1529 // not sure what those would look like in the AST
1530 var expected_typeof = this.members.some(function(member)
1532 return member.legacycaller
1533 || ("idlType" in member && member.idlType.legacycaller)
1534 || ("idlType" in member && typeof member.idlType == "object"
1535 && "idlType" in member.idlType && member.idlType.idlType == "legacycaller");
1536 }) ? "function" : "object";
1538 this.test_primary_interface_of(desc, obj, exception, expected_typeof);
1539 var current_interface = this;
1540 while (current_interface)
1542 if (!(current_interface.name in this.array.members))
1544 throw "Interface " + current_interface.name + " not found (inherited by " + this.name + ")";
1546 if (current_interface.prevent_multiple_testing && current_interface.already_tested)
1550 current_interface.test_interface_of(desc, obj, exception, expected_typeof);
1551 current_interface = this.array.members[current_interface.base];
1556 IdlInterface.prototype.test_primary_interface_of = function(desc, obj, exception, expected_typeof)
1559 // We can't easily test that its prototype is correct if there's no
1560 // interface object, or the object is from a different global environment
1561 // (not instanceof Object). TODO: test in this case that its prototype at
1562 // least looks correct, even if we can't test that it's actually correct.
1563 if (!this.has_extended_attribute("NoInterfaceObject")
1564 && (typeof obj != expected_typeof || obj instanceof Object))
1568 assert_equals(exception, null, "Unexpected exception when evaluating object");
1569 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1570 assert_own_property(window, this.name,
1571 "window does not have own property " + format_value(this.name));
1572 assert_own_property(window[this.name], "prototype",
1573 'interface "' + this.name + '" does not have own property "prototype"');
1575 // "The value of the internal [[Prototype]] property of the
1576 // platform object is the interface prototype object of the primary
1577 // interface from the platform object’s associated global
1579 assert_equals(Object.getPrototypeOf(obj),
1580 window[this.name].prototype,
1581 desc + "'s prototype is not " + this.name + ".prototype");
1582 }.bind(this), this.name + " must be primary interface of " + desc);
1585 // "The class string of a platform object that implements one or more
1586 // interfaces must be the identifier of the primary interface of the
1587 // platform object."
1590 assert_equals(exception, null, "Unexpected exception when evaluating object");
1591 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1592 assert_class_string(obj, this.name, "class string of " + desc);
1593 if (!this.has_stringifier())
1595 assert_equals(String(obj), "[object " + this.name + "]", "String(" + desc + ")");
1597 }.bind(this), "Stringification of " + desc);
1601 IdlInterface.prototype.test_interface_of = function(desc, obj, exception, expected_typeof)
1604 // TODO: Indexed and named properties, more checks on interface members
1605 this.already_tested = true;
1607 for (var i = 0; i < this.members.length; i++)
1609 var member = this.members[i];
1610 if (member.has_extended_attribute("Unforgeable"))
1614 assert_equals(exception, null, "Unexpected exception when evaluating object");
1615 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1616 do_interface_attribute_asserts(obj, member);
1617 }.bind(this), this.name + " interface: " + desc + ' must have own property "' + member.name + '"');
1619 else if ((member.type == "const"
1620 || member.type == "attribute"
1621 || member.type == "operation")
1626 assert_equals(exception, null, "Unexpected exception when evaluating object");
1627 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1628 if (!member["static"]) {
1629 assert_inherits(obj, member.name);
1630 if (member.type == "const")
1632 assert_equals(obj[member.name], constValue(member.value));
1634 if (member.type == "attribute")
1636 // Attributes are accessor properties, so they might
1637 // legitimately throw an exception rather than returning
1639 var property, thrown = false;
1642 property = obj[member.name];
1650 this.array.assert_type_is(property, member.idlType);
1653 if (member.type == "operation")
1655 assert_equals(typeof obj[member.name], "function");
1658 }.bind(this), this.name + " interface: " + desc + ' must inherit property "' + member.name + '" with the proper type (' + i + ')');
1660 // TODO: This is wrong if there are multiple operations with the same
1662 // TODO: Test passing arguments of the wrong type.
1663 if (member.type == "operation" && member.name && member.arguments.length)
1667 assert_equals(exception, null, "Unexpected exception when evaluating object");
1668 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1669 if (!member["static"]) {
1670 assert_inherits(obj, member.name);
1674 assert_false(member.name in obj);
1677 for (var i = 0; i < member.arguments.length; i++)
1679 if (member.arguments[i].optional)
1683 assert_throws(new TypeError(), function()
1685 obj[member.name].apply(obj, args);
1686 }.bind(this), "Called with " + i + " arguments");
1688 args.push(create_suitable_object(member.arguments[i].idlType));
1690 }.bind(this), this.name + " interface: calling " + member.name +
1691 "(" + member.arguments.map(function(m) { return m.idlType.idlType; }) +
1692 ") on " + desc + " with too few arguments must throw TypeError");
1698 IdlInterface.prototype.has_stringifier = function()
1701 if (this.members.some(function(member) { return member.stringifier; })) {
1705 this.array.members[this.base].has_stringifier()) {
1712 function do_interface_attribute_asserts(obj, member)
1715 // "For each attribute defined on the interface, there must exist a
1716 // corresponding property. If the attribute was declared with the
1717 // [Unforgeable] extended attribute, then the property exists on every
1718 // object that implements the interface. Otherwise, it exists on the
1719 // interface’s interface prototype object."
1721 // This is called by test_self() with the prototype as obj, and by
1722 // test_interface_of() with the object as obj.
1723 assert_own_property(obj, member.name);
1725 // "The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
1726 // true, [[Configurable]]: configurable }, where:
1727 // "configurable is false if the attribute was declared with the
1728 // [Unforgeable] extended attribute and true otherwise;
1729 // "G is the attribute getter, defined below; and
1730 // "S is the attribute setter, also defined below."
1731 var desc = Object.getOwnPropertyDescriptor(obj, member.name);
1732 assert_false("value" in desc, 'property descriptor has value but is supposed to be accessor');
1733 assert_false("writable" in desc, 'property descriptor has "writable" field but is supposed to be accessor');
1734 assert_true(desc.enumerable, "property is not enumerable");
1735 if (member.has_extended_attribute("Unforgeable"))
1737 assert_false(desc.configurable, "[Unforgeable] property must not be configurable");
1741 assert_true(desc.configurable, "property must be configurable");
1744 // "The attribute getter is a Function object whose behavior when invoked
1747 // "The value of the Function object’s “length” property is the Number
1749 assert_equals(typeof desc.get, "function", "getter must be Function");
1750 assert_equals(desc.get.length, 0, "getter length must be 0");
1751 // TODO: Account for LenientThis
1752 assert_throws(new TypeError(), function()
1755 }.bind(this), "calling getter on wrong object type must throw TypeError");
1757 // TODO: Test calling setter on the interface prototype (should throw
1758 // TypeError in most cases).
1760 // "The attribute setter is undefined if the attribute is declared readonly
1761 // and has neither a [PutForwards] nor a [Replaceable] extended attribute
1762 // declared on it. Otherwise, it is a Function object whose behavior when
1763 // invoked is as follows:
1765 // "The value of the Function object’s “length” property is the Number
1768 && !member.has_extended_attribute("PutForwards")
1769 && !member.has_extended_attribute("Replaceable"))
1771 assert_equals(desc.set, undefined, "setter must be undefined for readonly attributes");
1775 assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes");
1776 assert_equals(desc.set.length, 1, "setter length must be 1");
1781 /// IdlInterfaceMember ///
1782 function IdlInterfaceMember(obj)
1786 * obj is an object produced by the WebIDLParser.js "ifMember" production.
1787 * We just forward all properties to this object without modification,
1788 * except for special extAttrs handling.
1794 if (!("extAttrs" in this))
1801 IdlInterfaceMember.prototype = Object.create(IdlObject.prototype);
1803 /// Internal helper functions ///
1804 function create_suitable_object(type)
1808 * type is an object produced by the WebIDLParser.js "type" production. We
1809 * return a JavaScript value that matches the type, if we can figure out
1816 switch (type.idlType)
1822 case "byte": case "octet": case "short": case "unsigned short":
1823 case "long": case "unsigned long": case "long long":
1824 case "unsigned long long": case "float": case "double":
1825 case "unrestricted float": case "unrestricted double":
1835 return document.createTextNode("abc");
1842 // Used for IdlArray.prototype.assert_type_is
1843 function IdlEnum(obj)
1847 * obj is an object produced by the WebIDLParser.js "dictionary"
1851 /** Self-explanatory. */
1852 this.name = obj.name;
1854 /** An array of values produced by the "enum" production. */
1855 this.values = obj.values;
1860 IdlEnum.prototype = Object.create(IdlObject.prototype);
1863 // Used for IdlArray.prototype.assert_type_is
1864 function IdlTypedef(obj)
1868 * obj is an object produced by the WebIDLParser.js "typedef"
1872 /** Self-explanatory. */
1873 this.name = obj.name;
1875 /** An array of values produced by the "typedef" production. */
1876 this.values = obj.values;
1881 IdlTypedef.prototype = Object.create(IdlObject.prototype);
1884 // vim: set expandtab shiftwidth=4 tabstop=4 foldmarker=@{,@} foldmethod=marker: