Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / LayoutTests / resources / idlharness.js
blob8e41703e65845d3a75a610ef8a7f1c5532626d9c
1 /*
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
9 */
11 /* For user documentation see docs/idlharness.md */
13 /**
14  * Notes for people who want to edit this file (not just use it as a library):
15  *
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.
21  *
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
25  * grammar:
26  *
27  *   https://github.com/darobin/webidl.js/blob/master/lib/grammar.peg
28  *
29  * So for instance:
30  *
31  *   // interface definition
32  *   interface
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 }; }
35  *
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.
42  *
43  * (Note that the version of WebIDLParser.js we use might sometimes be
44  * out-of-date or forked.)
45  *
46  * The members and methods of the classes defined by this file are all at least
47  * briefly documented, hopefully.
48  */
49 (function(){
50 "use strict";
51 /// Helpers ///
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;
56     return cnt.value;
59 function minOverloadLength(overloads) {
60     if (!overloads.length) {
61         return 0;
62     }
64     return overloads.map(function(attr) {
65         return attr.arguments ? attr.arguments.filter(function(arg) {
66             return !arg.optional && !arg.variadic;
67         }).length : 0;
68     })
69     .reduce(function(m, n) { return Math.min(m, n); });
72 /// IdlArray ///
73 // Entry point
74 self.IdlArray = function()
75 //@{
77     /**
78      * A map from strings to the corresponding named IdlObject, such as
79      * IdlInterface or IdlException.  These are the things that test() will run
80      * tests on.
81      */
82     this.members = {};
84     /**
85      * A map from strings to arrays of strings.  The keys are interface or
86      * exception names, and are expected to also exist as keys in this.members
87      * (otherwise they'll be ignored).  This is populated by add_objects() --
88      * see documentation at the start of the file.  The actual tests will be
89      * run by calling this.members[name].test_object(obj) for each obj in
90      * this.objects[name].  obj is a string that will be eval'd to produce a
91      * JavaScript value, which is supposed to be an object implementing the
92      * given IdlObject (interface, exception, etc.).
93      */
94     this.objects = {};
96     /**
97      * When adding multiple collections of IDLs one at a time, an earlier one
98      * might contain a partial interface or implements statement that depends
99      * on a later one.  Save these up and handle them right before we run
100      * tests.
101      *
102      * .partials is simply an array of objects from WebIDLParser.js'
103      * "partialinterface" production.  .implements maps strings to arrays of
104      * strings, such that
105      *
106      *   A implements B;
107      *   A implements C;
108      *   D implements E;
109      *
110      * results in { A: ["B", "C"], D: ["E"] }.
111      */
112     this.partials = [];
113     this["implements"] = {};
116 //@}
117 IdlArray.prototype.add_idls = function(raw_idls)
118 //@{
120     /** Entry point.  See documentation at beginning of file. */
121     this.internal_add_idls(WebIDL2.parse(raw_idls));
124 //@}
125 IdlArray.prototype.add_untested_idls = function(raw_idls)
126 //@{
128     /** Entry point.  See documentation at beginning of file. */
129     var parsed_idls = WebIDL2.parse(raw_idls);
130     for (var i = 0; i < parsed_idls.length; i++)
131     {
132         parsed_idls[i].untested = true;
133         if ("members" in parsed_idls[i])
134         {
135             for (var j = 0; j < parsed_idls[i].members.length; j++)
136             {
137                 parsed_idls[i].members[j].untested = true;
138             }
139         }
140     }
141     this.internal_add_idls(parsed_idls);
144 //@}
145 IdlArray.prototype.internal_add_idls = function(parsed_idls)
146 //@{
148     /**
149      * Internal helper called by add_idls() and add_untested_idls().
150      * parsed_idls is an array of objects that come from WebIDLParser.js's
151      * "definitions" production.  The add_untested_idls() entry point
152      * additionally sets an .untested property on each object (and its
153      * .members) so that they'll be skipped by test() -- they'll only be
154      * used for base interfaces of tested interfaces, return types, etc.
155      */
156     parsed_idls.forEach(function(parsed_idl)
157     {
158         if (parsed_idl.type == "interface" && parsed_idl.partial)
159         {
160             this.partials.push(parsed_idl);
161             return;
162         }
164         if (parsed_idl.type == "implements")
165         {
166             if (!(parsed_idl.target in this["implements"]))
167             {
168                 this["implements"][parsed_idl.target] = [];
169             }
170             this["implements"][parsed_idl.target].push(parsed_idl["implements"]);
171             return;
172         }
174         parsed_idl.array = this;
175         if (parsed_idl.name in this.members)
176         {
177             throw "Duplicate identifier " + parsed_idl.name;
178         }
179         switch(parsed_idl.type)
180         {
181         case "interface":
182             this.members[parsed_idl.name] =
183                 new IdlInterface(parsed_idl, /* is_callback = */ false);
184             break;
186         case "dictionary":
187             // Nothing to test, but we need the dictionary info around for type
188             // checks
189             this.members[parsed_idl.name] = new IdlDictionary(parsed_idl);
190             break;
192         case "typedef":
193             this.members[parsed_idl.name] = new IdlTypedef(parsed_idl);
194             break;
196         case "callback":
197             // TODO
198             console.log("callback not yet supported");
199             break;
201         case "enum":
202             this.members[parsed_idl.name] = new IdlEnum(parsed_idl);
203             break;
205         case "callback interface":
206             this.members[parsed_idl.name] =
207                 new IdlInterface(parsed_idl, /* is_callback = */ true);
208             break;
210         default:
211             throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported";
212         }
213     }.bind(this));
216 //@}
217 IdlArray.prototype.add_objects = function(dict)
218 //@{
220     /** Entry point.  See documentation at beginning of file. */
221     for (var k in dict)
222     {
223         if (k in this.objects)
224         {
225             this.objects[k] = this.objects[k].concat(dict[k]);
226         }
227         else
228         {
229             this.objects[k] = dict[k];
230         }
231     }
234 //@}
235 IdlArray.prototype.prevent_multiple_testing = function(name)
236 //@{
238     /** Entry point.  See documentation at beginning of file. */
239     this.members[name].prevent_multiple_testing = true;
242 //@}
243 IdlArray.prototype.recursively_get_implements = function(interface_name)
244 //@{
246     /**
247      * Helper function for test().  Returns an array of things that implement
248      * interface_name, so if the IDL contains
249      *
250      *   A implements B;
251      *   B implements C;
252      *   B implements D;
253      *
254      * then recursively_get_implements("A") should return ["B", "C", "D"].
255      */
256     var ret = this["implements"][interface_name];
257     if (ret === undefined)
258     {
259         return [];
260     }
261     for (var i = 0; i < this["implements"][interface_name].length; i++)
262     {
263         ret = ret.concat(this.recursively_get_implements(ret[i]));
264         if (ret.indexOf(ret[i]) != ret.lastIndexOf(ret[i]))
265         {
266             throw "Circular implements statements involving " + ret[i];
267         }
268     }
269     return ret;
272 //@}
273 IdlArray.prototype.test = function()
274 //@{
276     /** Entry point.  See documentation at beginning of file. */
278     // First merge in all the partial interfaces and implements statements we
279     // encountered.
280     this.partials.forEach(function(parsed_idl)
281     {
282         if (!(parsed_idl.name in this.members)
283         || !(this.members[parsed_idl.name] instanceof IdlInterface))
284         {
285             throw "Partial interface " + parsed_idl.name + " with no original interface";
286         }
287         if (parsed_idl.extAttrs)
288         {
289             parsed_idl.extAttrs.forEach(function(extAttr)
290             {
291                 this.members[parsed_idl.name].extAttrs.push(extAttr);
292             }.bind(this));
293         }
294         parsed_idl.members.forEach(function(member)
295         {
296             this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member));
297         }.bind(this));
298     }.bind(this));
299     this.partials = [];
301     for (var lhs in this["implements"])
302     {
303         this.recursively_get_implements(lhs).forEach(function(rhs)
304         {
305             var errStr = lhs + " implements " + rhs + ", but ";
306             if (!(lhs in this.members)) throw errStr + lhs + " is undefined.";
307             if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface.";
308             if (!(rhs in this.members)) throw errStr + rhs + " is undefined.";
309             if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface.";
310             this.members[rhs].members.forEach(function(member)
311             {
312                 this.members[lhs].members.push(new IdlInterfaceMember(member));
313             }.bind(this));
314         }.bind(this));
315     }
316     this["implements"] = {};
318     // Now run test() on every member, and test_object() for every object.
319     for (var name in this.members)
320     {
321         this.members[name].test();
322         if (name in this.objects)
323         {
324             this.objects[name].forEach(function(str)
325             {
326                 this.members[name].test_object(str);
327             }.bind(this));
328         }
329     }
332 //@}
333 IdlArray.prototype.assert_type_is = function(value, type)
334 //@{
336     /**
337      * Helper function that tests that value is an instance of type according
338      * to the rules of WebIDL.  value is any JavaScript value, and type is an
339      * object produced by WebIDLParser.js' "type" production.  That production
340      * is fairly elaborate due to the complexity of WebIDL's types, so it's
341      * best to look at the grammar to figure out what properties it might have.
342      */
343     if (type.idlType == "any")
344     {
345         // No assertions to make
346         return;
347     }
349     if (type.nullable && value === null)
350     {
351         // This is fine
352         return;
353     }
355     if (type.array)
356     {
357         // TODO: not supported yet
358         return;
359     }
361     if (type.sequence)
362     {
363         assert_true(Array.isArray(value), "is not array");
364         if (!value.length)
365         {
366             // Nothing we can do.
367             return;
368         }
369         this.assert_type_is(value[0], type.idlType.idlType);
370         return;
371     }
373     type = type.idlType;
375     switch(type)
376     {
377         case "void":
378             assert_equals(value, undefined);
379             return;
381         case "boolean":
382             assert_equals(typeof value, "boolean");
383             return;
385         case "byte":
386             assert_equals(typeof value, "number");
387             assert_equals(value, Math.floor(value), "not an integer");
388             assert_true(-128 <= value && value <= 127, "byte " + value + " not in range [-128, 127]");
389             return;
391         case "octet":
392             assert_equals(typeof value, "number");
393             assert_equals(value, Math.floor(value), "not an integer");
394             assert_true(0 <= value && value <= 255, "octet " + value + " not in range [0, 255]");
395             return;
397         case "short":
398             assert_equals(typeof value, "number");
399             assert_equals(value, Math.floor(value), "not an integer");
400             assert_true(-32768 <= value && value <= 32767, "short " + value + " not in range [-32768, 32767]");
401             return;
403         case "unsigned short":
404             assert_equals(typeof value, "number");
405             assert_equals(value, Math.floor(value), "not an integer");
406             assert_true(0 <= value && value <= 65535, "unsigned short " + value + " not in range [0, 65535]");
407             return;
409         case "long":
410             assert_equals(typeof value, "number");
411             assert_equals(value, Math.floor(value), "not an integer");
412             assert_true(-2147483648 <= value && value <= 2147483647, "long " + value + " not in range [-2147483648, 2147483647]");
413             return;
415         case "unsigned long":
416             assert_equals(typeof value, "number");
417             assert_equals(value, Math.floor(value), "not an integer");
418             assert_true(0 <= value && value <= 4294967295, "unsigned long " + value + " not in range [0, 4294967295]");
419             return;
421         case "long long":
422             assert_equals(typeof value, "number");
423             return;
425         case "unsigned long long":
426         case "DOMTimeStamp":
427             assert_equals(typeof value, "number");
428             assert_true(0 <= value, "unsigned long long is negative");
429             return;
431         case "float":
432         case "double":
433         case "DOMHighResTimeStamp":
434         case "unrestricted float":
435         case "unrestricted double":
436             // TODO: distinguish these cases
437             assert_equals(typeof value, "number");
438             return;
440         case "DOMString":
441         case "ByteString":
442         case "USVString":
443             // TODO: https://github.com/w3c/testharness.js/issues/92
444             assert_equals(typeof value, "string");
445             return;
447         case "object":
448             assert_true(typeof value == "object" || typeof value == "function", "wrong type: not object or function");
449             return;
450     }
452     if (!(type in this.members))
453     {
454         throw "Unrecognized type " + type;
455     }
457     if (this.members[type] instanceof IdlInterface)
458     {
459         // We don't want to run the full
460         // IdlInterface.prototype.test_instance_of, because that could result
461         // in an infinite loop.  TODO: This means we don't have tests for
462         // NoInterfaceObject interfaces, and we also can't test objects that
463         // come from another self.
464         assert_true(typeof value == "object" || typeof value == "function", "wrong type: not object or function");
465         if (value instanceof Object
466         && !this.members[type].has_extended_attribute("NoInterfaceObject")
467         && type in self)
468         {
469             assert_true(value instanceof self[type], "not instanceof " + type);
470         }
471     }
472     else if (this.members[type] instanceof IdlEnum)
473     {
474         assert_equals(typeof value, "string");
475     }
476     else if (this.members[type] instanceof IdlDictionary)
477     {
478         // TODO: Test when we actually have something to test this on
479     }
480     else if (this.members[type] instanceof IdlTypedef)
481     {
482         // TODO: Test when we actually have something to test this on
483     }
484     else
485     {
486         throw "Type " + type + " isn't an interface or dictionary";
487     }
489 //@}
491 /// IdlObject ///
492 function IdlObject() {}
493 IdlObject.prototype.test = function()
494 //@{
496     /**
497      * By default, this does nothing, so no actual tests are run for IdlObjects
498      * that don't define any (e.g., IdlDictionary at the time of this writing).
499      */
502 //@}
503 IdlObject.prototype.has_extended_attribute = function(name)
504 //@{
506     /**
507      * This is only meaningful for things that support extended attributes,
508      * such as interfaces, exceptions, and members.
509      */
510     return this.extAttrs.some(function(o)
511     {
512         return o.name == name;
513     });
516 //@}
518 /// IdlDictionary ///
519 // Used for IdlArray.prototype.assert_type_is
520 function IdlDictionary(obj)
521 //@{
523     /**
524      * obj is an object produced by the WebIDLParser.js "dictionary"
525      * production.
526      */
528     /** Self-explanatory. */
529     this.name = obj.name;
531     /** An array of objects produced by the "dictionaryMember" production. */
532     this.members = obj.members;
534     /**
535      * The name (as a string) of the dictionary type we inherit from, or null
536      * if there is none.
537      */
538     this.base = obj.inheritance;
541 //@}
542 IdlDictionary.prototype = Object.create(IdlObject.prototype);
544 /// IdlInterface ///
545 function IdlInterface(obj, is_callback) {
546     /**
547      * obj is an object produced by the WebIDLParser.js "exception" or
548      * "interface" production, as appropriate.
549      */
551     /** Self-explanatory. */
552     this.name = obj.name;
554     /** A back-reference to our IdlArray. */
555     this.array = obj.array;
557     /**
558      * An indicator of whether we should run tests on the (exception) interface
559      * object and (exception) interface prototype object.  Tests on members are
560      * controlled by .untested on each member, not this.
561      */
562     this.untested = obj.untested;
564     /** An array of objects produced by the "ExtAttr" production. */
565     this.extAttrs = obj.extAttrs;
567     /** An array of IdlInterfaceMembers. */
568     this.members = obj.members.map(function(m){return new IdlInterfaceMember(m); });
569     if (this.has_extended_attribute("Unforgeable")) {
570         this.members
571             .filter(function(m) { return !m["static"] && (m.type == "attribute" || m.type == "operation"); })
572             .forEach(function(m) { return m.isUnforgeable = true; });
573     }
575     /**
576      * The name (as a string) of the type we inherit from, or null if there is
577      * none.
578      */
579     this.base = obj.inheritance;
581     this._is_callback = is_callback;
583 IdlInterface.prototype = Object.create(IdlObject.prototype);
584 IdlInterface.prototype.is_callback = function()
585 //@{
587     return this._is_callback;
589 //@}
591 IdlInterface.prototype.has_constants = function()
592 //@{
594     return this.members.some(function(member) {
595         return member.type === "const";
596     });
598 //@}
600 IdlInterface.prototype.is_global = function()
601 //@{
603     return this.extAttrs.some(function(attribute) {
604         return attribute.name === "Global" ||
605                attribute.name === "PrimaryGlobal";
606     });
608 //@}
610 IdlInterface.prototype.test = function()
611 //@{
613     if (this.has_extended_attribute("NoInterfaceObject"))
614     {
615         // No tests to do without an instance.  TODO: We should still be able
616         // to run tests on the prototype object, if we obtain one through some
617         // other means.
618         return;
619     }
621     if (!this.untested)
622     {
623         // First test things to do with the exception/interface object and
624         // exception/interface prototype object.
625         this.test_self();
626     }
627     // Then test things to do with its members (constants, fields, attributes,
628     // operations, . . .).  These are run even if .untested is true, because
629     // members might themselves be marked as .untested.  This might happen to
630     // interfaces if the interface itself is untested but a partial interface
631     // that extends it is tested -- then the interface itself and its initial
632     // members will be marked as untested, but the members added by the partial
633     // interface are still tested.
634     this.test_members();
636 //@}
638 IdlInterface.prototype.test_self = function()
639 //@{
641     test(function()
642     {
643         // This function tests WebIDL as of 2015-01-13.
644         // TODO: Consider [Exposed].
646         // "For every interface that is exposed in a given ECMAScript global
647         // environment and:
648         // * is a callback interface that has constants declared on it, or
649         // * is a non-callback interface that is not declared with the
650         //   [NoInterfaceObject] extended attribute,
651         // a corresponding property MUST exist on the ECMAScript global object.
652         // The name of the property is the identifier of the interface, and its
653         // value is an object called the interface object.
654         // The property has the attributes { [[Writable]]: true,
655         // [[Enumerable]]: false, [[Configurable]]: true }."
656         if (this.is_callback() && !this.has_constants()) {
657             return;
658         }
660         // TODO: Should we test here that the property is actually writable
661         // etc., or trust getOwnPropertyDescriptor?
662         assert_own_property(self, this.name,
663                             "self does not have own property " + format_value(this.name));
664         var desc = Object.getOwnPropertyDescriptor(self, this.name);
665         assert_false("get" in desc, "self's property " + format_value(this.name) + " has getter");
666         assert_false("set" in desc, "self's property " + format_value(this.name) + " has setter");
667         assert_true(desc.writable, "self's property " + format_value(this.name) + " is not writable");
668         assert_false(desc.enumerable, "self's property " + format_value(this.name) + " is enumerable");
669         assert_true(desc.configurable, "self's property " + format_value(this.name) + " is not configurable");
671         if (this.is_callback()) {
672             // "The internal [[Prototype]] property of an interface object for
673             // a callback interface MUST be the Object.prototype object."
674             assert_equals(Object.getPrototypeOf(self[this.name]), Object.prototype,
675                           "prototype of self's property " + format_value(this.name) + " is not Object.prototype");
677             return;
678         }
680         // "The interface object for a given non-callback interface is a
681         // function object."
682         // "If an object is defined to be a function object, then it has
683         // characteristics as follows:"
685         // Its [[Prototype]] internal property is otherwise specified (see
686         // below).
688         // "* Its [[Get]] internal property is set as described in ECMA-262
689         //    section 9.1.8."
690         // Not much to test for this.
692         // "* Its [[Construct]] internal property is set as described in
693         //    ECMA-262 section 19.2.2.3."
694         // Tested below if no constructor is defined.  TODO: test constructors
695         // if defined.
697         // "* Its @@hasInstance property is set as described in ECMA-262
698         //    section 19.2.3.8, unless otherwise specified."
699         // TODO
701         // ES6 (rev 30) 19.1.3.6:
702         // "Else, if O has a [[Call]] internal method, then let builtinTag be
703         // "Function"."
704         assert_class_string(self[this.name], "Function", "class string of " + this.name);
706         // "The [[Prototype]] internal property of an interface object for a
707         // non-callback interface is determined as follows:"
708         var prototype = Object.getPrototypeOf(self[this.name]);
709         if (this.base) {
710             // "* If the interface inherits from some other interface, the
711             //    value of [[Prototype]] is the interface object for that other
712             //    interface."
713             var has_interface_object =
714                 !this.array
715                      .members[this.base]
716                      .has_extended_attribute("NoInterfaceObject");
717             if (has_interface_object) {
718                 assert_own_property(self, this.base,
719                                     'should inherit from ' + this.base +
720                                     ', but self has no such property');
721                 assert_equals(prototype, self[this.base],
722                               'prototype of ' + this.name + ' is not ' +
723                               this.base);
724             }
725         } else {
726             // "If the interface doesn't inherit from any other interface, the
727             // value of [[Prototype]] is %FunctionPrototype% ([ECMA-262],
728             // section 6.1.7.4)."
729             assert_equals(prototype, Function.prototype,
730                           "prototype of self's property " + format_value(this.name) + " is not Function.prototype");
731         }
733         if (!this.has_extended_attribute("Constructor")) {
734             // "The internal [[Call]] method of the interface object behaves as
735             // follows . . .
736             //
737             // "If I was not declared with a [Constructor] extended attribute,
738             // then throw a TypeError."
739             assert_throws(new TypeError(), function() {
740                 self[this.name]();
741             }.bind(this), "interface object didn't throw TypeError when called as a function");
742             assert_throws(new TypeError(), function() {
743                 new self[this.name]();
744             }.bind(this), "interface object didn't throw TypeError when called as a constructor");
745         }
746     }.bind(this), this.name + " interface: existence and properties of interface object");
748     if (!this.is_callback()) {
749         test(function() {
750             // This function tests WebIDL as of 2014-10-25.
751             // https://heycam.github.io/webidl/#es-interface-call
753             assert_own_property(self, this.name,
754                                 "self does not have own property " + format_value(this.name));
756             // "Interface objects for non-callback interfaces MUST have a
757             // property named “length” with attributes { [[Writable]]: false,
758             // [[Enumerable]]: false, [[Configurable]]: true } whose value is
759             // a Number."
760             assert_own_property(self[this.name], "length");
761             var desc = Object.getOwnPropertyDescriptor(self[this.name], "length");
762             assert_false("get" in desc, this.name + ".length has getter");
763             assert_false("set" in desc, this.name + ".length has setter");
764             assert_false(desc.writable, this.name + ".length is writable");
765             assert_false(desc.enumerable, this.name + ".length is enumerable");
766             assert_true(desc.configurable, this.name + ".length is not configurable");
768             var constructors = this.extAttrs
769                 .filter(function(attr) { return attr.name == "Constructor"; });
770             var expected_length = minOverloadLength(constructors);
771             assert_equals(self[this.name].length, expected_length, "wrong value for " + this.name + ".length");
772         }.bind(this), this.name + " interface object length");
773     }
775     // TODO: Test named constructors if I find any interfaces that have them.
777     test(function()
778     {
779         // This function tests WebIDL as of 2015-01-21.
780         // https://heycam.github.io/webidl/#interface-object
782         if (this.is_callback() && !this.has_constants()) {
783             return;
784         }
786         assert_own_property(self, this.name,
787                             "self does not have own property " + format_value(this.name));
789         if (this.is_callback()) {
790             assert_false("prototype" in self[this.name],
791                          this.name + ' should not have a "prototype" property');
792             return;
793         }
795         // "An interface object for a non-callback interface must have a
796         // property named “prototype” with attributes { [[Writable]]: false,
797         // [[Enumerable]]: false, [[Configurable]]: false } whose value is an
798         // object called the interface prototype object. This object has
799         // properties that correspond to the regular attributes and regular
800         // operations defined on the interface, and is described in more detail
801         // in section 4.5.4 below."
802         assert_own_property(self[this.name], "prototype",
803                             'interface "' + this.name + '" does not have own property "prototype"');
804         var desc = Object.getOwnPropertyDescriptor(self[this.name], "prototype");
805         assert_false("get" in desc, this.name + ".prototype has getter");
806         assert_false("set" in desc, this.name + ".prototype has setter");
807         assert_false(desc.writable, this.name + ".prototype is writable");
808         assert_false(desc.enumerable, this.name + ".prototype is enumerable");
809         assert_false(desc.configurable, this.name + ".prototype is configurable");
811         // Next, test that the [[Prototype]] of the interface prototype object
812         // is correct. (This is made somewhat difficult by the existence of
813         // [NoInterfaceObject].)
814         // TODO: Aryeh thinks there's at least other place in this file where
815         //       we try to figure out if an interface prototype object is
816         //       correct. Consolidate that code.
818         // "The interface prototype object for a given interface A must have an
819         // internal [[Prototype]] property whose value is returned from the
820         // following steps:
821         // "If A is declared with the [Global] or [PrimaryGlobal] extended
822         // attribute, and A supports named properties, then return the named
823         // properties object for A, as defined in section 4.5.5 below.
824         // "Otherwise, if A is declared to inherit from another interface, then
825         // return the interface prototype object for the inherited interface.
826         // "Otherwise, if A is declared with the [ArrayClass] extended
827         // attribute, then return %ArrayPrototype% ([ECMA-262], section
828         // 6.1.7.4).
829         // "Otherwise, return %ObjectPrototype% ([ECMA-262], section 6.1.7.4).
830         // ([ECMA-262], section 15.2.4).
831         if (this.name === "Window") {
832             assert_class_string(Object.getPrototypeOf(self[this.name].prototype),
833                                 'WindowProperties',
834                                 'Class name for prototype of Window' +
835                                 '.prototype is not "WindowProperties"');
836         } else {
837             var inherit_interface, inherit_interface_has_interface_object;
838             if (this.base) {
839                 inherit_interface = this.base;
840                 inherit_interface_has_interface_object =
841                     !this.array
842                          .members[inherit_interface]
843                          .has_extended_attribute("NoInterfaceObject");
844             } else if (this.has_extended_attribute('ArrayClass')) {
845                 inherit_interface = 'Array';
846                 inherit_interface_has_interface_object = true;
847             } else {
848                 inherit_interface = 'Object';
849                 inherit_interface_has_interface_object = true;
850             }
851             if (inherit_interface_has_interface_object) {
852                 assert_own_property(self, inherit_interface,
853                                     'should inherit from ' + inherit_interface + ', but self has no such property');
854                 assert_own_property(self[inherit_interface], 'prototype',
855                                     'should inherit from ' + inherit_interface + ', but that object has no "prototype" property');
856                 assert_equals(Object.getPrototypeOf(self[this.name].prototype),
857                               self[inherit_interface].prototype,
858                               'prototype of ' + this.name + '.prototype is not ' + inherit_interface + '.prototype');
859             } else {
860                 // We can't test that we get the correct object, because this is the
861                 // only way to get our hands on it. We only test that its class
862                 // string, at least, is correct.
863                 assert_class_string(Object.getPrototypeOf(self[this.name].prototype),
864                                     inherit_interface + 'Prototype',
865                                     'Class name for prototype of ' + this.name +
866                                     '.prototype is not "' + inherit_interface + 'Prototype"');
867             }
868         }
870         // "The class string of an interface prototype object is the
871         // concatenation of the interface’s identifier and the string
872         // “Prototype”."
873         assert_class_string(self[this.name].prototype, this.name + "Prototype",
874                             "class string of " + this.name + ".prototype");
875         // String() should end up calling {}.toString if nothing defines a
876         // stringifier.
877         if (!this.has_stringifier()) {
878             assert_equals(String(self[this.name].prototype), "[object " + this.name + "Prototype]",
879                     "String(" + this.name + ".prototype)");
880         }
881     }.bind(this), this.name + " interface: existence and properties of interface prototype object");
883     test(function()
884     {
885         if (this.is_callback() && !this.has_constants()) {
886             return;
887         }
889         assert_own_property(self, this.name,
890                             "self does not have own property " + format_value(this.name));
892         if (this.is_callback()) {
893             assert_false("prototype" in self[this.name],
894                          this.name + ' should not have a "prototype" property');
895             return;
896         }
898         assert_own_property(self[this.name], "prototype",
899                             'interface "' + this.name + '" does not have own property "prototype"');
901         // "If the [NoInterfaceObject] extended attribute was not specified on
902         // the interface, then the interface prototype object must also have a
903         // property named “constructor” with attributes { [[Writable]]: true,
904         // [[Enumerable]]: false, [[Configurable]]: true } whose value is a
905         // reference to the interface object for the interface."
906         assert_own_property(self[this.name].prototype, "constructor",
907                             this.name + '.prototype does not have own property "constructor"');
908         var desc = Object.getOwnPropertyDescriptor(self[this.name].prototype, "constructor");
909         assert_false("get" in desc, this.name + ".prototype.constructor has getter");
910         assert_false("set" in desc, this.name + ".prototype.constructor has setter");
911         assert_true(desc.writable, this.name + ".prototype.constructor is not writable");
912         assert_false(desc.enumerable, this.name + ".prototype.constructor is enumerable");
913         assert_true(desc.configurable, this.name + ".prototype.constructor in not configurable");
914         assert_equals(self[this.name].prototype.constructor, self[this.name],
915                       this.name + '.prototype.constructor is not the same object as ' + this.name);
916     }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property');
919 //@}
920 IdlInterface.prototype.test_member_const = function(member)
921 //@{
923     test(function()
924     {
925         if (this.is_callback() && !this.has_constants()) {
926             return;
927         }
929         assert_own_property(self, this.name,
930                             "self does not have own property " + format_value(this.name));
932         // "For each constant defined on an interface A, there must be
933         // a corresponding property on the interface object, if it
934         // exists."
935         assert_own_property(self[this.name], member.name);
936         // "The value of the property is that which is obtained by
937         // converting the constant’s IDL value to an ECMAScript
938         // value."
939         assert_equals(self[this.name][member.name], constValue(member.value),
940                       "property has wrong value");
941         // "The property has attributes { [[Writable]]: false,
942         // [[Enumerable]]: true, [[Configurable]]: false }."
943         var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name);
944         assert_false("get" in desc, "property has getter");
945         assert_false("set" in desc, "property has setter");
946         assert_false(desc.writable, "property is writable");
947         assert_true(desc.enumerable, "property is not enumerable");
948         assert_false(desc.configurable, "property is configurable");
949     }.bind(this), this.name + " interface: constant " + member.name + " on interface object");
950     // "In addition, a property with the same characteristics must
951     // exist on the interface prototype object."
952     test(function()
953     {
954         if (this.is_callback() && !this.has_constants()) {
955             return;
956         }
958         assert_own_property(self, this.name,
959                             "self does not have own property " + format_value(this.name));
961         if (this.is_callback()) {
962             assert_false("prototype" in self[this.name],
963                          this.name + ' should not have a "prototype" property');
964             return;
965         }
967         assert_own_property(self[this.name], "prototype",
968                             'interface "' + this.name + '" does not have own property "prototype"');
970         assert_own_property(self[this.name].prototype, member.name);
971         assert_equals(self[this.name].prototype[member.name], constValue(member.value),
972                       "property has wrong value");
973         var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name);
974         assert_false("get" in desc, "property has getter");
975         assert_false("set" in desc, "property has setter");
976         assert_false(desc.writable, "property is writable");
977         assert_true(desc.enumerable, "property is not enumerable");
978         assert_false(desc.configurable, "property is configurable");
979     }.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object");
983 //@}
984 IdlInterface.prototype.test_member_attribute = function(member)
985 //@{
987     test(function()
988     {
989         if (this.is_callback() && !this.has_constants()) {
990             return;
991         }
993         assert_own_property(self, this.name,
994                             "self does not have own property " + format_value(this.name));
995         assert_own_property(self[this.name], "prototype",
996                             'interface "' + this.name + '" does not have own property "prototype"');
998         if (member["static"]) {
999             assert_own_property(self[this.name], member.name,
1000                 "The interface object must have a property " +
1001                 format_value(member.name));
1002         } else if (this.is_global()) {
1003             assert_own_property(self, member.name,
1004                 "The global object must have a property " +
1005                 format_value(member.name));
1006             assert_false(member.name in self[this.name].prototype,
1007                 "The prototype object must not have a property " +
1008                 format_value(member.name));
1010             // Try/catch around the get here, since it can legitimately throw.
1011             // If it does, we obviously can't check for equality with direct
1012             // invocation of the getter.
1013             var gotValue;
1014             var propVal;
1015             try {
1016                 propVal = self[member.name];
1017                 gotValue = true;
1018             } catch (e) {
1019                 gotValue = false;
1020             }
1021             if (gotValue) {
1022                 var getter = Object.getOwnPropertyDescriptor(self, member.name).get;
1023                 assert_equals(typeof(getter), "function",
1024                               format_value(member.name) + " must have a getter");
1025                 assert_equals(propVal, getter.call(undefined),
1026                               "Gets on a global should not require an explicit this");
1027             }
1028             this.do_interface_attribute_asserts(self, member);
1029         } else {
1030             assert_true(member.name in self[this.name].prototype,
1031                 "The prototype object must have a property " +
1032                 format_value(member.name));
1034             if (!member.has_extended_attribute("LenientThis")) {
1035                 assert_throws(new TypeError(), function() {
1036                     self[this.name].prototype[member.name];
1037                 }.bind(this), "getting property on prototype object must throw TypeError");
1038             } else {
1039                 assert_equals(self[this.name].prototype[member.name], undefined,
1040                               "getting property on prototype object must return undefined");
1041             }
1042             this.do_interface_attribute_asserts(self[this.name].prototype, member);
1043         }
1044     }.bind(this), this.name + " interface: attribute " + member.name);
1047 //@}
1048 IdlInterface.prototype.test_member_operation = function(member)
1049 //@{
1051     test(function()
1052     {
1053         if (this.is_callback() && !this.has_constants()) {
1054             return;
1055         }
1057         assert_own_property(self, this.name,
1058                             "self does not have own property " + format_value(this.name));
1060         if (this.is_callback()) {
1061             assert_false("prototype" in self[this.name],
1062                          this.name + ' should not have a "prototype" property');
1063             return;
1064         }
1066         assert_own_property(self[this.name], "prototype",
1067                             'interface "' + this.name + '" does not have own property "prototype"');
1069         // "For each unique identifier of an operation defined on the
1070         // interface, there must be a corresponding property on the
1071         // interface prototype object (if it is a regular operation) or
1072         // the interface object (if it is a static operation), unless
1073         // the effective overload set for that identifier and operation
1074         // and with an argument count of 0 (for the ECMAScript language
1075         // binding) has no entries."
1076         //
1077         var memberHolderObject;
1078         if (member["static"]) {
1079             assert_own_property(self[this.name], member.name,
1080                     "interface object missing static operation");
1081             memberHolderObject = self[this.name];
1082         } else if (this.is_global()) {
1083             assert_own_property(self, member.name,
1084                     "global object missing non-static operation");
1085             memberHolderObject = self;
1086         } else {
1087             assert_own_property(self[this.name].prototype, member.name,
1088                     "interface prototype object missing non-static operation");
1089             memberHolderObject = self[this.name].prototype;
1090         }
1092         this.do_member_operation_asserts(memberHolderObject, member);
1093     }.bind(this), this.name + " interface: operation " + member.name +
1094     "(" + member.arguments.map(function(m) { return m.idlType.idlType; }) +
1095     ")");
1098 //@}
1099 IdlInterface.prototype.do_member_operation_asserts = function(memberHolderObject, member)
1100 //@{
1102     var operationUnforgeable = member.isUnforgeable;
1103     var desc = Object.getOwnPropertyDescriptor(memberHolderObject, member.name);
1104     // "The property has attributes { [[Writable]]: B,
1105     // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the
1106     // operation is unforgeable on the interface, and true otherwise".
1107     assert_false("get" in desc, "property has getter");
1108     assert_false("set" in desc, "property has setter");
1109     assert_equals(desc.writable, !operationUnforgeable,
1110                   "property should be writable if and only if not unforgeable");
1111     assert_true(desc.enumerable, "property is not enumerable");
1112     assert_equals(desc.configurable, !operationUnforgeable,
1113                   "property should be configurable if and only if not unforgeable");
1114     // "The value of the property is a Function object whose
1115     // behavior is as follows . . ."
1116     assert_equals(typeof memberHolderObject[member.name], "function",
1117                   "property must be a function");
1118     // "The value of the Function object’s “length” property is
1119     // a Number determined as follows:
1120     // ". . .
1121     // "Return the length of the shortest argument list of the
1122     // entries in S."
1123     assert_equals(memberHolderObject[member.name].length,
1124         minOverloadLength(this.members.filter(function(m) {
1125             return m.type == "operation" && m.name == member.name;
1126         })),
1127         "property has wrong .length");
1129     // Make some suitable arguments
1130     var args = member.arguments.map(function(arg) {
1131         return create_suitable_object(arg.idlType);
1132     });
1134     // "Let O be a value determined as follows:
1135     // ". . .
1136     // "Otherwise, throw a TypeError."
1137     // This should be hit if the operation is not static, there is
1138     // no [ImplicitThis] attribute, and the this value is null.
1139     //
1140     // TODO: We currently ignore the [ImplicitThis] case.  Except we manually
1141     // check for globals, since otherwise we'll invoke window.close().  And we
1142     // have to skip this test for anything that on the proto chain of "self",
1143     // since that does in fact have implicit-this behavior.
1144     if (!member["static"]) {
1145         if (!this.is_global() &&
1146             memberHolderObject[member.name] != self[member.name])
1147         {
1148             assert_throws(new TypeError(), function() {
1149                 memberHolderObject[member.name].apply(null, args);
1150             }, "calling operation with this = null didn't throw TypeError");
1151         }
1153         // ". . . If O is not null and is also not a platform object
1154         // that implements interface I, throw a TypeError."
1155         //
1156         // TODO: Test a platform object that implements some other
1157         // interface.  (Have to be sure to get inheritance right.)
1158         assert_throws(new TypeError(), function() {
1159             memberHolderObject[member.name].apply({}, args);
1160         }, "calling operation with this = {} didn't throw TypeError");
1161     }
1164 //@}
1165 IdlInterface.prototype.test_member_stringifier = function(member)
1166 //@{
1168     test(function()
1169     {
1170         if (this.is_callback() && !this.has_constants()) {
1171             return;
1172         }
1174         assert_own_property(self, this.name,
1175                             "self does not have own property " + format_value(this.name));
1177         if (this.is_callback()) {
1178             assert_false("prototype" in self[this.name],
1179                          this.name + ' should not have a "prototype" property');
1180             return;
1181         }
1183         assert_own_property(self[this.name], "prototype",
1184                             'interface "' + this.name + '" does not have own property "prototype"');
1186         // ". . . the property exists on the interface prototype object."
1187         var interfacePrototypeObject = self[this.name].prototype;
1188         assert_own_property(self[this.name].prototype, "toString",
1189                 "interface prototype object missing non-static operation");
1191         var stringifierUnforgeable = member.isUnforgeable;
1192         var desc = Object.getOwnPropertyDescriptor(interfacePrototypeObject, "toString");
1193         // "The property has attributes { [[Writable]]: B,
1194         // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the
1195         // stringifier is unforgeable on the interface, and true otherwise."
1196         assert_false("get" in desc, "property has getter");
1197         assert_false("set" in desc, "property has setter");
1198         assert_equals(desc.writable, !stringifierUnforgeable,
1199                       "property should be writable if and only if not unforgeable");
1200         assert_true(desc.enumerable, "property is not enumerable");
1201         assert_equals(desc.configurable, !stringifierUnforgeable,
1202                       "property should be configurable if and only if not unforgeable");
1203         // "The value of the property is a Function object, which behaves as
1204         // follows . . ."
1205         assert_equals(typeof interfacePrototypeObject.toString, "function",
1206                       "property must be a function");
1207         // "The value of the Function object’s “length” property is the Number
1208         // value 0."
1209         assert_equals(interfacePrototypeObject.toString.length, 0,
1210             "property has wrong .length");
1212         // "Let O be the result of calling ToObject on the this value."
1213         assert_throws(new TypeError(), function() {
1214             self[this.name].prototype.toString.apply(null, []);
1215         }, "calling stringifier with this = null didn't throw TypeError");
1217         // "If O is not an object that implements the interface on which the
1218         // stringifier was declared, then throw a TypeError."
1219         //
1220         // TODO: Test a platform object that implements some other
1221         // interface.  (Have to be sure to get inheritance right.)
1222         assert_throws(new TypeError(), function() {
1223             self[this.name].prototype.toString.apply({}, []);
1224         }, "calling stringifier with this = {} didn't throw TypeError");
1225     }.bind(this), this.name + " interface: stringifier");
1228 //@}
1229 IdlInterface.prototype.test_members = function()
1230 //@{
1232     for (var i = 0; i < this.members.length; i++)
1233     {
1234         var member = this.members[i];
1235         if (member.untested) {
1236             continue;
1237         }
1239         switch (member.type) {
1240         case "const":
1241             this.test_member_const(member);
1242             break;
1244         case "attribute":
1245             // For unforgeable attributes, we do the checks in
1246             // test_interface_of instead.
1247             if (!member.isUnforgeable)
1248             {
1249                 this.test_member_attribute(member);
1250             }
1251             break;
1253         case "operation":
1254             // TODO: Need to correctly handle multiple operations with the same
1255             // identifier.
1256             // For unforgeable operations, we do the checks in
1257             // test_interface_of instead.
1258             if (member.name) {
1259                 if (!member.isUnforgeable)
1260                 {
1261                     this.test_member_operation(member);
1262                 }
1263             } else if (member.stringifier) {
1264                 this.test_member_stringifier(member);
1265             }
1266             break;
1268         default:
1269             // TODO: check more member types.
1270             break;
1271         }
1272     }
1275 //@}
1276 IdlInterface.prototype.test_object = function(desc)
1277 //@{
1279     var obj, exception = null;
1280     try
1281     {
1282         obj = eval(desc);
1283     }
1284     catch(e)
1285     {
1286         exception = e;
1287     }
1289     // TODO: WebIDLParser doesn't currently support named legacycallers, so I'm
1290     // not sure what those would look like in the AST
1291     var expected_typeof = this.members.some(function(member)
1292     {
1293         return member.legacycaller
1294             || ("idlType" in member && member.idlType.legacycaller)
1295             || ("idlType" in member && typeof member.idlType == "object"
1296             && "idlType" in member.idlType && member.idlType.idlType == "legacycaller");
1297     }) ? "function" : "object";
1299     this.test_primary_interface_of(desc, obj, exception, expected_typeof);
1300     var current_interface = this;
1301     while (current_interface)
1302     {
1303         if (!(current_interface.name in this.array.members))
1304         {
1305             throw "Interface " + current_interface.name + " not found (inherited by " + this.name + ")";
1306         }
1307         if (current_interface.prevent_multiple_testing && current_interface.already_tested)
1308         {
1309             return;
1310         }
1311         current_interface.test_interface_of(desc, obj, exception, expected_typeof);
1312         current_interface = this.array.members[current_interface.base];
1313     }
1316 //@}
1317 IdlInterface.prototype.test_primary_interface_of = function(desc, obj, exception, expected_typeof)
1318 //@{
1320     // We can't easily test that its prototype is correct if there's no
1321     // interface object, or the object is from a different global environment
1322     // (not instanceof Object).  TODO: test in this case that its prototype at
1323     // least looks correct, even if we can't test that it's actually correct.
1324     if (!this.has_extended_attribute("NoInterfaceObject")
1325     && (typeof obj != expected_typeof || obj instanceof Object))
1326     {
1327         test(function()
1328         {
1329             assert_equals(exception, null, "Unexpected exception when evaluating object");
1330             assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1331             assert_own_property(self, this.name,
1332                                 "self does not have own property " + format_value(this.name));
1333             assert_own_property(self[this.name], "prototype",
1334                                 'interface "' + this.name + '" does not have own property "prototype"');
1336             // "The value of the internal [[Prototype]] property of the
1337             // platform object is the interface prototype object of the primary
1338             // interface from the platform object’s associated global
1339             // environment."
1340             assert_equals(Object.getPrototypeOf(obj),
1341                           self[this.name].prototype,
1342                           desc + "'s prototype is not " + this.name + ".prototype");
1343         }.bind(this), this.name + " must be primary interface of " + desc);
1344     }
1346     // "The class string of a platform object that implements one or more
1347     // interfaces must be the identifier of the primary interface of the
1348     // platform object."
1349     test(function()
1350     {
1351         assert_equals(exception, null, "Unexpected exception when evaluating object");
1352         assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1353         assert_class_string(obj, this.name, "class string of " + desc);
1354         if (!this.has_stringifier())
1355         {
1356             assert_equals(String(obj), "[object " + this.name + "]", "String(" + desc + ")");
1357         }
1358     }.bind(this), "Stringification of " + desc);
1361 //@}
1362 IdlInterface.prototype.test_interface_of = function(desc, obj, exception, expected_typeof)
1363 //@{
1365     // TODO: Indexed and named properties, more checks on interface members
1366     this.already_tested = true;
1368     for (var i = 0; i < this.members.length; i++)
1369     {
1370         var member = this.members[i];
1371         if (member.type == "attribute" && member.isUnforgeable)
1372         {
1373             test(function()
1374             {
1375                 assert_equals(exception, null, "Unexpected exception when evaluating object");
1376                 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1377                 this.do_interface_attribute_asserts(obj, member);
1378             }.bind(this), this.name + " interface: " + desc + ' must have own property "' + member.name + '"');
1379         }
1380         else if (member.type == "operation" &&
1381                  member.name &&
1382                  member.isUnforgeable)
1383         {
1384             test(function()
1385             {
1386                 assert_equals(exception, null, "Unexpected exception when evaluating object");
1387                 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1388                 assert_own_property(obj, member.name,
1389                                     "Doesn't have the unforgeable operation property");
1390                 this.do_member_operation_asserts(obj, member);
1391             }.bind(this), this.name + " interface: " + desc + ' must have own property "' + member.name + '"');
1392         }
1393         else if ((member.type == "const"
1394         || member.type == "attribute"
1395         || member.type == "operation")
1396         && member.name)
1397         {
1398             test(function()
1399             {
1400                 assert_equals(exception, null, "Unexpected exception when evaluating object");
1401                 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1402                 if (!member["static"]) {
1403                     if (!this.is_global()) {
1404                         assert_inherits(obj, member.name);
1405                     } else {
1406                         assert_own_property(obj, member.name);
1407                     }
1409                     if (member.type == "const")
1410                     {
1411                         assert_equals(obj[member.name], constValue(member.value));
1412                     }
1413                     if (member.type == "attribute")
1414                     {
1415                         // Attributes are accessor properties, so they might
1416                         // legitimately throw an exception rather than returning
1417                         // anything.
1418                         var property, thrown = false;
1419                         try
1420                         {
1421                             property = obj[member.name];
1422                         }
1423                         catch (e)
1424                         {
1425                             thrown = true;
1426                         }
1427                         if (!thrown)
1428                         {
1429                             this.array.assert_type_is(property, member.idlType);
1430                         }
1431                     }
1432                     if (member.type == "operation")
1433                     {
1434                         assert_equals(typeof obj[member.name], "function");
1435                     }
1436                 }
1437             }.bind(this), this.name + " interface: " + desc + ' must inherit property "' + member.name + '" with the proper type (' + i + ')');
1438         }
1439         // TODO: This is wrong if there are multiple operations with the same
1440         // identifier.
1441         // TODO: Test passing arguments of the wrong type.
1442         if (member.type == "operation" && member.name && member.arguments.length)
1443         {
1444             test(function()
1445             {
1446                 assert_equals(exception, null, "Unexpected exception when evaluating object");
1447                 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1448                 if (!member["static"]) {
1449                     if (!this.is_global() && !member.isUnforgeable) {
1450                         assert_inherits(obj, member.name);
1451                     } else {
1452                         assert_own_property(obj, member.name);
1453                     }
1454                 }
1455                 else
1456                 {
1457                     assert_false(member.name in obj);
1458                 }
1460                 var minLength = minOverloadLength(this.members.filter(function(m) {
1461                     return m.type == "operation" && m.name == member.name;
1462                 }));
1463                 var args = [];
1464                 for (var i = 0; i < minLength; i++) {
1465                     assert_throws(new TypeError(), function()
1466                     {
1467                         obj[member.name].apply(obj, args);
1468                     }.bind(this), "Called with " + i + " arguments");
1470                     args.push(create_suitable_object(member.arguments[i].idlType));
1471                 }
1472             }.bind(this), this.name + " interface: calling " + member.name +
1473             "(" + member.arguments.map(function(m) { return m.idlType.idlType; }) +
1474             ") on " + desc + " with too few arguments must throw TypeError");
1475         }
1476     }
1479 //@}
1480 IdlInterface.prototype.has_stringifier = function()
1481 //@{
1483     if (this.members.some(function(member) { return member.stringifier; })) {
1484         return true;
1485     }
1486     if (this.base &&
1487         this.array.members[this.base].has_stringifier()) {
1488         return true;
1489     }
1490     return false;
1493 //@}
1494 IdlInterface.prototype.do_interface_attribute_asserts = function(obj, member)
1495 //@{
1497     // This function tests WebIDL as of 2015-01-27.
1498     // TODO: Consider [Exposed].
1500     // This is called by test_member_attribute() with the prototype as obj if
1501     // it is not a global, and the global otherwise, and by test_interface_of()
1502     // with the object as obj.
1504     // "For each exposed attribute of the interface, whether it was declared on
1505     // the interface itself or one of its consequential interfaces, there MUST
1506     // exist a corresponding property. The characteristics of this property are
1507     // as follows:"
1509     // "The name of the property is the identifier of the attribute."
1510     assert_own_property(obj, member.name);
1512     // "The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
1513     // true, [[Configurable]]: configurable }, where:
1514     // "configurable is false if the attribute was declared with the
1515     // [Unforgeable] extended attribute and true otherwise;
1516     // "G is the attribute getter, defined below; and
1517     // "S is the attribute setter, also defined below."
1518     var desc = Object.getOwnPropertyDescriptor(obj, member.name);
1519     assert_false("value" in desc, 'property descriptor has value but is supposed to be accessor');
1520     assert_false("writable" in desc, 'property descriptor has "writable" field but is supposed to be accessor');
1521     assert_true(desc.enumerable, "property is not enumerable");
1522     if (member.isUnforgeable)
1523     {
1524         assert_false(desc.configurable, "[Unforgeable] property must not be configurable");
1525     }
1526     else
1527     {
1528         assert_true(desc.configurable, "property must be configurable");
1529     }
1532     // "The attribute getter is a Function object whose behavior when invoked
1533     // is as follows:"
1534     assert_equals(typeof desc.get, "function", "getter must be Function");
1536     // "If the attribute is a regular attribute, then:"
1537     if (!member["static"]) {
1538         // "If O is not a platform object that implements I, then:
1539         // "If the attribute was specified with the [LenientThis] extended
1540         // attribute, then return undefined.
1541         // "Otherwise, throw a TypeError."
1542         if (!member.has_extended_attribute("LenientThis")) {
1543             assert_throws(new TypeError(), function() {
1544                 desc.get.call({});
1545             }.bind(this), "calling getter on wrong object type must throw TypeError");
1546         } else {
1547             assert_equals(desc.get.call({}), undefined,
1548                           "calling getter on wrong object type must return undefined");
1549         }
1550     }
1552     // "The value of the Function object’s “length” property is the Number
1553     // value 0."
1554     assert_equals(desc.get.length, 0, "getter length must be 0");
1557     // TODO: Test calling setter on the interface prototype (should throw
1558     // TypeError in most cases).
1559     if (member.readonly
1560     && !member.has_extended_attribute("PutForwards")
1561     && !member.has_extended_attribute("Replaceable"))
1562     {
1563         // "The attribute setter is undefined if the attribute is declared
1564         // readonly and has neither a [PutForwards] nor a [Replaceable]
1565         // extended attribute declared on it."
1566         assert_equals(desc.set, undefined, "setter must be undefined for readonly attributes");
1567     }
1568     else
1569     {
1570         // "Otherwise, it is a Function object whose behavior when
1571         // invoked is as follows:"
1572         assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes");
1574         // "If the attribute is a regular attribute, then:"
1575         if (!member["static"]) {
1576             // "If /validThis/ is false and the attribute was not specified
1577             // with the [LenientThis] extended attribute, then throw a
1578             // TypeError."
1579             // "If the attribute is declared with a [Replaceable] extended
1580             // attribute, then: ..."
1581             // "If validThis is false, then return."
1582             if (!member.has_extended_attribute("LenientThis")) {
1583                 assert_throws(new TypeError(), function() {
1584                     desc.set.call({});
1585                 }.bind(this), "calling setter on wrong object type must throw TypeError");
1586             } else {
1587                 assert_equals(desc.set.call({}), undefined,
1588                               "calling setter on wrong object type must return undefined");
1589             }
1590         }
1592         // "The value of the Function object’s “length” property is the Number
1593         // value 1."
1594         assert_equals(desc.set.length, 1, "setter length must be 1");
1595     }
1597 //@}
1599 /// IdlInterfaceMember ///
1600 function IdlInterfaceMember(obj)
1601 //@{
1603     /**
1604      * obj is an object produced by the WebIDLParser.js "ifMember" production.
1605      * We just forward all properties to this object without modification,
1606      * except for special extAttrs handling.
1607      */
1608     for (var k in obj)
1609     {
1610         this[k] = obj[k];
1611     }
1612     if (!("extAttrs" in this))
1613     {
1614         this.extAttrs = [];
1615     }
1617     this.isUnforgeable = this.has_extended_attribute("Unforgeable");
1620 //@}
1621 IdlInterfaceMember.prototype = Object.create(IdlObject.prototype);
1623 /// Internal helper functions ///
1624 function create_suitable_object(type)
1625 //@{
1627     /**
1628      * type is an object produced by the WebIDLParser.js "type" production.  We
1629      * return a JavaScript value that matches the type, if we can figure out
1630      * how.
1631      */
1632     if (type.nullable)
1633     {
1634         return null;
1635     }
1636     switch (type.idlType)
1637     {
1638         case "any":
1639         case "boolean":
1640             return true;
1642         case "byte": case "octet": case "short": case "unsigned short":
1643         case "long": case "unsigned long": case "long long":
1644         case "unsigned long long": case "float": case "double":
1645         case "unrestricted float": case "unrestricted double":
1646             return 7;
1648         case "DOMString":
1649         case "ByteString":
1650         case "USVString":
1651             return "foo";
1653         case "object":
1654             return {a: "b"};
1656         case "Node":
1657             return document.createTextNode("abc");
1658     }
1659     return null;
1661 //@}
1663 /// IdlEnum ///
1664 // Used for IdlArray.prototype.assert_type_is
1665 function IdlEnum(obj)
1666 //@{
1668     /**
1669      * obj is an object produced by the WebIDLParser.js "dictionary"
1670      * production.
1671      */
1673     /** Self-explanatory. */
1674     this.name = obj.name;
1676     /** An array of values produced by the "enum" production. */
1677     this.values = obj.values;
1680 //@}
1682 IdlEnum.prototype = Object.create(IdlObject.prototype);
1684 /// IdlTypedef ///
1685 // Used for IdlArray.prototype.assert_type_is
1686 function IdlTypedef(obj)
1687 //@{
1689     /**
1690      * obj is an object produced by the WebIDLParser.js "typedef"
1691      * production.
1692      */
1694     /** Self-explanatory. */
1695     this.name = obj.name;
1697     /** An array of values produced by the "typedef" production. */
1698     this.values = obj.values;
1701 //@}
1703 IdlTypedef.prototype = Object.create(IdlObject.prototype);
1705 }());
1706 // vim: set expandtab shiftwidth=4 tabstop=4 foldmarker=@{,@} foldmethod=marker: