Move helper files for iOS touch gestures into LayoutTests/resources
[WebKit-https.git] / LayoutTests / resources / idlharness.js
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].
5
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 */
10
11 /* For user documentation see docs/idlharness.md */
12
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 //@{
54 {
55     if (cnt.type === "null") return null;
56     if (cnt.type === "NaN") return NaN;
57     if (cnt.type === "Infinity") return cnt.negative ? -Infinity : Infinity;
58     return cnt.value;
59 }
60
61 //@}
62 function minOverloadLength(overloads)
63 //@{
64 {
65     if (!overloads.length) {
66         return 0;
67     }
68
69     return overloads.map(function(attr) {
70         return attr.arguments ? attr.arguments.filter(function(arg) {
71             return !arg.optional && !arg.variadic;
72         }).length : 0;
73     })
74     .reduce(function(m, n) { return Math.min(m, n); });
75 }
76
77 //@}
78 function throwOrReject(a_test, operation, fn, obj, args,  message, cb)
79 //@{
80 {
81     if (operation.idlType.generic !== "Promise") {
82         assert_throws(new TypeError(), function() {
83             fn.apply(obj, args);
84         }, message);
85         cb();
86     } else {
87         try {
88             promise_rejects(a_test, new TypeError(), fn.apply(obj, args)).then(cb, cb);
89         } catch (e){
90             a_test.step(function() {
91                 assert_unreached("Throws \"" + e + "\" instead of rejecting promise");
92                 cb();
93             });
94         }
95     }
96 }
97
98 //@}
99 function awaitNCallbacks(n, cb, ctx)
100 //@{
101 {
102     var counter = 0;
103     return function() {
104         counter++;
105         if (counter >= n) {
106             cb();
107         }
108     };
109 }
110
111 //@}
112 var fround =
113 //@{
114 (function(){
115     if (Math.fround) return Math.fround;
116
117     var arr = new Float32Array(1);
118     return function fround(n) {
119         arr[0] = n;
120         return arr[0];
121     };
122 })();
123 //@}
124
125 /// IdlArray ///
126 // Entry point
127 self.IdlArray = function()
128 //@{
129 {
130     /**
131      * A map from strings to the corresponding named IdlObject, such as
132      * IdlInterface or IdlException.  These are the things that test() will run
133      * tests on.
134      */
135     this.members = {};
136
137     /**
138      * A map from strings to arrays of strings.  The keys are interface or
139      * exception names, and are expected to also exist as keys in this.members
140      * (otherwise they'll be ignored).  This is populated by add_objects() --
141      * see documentation at the start of the file.  The actual tests will be
142      * run by calling this.members[name].test_object(obj) for each obj in
143      * this.objects[name].  obj is a string that will be eval'd to produce a
144      * JavaScript value, which is supposed to be an object implementing the
145      * given IdlObject (interface, exception, etc.).
146      */
147     this.objects = {};
148
149     /**
150      * When adding multiple collections of IDLs one at a time, an earlier one
151      * might contain a partial interface or implements statement that depends
152      * on a later one.  Save these up and handle them right before we run
153      * tests.
154      *
155      * .partials is simply an array of objects from WebIDLParser.js'
156      * "partialinterface" production.  .implements maps strings to arrays of
157      * strings, such that
158      *
159      *   A implements B;
160      *   A implements C;
161      *   D implements E;
162      *
163      * results in { A: ["B", "C"], D: ["E"] }.
164      */
165     this.partials = [];
166     this["implements"] = {};
167 };
168
169 //@}
170 IdlArray.prototype.add_idls = function(raw_idls)
171 //@{
172 {
173     /** Entry point.  See documentation at beginning of file. */
174     this.internal_add_idls(WebIDL2.parse(raw_idls));
175 };
176
177 //@}
178 IdlArray.prototype.add_untested_idls = function(raw_idls)
179 //@{
180 {
181     /** Entry point.  See documentation at beginning of file. */
182     var parsed_idls = WebIDL2.parse(raw_idls);
183     for (var i = 0; i < parsed_idls.length; i++)
184     {
185         parsed_idls[i].untested = true;
186         if ("members" in parsed_idls[i])
187         {
188             for (var j = 0; j < parsed_idls[i].members.length; j++)
189             {
190                 parsed_idls[i].members[j].untested = true;
191             }
192         }
193     }
194     this.internal_add_idls(parsed_idls);
195 };
196
197 //@}
198 IdlArray.prototype.internal_add_idls = function(parsed_idls)
199 //@{
200 {
201     /**
202      * Internal helper called by add_idls() and add_untested_idls().
203      * parsed_idls is an array of objects that come from WebIDLParser.js's
204      * "definitions" production.  The add_untested_idls() entry point
205      * additionally sets an .untested property on each object (and its
206      * .members) so that they'll be skipped by test() -- they'll only be
207      * used for base interfaces of tested interfaces, return types, etc.
208      */
209     parsed_idls.forEach(function(parsed_idl)
210     {
211         if (parsed_idl.type == "interface" && parsed_idl.partial)
212         {
213             this.partials.push(parsed_idl);
214             return;
215         }
216
217         if (parsed_idl.type == "implements")
218         {
219             if (!(parsed_idl.target in this["implements"]))
220             {
221                 this["implements"][parsed_idl.target] = [];
222             }
223             this["implements"][parsed_idl.target].push(parsed_idl["implements"]);
224             return;
225         }
226
227         parsed_idl.array = this;
228         if (parsed_idl.name in this.members)
229         {
230             throw "Duplicate identifier " + parsed_idl.name;
231         }
232         switch(parsed_idl.type)
233         {
234         case "interface":
235             this.members[parsed_idl.name] =
236                 new IdlInterface(parsed_idl, /* is_callback = */ false);
237             break;
238
239         case "dictionary":
240             // Nothing to test, but we need the dictionary info around for type
241             // checks
242             this.members[parsed_idl.name] = new IdlDictionary(parsed_idl);
243             break;
244
245         case "typedef":
246             this.members[parsed_idl.name] = new IdlTypedef(parsed_idl);
247             break;
248
249         case "callback":
250             // TODO
251             console.log("callback not yet supported");
252             break;
253
254         case "enum":
255             this.members[parsed_idl.name] = new IdlEnum(parsed_idl);
256             break;
257
258         case "callback interface":
259             this.members[parsed_idl.name] =
260                 new IdlInterface(parsed_idl, /* is_callback = */ true);
261             break;
262
263         default:
264             throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported";
265         }
266     }.bind(this));
267 };
268
269 //@}
270 IdlArray.prototype.add_objects = function(dict)
271 //@{
272 {
273     /** Entry point.  See documentation at beginning of file. */
274     for (var k in dict)
275     {
276         if (k in this.objects)
277         {
278             this.objects[k] = this.objects[k].concat(dict[k]);
279         }
280         else
281         {
282             this.objects[k] = dict[k];
283         }
284     }
285 };
286
287 //@}
288 IdlArray.prototype.prevent_multiple_testing = function(name)
289 //@{
290 {
291     /** Entry point.  See documentation at beginning of file. */
292     this.members[name].prevent_multiple_testing = true;
293 };
294
295 //@}
296 IdlArray.prototype.recursively_get_implements = function(interface_name)
297 //@{
298 {
299     /**
300      * Helper function for test().  Returns an array of things that implement
301      * interface_name, so if the IDL contains
302      *
303      *   A implements B;
304      *   B implements C;
305      *   B implements D;
306      *
307      * then recursively_get_implements("A") should return ["B", "C", "D"].
308      */
309     var ret = this["implements"][interface_name];
310     if (ret === undefined)
311     {
312         return [];
313     }
314     for (var i = 0; i < this["implements"][interface_name].length; i++)
315     {
316         ret = ret.concat(this.recursively_get_implements(ret[i]));
317         if (ret.indexOf(ret[i]) != ret.lastIndexOf(ret[i]))
318         {
319             throw "Circular implements statements involving " + ret[i];
320         }
321     }
322     return ret;
323 };
324
325 function exposed_in(globals) {
326     if ('document' in self) {
327         return globals.indexOf("Window") >= 0;
328     }
329     if ('DedicatedWorkerGlobalScope' in self &&
330         self instanceof DedicatedWorkerGlobalScope) {
331         return globals.indexOf("Worker") >= 0 ||
332                globals.indexOf("DedicatedWorker") >= 0;
333     }
334     if ('SharedWorkerGlobalScope' in self &&
335         self instanceof SharedWorkerGlobalScope) {
336         return globals.indexOf("Worker") >= 0 ||
337                globals.indexOf("SharedWorker") >= 0;
338     }
339     if ('ServiceWorkerGlobalScope' in self &&
340         self instanceof ServiceWorkerGlobalScope) {
341         return globals.indexOf("Worker") >= 0 ||
342                globals.indexOf("ServiceWorker") >= 0;
343     }
344     throw "Unexpected global object";
345 }
346
347 //@}
348 IdlArray.prototype.test = function()
349 //@{
350 {
351     /** Entry point.  See documentation at beginning of file. */
352
353     // First merge in all the partial interfaces and implements statements we
354     // encountered.
355     this.partials.forEach(function(parsed_idl)
356     {
357         if (!(parsed_idl.name in this.members)
358         || !(this.members[parsed_idl.name] instanceof IdlInterface))
359         {
360             throw "Partial interface " + parsed_idl.name + " with no original interface";
361         }
362         if (parsed_idl.extAttrs)
363         {
364             parsed_idl.extAttrs.forEach(function(extAttr)
365             {
366                 this.members[parsed_idl.name].extAttrs.push(extAttr);
367             }.bind(this));
368         }
369         parsed_idl.members.forEach(function(member)
370         {
371             this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member));
372         }.bind(this));
373     }.bind(this));
374     this.partials = [];
375
376     for (var lhs in this["implements"])
377     {
378         this.recursively_get_implements(lhs).forEach(function(rhs)
379         {
380             var errStr = lhs + " implements " + rhs + ", but ";
381             if (!(lhs in this.members)) throw errStr + lhs + " is undefined.";
382             if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface.";
383             if (!(rhs in this.members)) throw errStr + rhs + " is undefined.";
384             if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface.";
385             this.members[rhs].members.forEach(function(member)
386             {
387                 this.members[lhs].members.push(new IdlInterfaceMember(member));
388             }.bind(this));
389         }.bind(this));
390     }
391     this["implements"] = {};
392
393     Object.getOwnPropertyNames(this.members).forEach(function(memberName) {
394         var member = this.members[memberName];
395         if (!(member instanceof IdlInterface)) {
396             return;
397         }
398
399         var exposed = member.extAttrs.filter(function(a) { return a.name == "Exposed" });
400         if (exposed.length > 1) {
401             throw "Unexpected Exposed extended attributes on " + memberName + ": " + exposed;
402         }
403
404         var globals = exposed.length === 1
405                     ? exposed[0].rhs.value
406                     : ["Window"];
407         member.exposed = exposed_in(globals);
408     }.bind(this));
409
410     // Now run test() on every member, and test_object() for every object.
411     for (var name in this.members)
412     {
413         this.members[name].test();
414         if (name in this.objects)
415         {
416             this.objects[name].forEach(function(str)
417             {
418                 this.members[name].test_object(str);
419             }.bind(this));
420         }
421     }
422 };
423
424 //@}
425 IdlArray.prototype.assert_type_is = function(value, type)
426 //@{
427 {
428     /**
429      * Helper function that tests that value is an instance of type according
430      * to the rules of WebIDL.  value is any JavaScript value, and type is an
431      * object produced by WebIDLParser.js' "type" production.  That production
432      * is fairly elaborate due to the complexity of WebIDL's types, so it's
433      * best to look at the grammar to figure out what properties it might have.
434      */
435     if (type.idlType == "any")
436     {
437         // No assertions to make
438         return;
439     }
440
441     if (type.nullable && value === null)
442     {
443         // This is fine
444         return;
445     }
446
447     if (type.array)
448     {
449         // TODO: not supported yet
450         return;
451     }
452
453     if (type.sequence)
454     {
455         assert_true(Array.isArray(value), "is not array");
456         if (!value.length)
457         {
458             // Nothing we can do.
459             return;
460         }
461         this.assert_type_is(value[0], type.idlType.idlType);
462         return;
463     }
464
465     if (type.generic === "Promise") {
466         assert_true("then" in value, "Attribute with a Promise type has a then property");
467         // TODO: Ideally, we would check on project fulfillment
468         // that we get the right type
469         // but that would require making the type check async
470         return;
471     }
472
473     type = type.idlType;
474
475     switch(type)
476     {
477         case "void":
478             assert_equals(value, undefined);
479             return;
480
481         case "boolean":
482             assert_equals(typeof value, "boolean");
483             return;
484
485         case "byte":
486             assert_equals(typeof value, "number");
487             assert_equals(value, Math.floor(value), "not an integer");
488             assert_true(-128 <= value && value <= 127, "byte " + value + " not in range [-128, 127]");
489             return;
490
491         case "octet":
492             assert_equals(typeof value, "number");
493             assert_equals(value, Math.floor(value), "not an integer");
494             assert_true(0 <= value && value <= 255, "octet " + value + " not in range [0, 255]");
495             return;
496
497         case "short":
498             assert_equals(typeof value, "number");
499             assert_equals(value, Math.floor(value), "not an integer");
500             assert_true(-32768 <= value && value <= 32767, "short " + value + " not in range [-32768, 32767]");
501             return;
502
503         case "unsigned short":
504             assert_equals(typeof value, "number");
505             assert_equals(value, Math.floor(value), "not an integer");
506             assert_true(0 <= value && value <= 65535, "unsigned short " + value + " not in range [0, 65535]");
507             return;
508
509         case "long":
510             assert_equals(typeof value, "number");
511             assert_equals(value, Math.floor(value), "not an integer");
512             assert_true(-2147483648 <= value && value <= 2147483647, "long " + value + " not in range [-2147483648, 2147483647]");
513             return;
514
515         case "unsigned long":
516             assert_equals(typeof value, "number");
517             assert_equals(value, Math.floor(value), "not an integer");
518             assert_true(0 <= value && value <= 4294967295, "unsigned long " + value + " not in range [0, 4294967295]");
519             return;
520
521         case "long long":
522             assert_equals(typeof value, "number");
523             return;
524
525         case "unsigned long long":
526         case "DOMTimeStamp":
527             assert_equals(typeof value, "number");
528             assert_true(0 <= value, "unsigned long long is negative");
529             return;
530
531         case "float":
532             assert_equals(typeof value, "number");
533             assert_equals(value, fround(value), "float rounded to 32-bit float should be itself");
534             assert_not_equals(value, Infinity);
535             assert_not_equals(value, -Infinity);
536             assert_not_equals(value, NaN);
537             return;
538
539         case "DOMHighResTimeStamp":
540         case "double":
541             assert_equals(typeof value, "number");
542             assert_not_equals(value, Infinity);
543             assert_not_equals(value, -Infinity);
544             assert_not_equals(value, NaN);
545             return;
546
547         case "unrestricted float":
548             assert_equals(typeof value, "number");
549             assert_equals(value, fround(value), "unrestricted float rounded to 32-bit float should be itself");
550             return;
551
552         case "unrestricted double":
553             assert_equals(typeof value, "number");
554             return;
555
556         case "DOMString":
557             assert_equals(typeof value, "string");
558             return;
559
560         case "ByteString":
561             assert_equals(typeof value, "string");
562             assert_regexp_match(value, /^[\x00-\x7F]*$/);
563             return;
564
565         case "USVString":
566             assert_equals(typeof value, "string");
567             assert_regexp_match(value, /^([\x00-\ud7ff\ue000-\uffff]|[\ud800-\udbff][\udc00-\udfff])*$/);
568             return;
569
570         case "object":
571             assert_true(typeof value == "object" || typeof value == "function", "wrong type: not object or function");
572             return;
573     }
574
575     if (!(type in this.members))
576     {
577         throw "Unrecognized type " + type;
578     }
579
580     if (this.members[type] instanceof IdlInterface)
581     {
582         // We don't want to run the full
583         // IdlInterface.prototype.test_instance_of, because that could result
584         // in an infinite loop.  TODO: This means we don't have tests for
585         // NoInterfaceObject interfaces, and we also can't test objects that
586         // come from another self.
587         assert_true(typeof value == "object" || typeof value == "function", "wrong type: not object or function");
588         if (value instanceof Object
589         && !this.members[type].has_extended_attribute("NoInterfaceObject")
590         && type in self)
591         {
592             assert_true(value instanceof self[type], "not instanceof " + type);
593         }
594     }
595     else if (this.members[type] instanceof IdlEnum)
596     {
597         assert_equals(typeof value, "string");
598     }
599     else if (this.members[type] instanceof IdlDictionary)
600     {
601         // TODO: Test when we actually have something to test this on
602     }
603     else if (this.members[type] instanceof IdlTypedef)
604     {
605         // TODO: Test when we actually have something to test this on
606     }
607     else
608     {
609         throw "Type " + type + " isn't an interface or dictionary";
610     }
611 };
612 //@}
613
614 /// IdlObject ///
615 function IdlObject() {}
616 IdlObject.prototype.test = function()
617 //@{
618 {
619     /**
620      * By default, this does nothing, so no actual tests are run for IdlObjects
621      * that don't define any (e.g., IdlDictionary at the time of this writing).
622      */
623 };
624
625 //@}
626 IdlObject.prototype.has_extended_attribute = function(name)
627 //@{
628 {
629     /**
630      * This is only meaningful for things that support extended attributes,
631      * such as interfaces, exceptions, and members.
632      */
633     return this.extAttrs.some(function(o)
634     {
635         return o.name == name;
636     });
637 };
638
639 //@}
640
641 /// IdlDictionary ///
642 // Used for IdlArray.prototype.assert_type_is
643 function IdlDictionary(obj)
644 //@{
645 {
646     /**
647      * obj is an object produced by the WebIDLParser.js "dictionary"
648      * production.
649      */
650
651     /** Self-explanatory. */
652     this.name = obj.name;
653
654     /** An array of objects produced by the "dictionaryMember" production. */
655     this.members = obj.members;
656
657     /**
658      * The name (as a string) of the dictionary type we inherit from, or null
659      * if there is none.
660      */
661     this.base = obj.inheritance;
662 }
663
664 //@}
665 IdlDictionary.prototype = Object.create(IdlObject.prototype);
666
667 /// IdlInterface ///
668 function IdlInterface(obj, is_callback)
669 //@{
670 {
671     /**
672      * obj is an object produced by the WebIDLParser.js "interface" production.
673      */
674
675     /** Self-explanatory. */
676     this.name = obj.name;
677
678     /** A back-reference to our IdlArray. */
679     this.array = obj.array;
680
681     /**
682      * An indicator of whether we should run tests on the interface object and
683      * interface prototype object. Tests on members are controlled by .untested
684      * on each member, not this.
685      */
686     this.untested = obj.untested;
687
688     /** An array of objects produced by the "ExtAttr" production. */
689     this.extAttrs = obj.extAttrs;
690
691     /** An array of IdlInterfaceMembers. */
692     this.members = obj.members.map(function(m){return new IdlInterfaceMember(m); });
693     if (this.has_extended_attribute("Unforgeable")) {
694         this.members
695             .filter(function(m) { return !m["static"] && (m.type == "attribute" || m.type == "operation"); })
696             .forEach(function(m) { return m.isUnforgeable = true; });
697     }
698
699     /**
700      * The name (as a string) of the type we inherit from, or null if there is
701      * none.
702      */
703     this.base = obj.inheritance;
704
705     this._is_callback = is_callback;
706 }
707 //@}
708 IdlInterface.prototype = Object.create(IdlObject.prototype);
709 IdlInterface.prototype.is_callback = function()
710 //@{
711 {
712     return this._is_callback;
713 };
714 //@}
715
716 IdlInterface.prototype.has_constants = function()
717 //@{
718 {
719     return this.members.some(function(member) {
720         return member.type === "const";
721     });
722 };
723 //@}
724
725 IdlInterface.prototype.is_global = function()
726 //@{
727 {
728     return this.extAttrs.some(function(attribute) {
729         return attribute.name === "Global" ||
730                attribute.name === "PrimaryGlobal";
731     });
732 };
733 //@}
734
735 IdlInterface.prototype.test = function()
736 //@{
737 {
738     if (this.has_extended_attribute("NoInterfaceObject"))
739     {
740         // No tests to do without an instance.  TODO: We should still be able
741         // to run tests on the prototype object, if we obtain one through some
742         // other means.
743         return;
744     }
745
746     if (!this.exposed) {
747         test(function() {
748             assert_false(this.name in self);
749         }.bind(this), this.name + " interface: existence and properties of interface object");
750         return;
751     }
752
753     if (!this.untested)
754     {
755         // First test things to do with the exception/interface object and
756         // exception/interface prototype object.
757         this.test_self();
758     }
759     // Then test things to do with its members (constants, fields, attributes,
760     // operations, . . .).  These are run even if .untested is true, because
761     // members might themselves be marked as .untested.  This might happen to
762     // interfaces if the interface itself is untested but a partial interface
763     // that extends it is tested -- then the interface itself and its initial
764     // members will be marked as untested, but the members added by the partial
765     // interface are still tested.
766     this.test_members();
767 };
768 //@}
769
770 IdlInterface.prototype.test_self = function()
771 //@{
772 {
773     test(function()
774     {
775         // This function tests WebIDL as of 2015-01-13.
776
777         // "For every interface that is exposed in a given ECMAScript global
778         // environment and:
779         // * is a callback interface that has constants declared on it, or
780         // * is a non-callback interface that is not declared with the
781         //   [NoInterfaceObject] extended attribute,
782         // a corresponding property MUST exist on the ECMAScript global object.
783         // The name of the property is the identifier of the interface, and its
784         // value is an object called the interface object.
785         // The property has the attributes { [[Writable]]: true,
786         // [[Enumerable]]: false, [[Configurable]]: true }."
787         if (this.is_callback() && !this.has_constants()) {
788             return;
789         }
790
791         // TODO: Should we test here that the property is actually writable
792         // etc., or trust getOwnPropertyDescriptor?
793         assert_own_property(self, this.name,
794                             "self does not have own property " + format_value(this.name));
795         var desc = Object.getOwnPropertyDescriptor(self, this.name);
796         assert_false("get" in desc, "self's property " + format_value(this.name) + " has getter");
797         assert_false("set" in desc, "self's property " + format_value(this.name) + " has setter");
798         assert_true(desc.writable, "self's property " + format_value(this.name) + " is not writable");
799         assert_false(desc.enumerable, "self's property " + format_value(this.name) + " is enumerable");
800         assert_true(desc.configurable, "self's property " + format_value(this.name) + " is not configurable");
801
802         if (this.is_callback()) {
803             // "The internal [[Prototype]] property of an interface object for
804             // a callback interface must be the Function.prototype object."
805             assert_equals(Object.getPrototypeOf(self[this.name]), Function.prototype,
806                           "prototype of self's property " + format_value(this.name) + " is not Object.prototype");
807
808             return;
809         }
810
811         // "The interface object for a given non-callback interface is a
812         // function object."
813         // "If an object is defined to be a function object, then it has
814         // characteristics as follows:"
815
816         // Its [[Prototype]] internal property is otherwise specified (see
817         // below).
818
819         // "* Its [[Get]] internal property is set as described in ECMA-262
820         //    section 9.1.8."
821         // Not much to test for this.
822
823         // "* Its [[Construct]] internal property is set as described in
824         //    ECMA-262 section 19.2.2.3."
825         // Tested below if no constructor is defined.  TODO: test constructors
826         // if defined.
827
828         // "* Its @@hasInstance property is set as described in ECMA-262
829         //    section 19.2.3.8, unless otherwise specified."
830         // TODO
831
832         // ES6 (rev 30) 19.1.3.6:
833         // "Else, if O has a [[Call]] internal method, then let builtinTag be
834         // "Function"."
835         assert_class_string(self[this.name], "Function", "class string of " + this.name);
836
837         // "The [[Prototype]] internal property of an interface object for a
838         // non-callback interface is determined as follows:"
839         var prototype = Object.getPrototypeOf(self[this.name]);
840         if (this.base) {
841             // "* If the interface inherits from some other interface, the
842             //    value of [[Prototype]] is the interface object for that other
843             //    interface."
844             var has_interface_object =
845                 !this.array
846                      .members[this.base]
847                      .has_extended_attribute("NoInterfaceObject");
848             if (has_interface_object) {
849                 assert_own_property(self, this.base,
850                                     'should inherit from ' + this.base +
851                                     ', but self has no such property');
852                 assert_equals(prototype, self[this.base],
853                               'prototype of ' + this.name + ' is not ' +
854                               this.base);
855             }
856         } else {
857             // "If the interface doesn't inherit from any other interface, the
858             // value of [[Prototype]] is %FunctionPrototype% ([ECMA-262],
859             // section 6.1.7.4)."
860             assert_equals(prototype, Function.prototype,
861                           "prototype of self's property " + format_value(this.name) + " is not Function.prototype");
862         }
863
864         if (!this.has_extended_attribute("Constructor")) {
865             // "The internal [[Call]] method of the interface object behaves as
866             // follows . . .
867             //
868             // "If I was not declared with a [Constructor] extended attribute,
869             // then throw a TypeError."
870             assert_throws(new TypeError(), function() {
871                 self[this.name]();
872             }.bind(this), "interface object didn't throw TypeError when called as a function");
873             assert_throws(new TypeError(), function() {
874                 new self[this.name]();
875             }.bind(this), "interface object didn't throw TypeError when called as a constructor");
876         }
877     }.bind(this), this.name + " interface: existence and properties of interface object");
878
879     if (!this.is_callback()) {
880         test(function() {
881             // This function tests WebIDL as of 2014-10-25.
882             // https://heycam.github.io/webidl/#es-interface-call
883
884             assert_own_property(self, this.name,
885                                 "self does not have own property " + format_value(this.name));
886
887             // "Interface objects for non-callback interfaces MUST have a
888             // property named “length” with attributes { [[Writable]]: false,
889             // [[Enumerable]]: false, [[Configurable]]: true } whose value is
890             // a Number."
891             assert_own_property(self[this.name], "length");
892             var desc = Object.getOwnPropertyDescriptor(self[this.name], "length");
893             assert_false("get" in desc, this.name + ".length has getter");
894             assert_false("set" in desc, this.name + ".length has setter");
895             assert_false(desc.writable, this.name + ".length is writable");
896             assert_false(desc.enumerable, this.name + ".length is enumerable");
897             assert_true(desc.configurable, this.name + ".length is not configurable");
898
899             var constructors = this.extAttrs
900                 .filter(function(attr) { return attr.name == "Constructor"; });
901             var expected_length = minOverloadLength(constructors);
902             assert_equals(self[this.name].length, expected_length, "wrong value for " + this.name + ".length");
903         }.bind(this), this.name + " interface object length");
904     }
905
906     if (!this.is_callback() || this.has_constants()) {
907         test(function() {
908             // This function tests WebIDL as of 2015-11-17.
909             // https://heycam.github.io/webidl/#interface-object
910
911             assert_own_property(self, this.name,
912                                 "self does not have own property " + format_value(this.name));
913
914             // "All interface objects must have a property named “name” with
915             // attributes { [[Writable]]: false, [[Enumerable]]: false,
916             // [[Configurable]]: true } whose value is the identifier of the
917             // corresponding interface."
918
919             assert_own_property(self[this.name], "name");
920             var desc = Object.getOwnPropertyDescriptor(self[this.name], "name");
921             assert_false("get" in desc, this.name + ".name has getter");
922             assert_false("set" in desc, this.name + ".name has setter");
923             assert_false(desc.writable, this.name + ".name is writable");
924             assert_false(desc.enumerable, this.name + ".name is enumerable");
925             assert_true(desc.configurable, this.name + ".name is not configurable");
926             assert_equals(self[this.name].name, this.name, "wrong value for " + this.name + ".name");
927         }.bind(this), this.name + " interface object name");
928     }
929
930     // TODO: Test named constructors if I find any interfaces that have them.
931
932     test(function()
933     {
934         // This function tests WebIDL as of 2015-01-21.
935         // https://heycam.github.io/webidl/#interface-object
936
937         if (this.is_callback() && !this.has_constants()) {
938             return;
939         }
940
941         assert_own_property(self, this.name,
942                             "self does not have own property " + format_value(this.name));
943
944         if (this.is_callback()) {
945             assert_false("prototype" in self[this.name],
946                          this.name + ' should not have a "prototype" property');
947             return;
948         }
949
950         // "An interface object for a non-callback interface must have a
951         // property named “prototype” with attributes { [[Writable]]: false,
952         // [[Enumerable]]: false, [[Configurable]]: false } whose value is an
953         // object called the interface prototype object. This object has
954         // properties that correspond to the regular attributes and regular
955         // operations defined on the interface, and is described in more detail
956         // in section 4.5.4 below."
957         assert_own_property(self[this.name], "prototype",
958                             'interface "' + this.name + '" does not have own property "prototype"');
959         var desc = Object.getOwnPropertyDescriptor(self[this.name], "prototype");
960         assert_false("get" in desc, this.name + ".prototype has getter");
961         assert_false("set" in desc, this.name + ".prototype has setter");
962         assert_false(desc.writable, this.name + ".prototype is writable");
963         assert_false(desc.enumerable, this.name + ".prototype is enumerable");
964         assert_false(desc.configurable, this.name + ".prototype is configurable");
965
966         // Next, test that the [[Prototype]] of the interface prototype object
967         // is correct. (This is made somewhat difficult by the existence of
968         // [NoInterfaceObject].)
969         // TODO: Aryeh thinks there's at least other place in this file where
970         //       we try to figure out if an interface prototype object is
971         //       correct. Consolidate that code.
972
973         // "The interface prototype object for a given interface A must have an
974         // internal [[Prototype]] property whose value is returned from the
975         // following steps:
976         // "If A is declared with the [Global] or [PrimaryGlobal] extended
977         // attribute, and A supports named properties, then return the named
978         // properties object for A, as defined in section 4.5.5 below.
979         // "Otherwise, if A is declared to inherit from another interface, then
980         // return the interface prototype object for the inherited interface.
981         // "Otherwise, if A is declared with the [ArrayClass] extended
982         // attribute, then return %ArrayPrototype% ([ECMA-262], section
983         // 6.1.7.4).
984         // "Otherwise, return %ObjectPrototype% ([ECMA-262], section 6.1.7.4).
985         // ([ECMA-262], section 15.2.4).
986         if (this.name === "Window") {
987             assert_class_string(Object.getPrototypeOf(self[this.name].prototype),
988                                 'WindowProperties',
989                                 'Class name for prototype of Window' +
990                                 '.prototype is not "WindowProperties"');
991         } else {
992             var inherit_interface, inherit_interface_has_interface_object;
993             if (this.base) {
994                 inherit_interface = this.base;
995                 inherit_interface_has_interface_object =
996                     !this.array
997                          .members[inherit_interface]
998                          .has_extended_attribute("NoInterfaceObject");
999             } else if (this.has_extended_attribute('ArrayClass')) {
1000                 inherit_interface = 'Array';
1001                 inherit_interface_has_interface_object = true;
1002             } else {
1003                 inherit_interface = 'Object';
1004                 inherit_interface_has_interface_object = true;
1005             }
1006             if (inherit_interface_has_interface_object) {
1007                 assert_own_property(self, inherit_interface,
1008                                     'should inherit from ' + inherit_interface + ', but self has no such property');
1009                 assert_own_property(self[inherit_interface], 'prototype',
1010                                     'should inherit from ' + inherit_interface + ', but that object has no "prototype" property');
1011                 assert_equals(Object.getPrototypeOf(self[this.name].prototype),
1012                               self[inherit_interface].prototype,
1013                               'prototype of ' + this.name + '.prototype is not ' + inherit_interface + '.prototype');
1014             } else {
1015                 // We can't test that we get the correct object, because this is the
1016                 // only way to get our hands on it. We only test that its class
1017                 // string, at least, is correct.
1018                 assert_class_string(Object.getPrototypeOf(self[this.name].prototype),
1019                                     inherit_interface + 'Prototype',
1020                                     'Class name for prototype of ' + this.name +
1021                                     '.prototype is not "' + inherit_interface + 'Prototype"');
1022             }
1023         }
1024
1025         // "The class string of an interface prototype object is the
1026         // concatenation of the interface’s identifier and the string
1027         // “Prototype”."
1028         assert_class_string(self[this.name].prototype, this.name + "Prototype",
1029                             "class string of " + this.name + ".prototype");
1030         // String() should end up calling {}.toString if nothing defines a
1031         // stringifier.
1032         if (!this.has_stringifier()) {
1033             assert_equals(String(self[this.name].prototype), "[object " + this.name + "Prototype]",
1034                     "String(" + this.name + ".prototype)");
1035         }
1036     }.bind(this), this.name + " interface: existence and properties of interface prototype object");
1037
1038     test(function()
1039     {
1040         if (this.is_callback() && !this.has_constants()) {
1041             return;
1042         }
1043
1044         assert_own_property(self, this.name,
1045                             "self does not have own property " + format_value(this.name));
1046
1047         if (this.is_callback()) {
1048             assert_false("prototype" in self[this.name],
1049                          this.name + ' should not have a "prototype" property');
1050             return;
1051         }
1052
1053         assert_own_property(self[this.name], "prototype",
1054                             'interface "' + this.name + '" does not have own property "prototype"');
1055
1056         // "If the [NoInterfaceObject] extended attribute was not specified on
1057         // the interface, then the interface prototype object must also have a
1058         // property named “constructor” with attributes { [[Writable]]: true,
1059         // [[Enumerable]]: false, [[Configurable]]: true } whose value is a
1060         // reference to the interface object for the interface."
1061         assert_own_property(self[this.name].prototype, "constructor",
1062                             this.name + '.prototype does not have own property "constructor"');
1063         var desc = Object.getOwnPropertyDescriptor(self[this.name].prototype, "constructor");
1064         assert_false("get" in desc, this.name + ".prototype.constructor has getter");
1065         assert_false("set" in desc, this.name + ".prototype.constructor has setter");
1066         assert_true(desc.writable, this.name + ".prototype.constructor is not writable");
1067         assert_false(desc.enumerable, this.name + ".prototype.constructor is enumerable");
1068         assert_true(desc.configurable, this.name + ".prototype.constructor in not configurable");
1069         assert_equals(self[this.name].prototype.constructor, self[this.name],
1070                       this.name + '.prototype.constructor is not the same object as ' + this.name);
1071     }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property');
1072 };
1073
1074 //@}
1075 IdlInterface.prototype.test_member_const = function(member)
1076 //@{
1077 {
1078     if (!this.has_constants()) {
1079         throw "Internal error: test_member_const called without any constants";
1080     }
1081
1082     test(function()
1083     {
1084         assert_own_property(self, this.name,
1085                             "self does not have own property " + format_value(this.name));
1086
1087         // "For each constant defined on an interface A, there must be
1088         // a corresponding property on the interface object, if it
1089         // exists."
1090         assert_own_property(self[this.name], member.name);
1091         // "The value of the property is that which is obtained by
1092         // converting the constant’s IDL value to an ECMAScript
1093         // value."
1094         assert_equals(self[this.name][member.name], constValue(member.value),
1095                       "property has wrong value");
1096         // "The property has attributes { [[Writable]]: false,
1097         // [[Enumerable]]: true, [[Configurable]]: false }."
1098         var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name);
1099         assert_false("get" in desc, "property has getter");
1100         assert_false("set" in desc, "property has setter");
1101         assert_false(desc.writable, "property is writable");
1102         assert_true(desc.enumerable, "property is not enumerable");
1103         assert_false(desc.configurable, "property is configurable");
1104     }.bind(this), this.name + " interface: constant " + member.name + " on interface object");
1105
1106     // "In addition, a property with the same characteristics must
1107     // exist on the interface prototype object."
1108     test(function()
1109     {
1110         assert_own_property(self, this.name,
1111                             "self does not have own property " + format_value(this.name));
1112
1113         if (this.is_callback()) {
1114             assert_false("prototype" in self[this.name],
1115                          this.name + ' should not have a "prototype" property');
1116             return;
1117         }
1118
1119         assert_own_property(self[this.name], "prototype",
1120                             'interface "' + this.name + '" does not have own property "prototype"');
1121
1122         assert_own_property(self[this.name].prototype, member.name);
1123         assert_equals(self[this.name].prototype[member.name], constValue(member.value),
1124                       "property has wrong value");
1125         var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name);
1126         assert_false("get" in desc, "property has getter");
1127         assert_false("set" in desc, "property has setter");
1128         assert_false(desc.writable, "property is writable");
1129         assert_true(desc.enumerable, "property is not enumerable");
1130         assert_false(desc.configurable, "property is configurable");
1131     }.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object");
1132 };
1133
1134
1135 //@}
1136 IdlInterface.prototype.test_member_attribute = function(member)
1137 //@{
1138   {
1139     var a_test = async_test(this.name + " interface: attribute " + member.name);
1140     a_test.step(function()
1141     {
1142         if (this.is_callback() && !this.has_constants()) {
1143             a_test.done()
1144             return;
1145         }
1146
1147         assert_own_property(self, this.name,
1148                             "self does not have own property " + format_value(this.name));
1149         assert_own_property(self[this.name], "prototype",
1150                             'interface "' + this.name + '" does not have own property "prototype"');
1151
1152         if (member["static"]) {
1153             assert_own_property(self[this.name], member.name,
1154                 "The interface object must have a property " +
1155                 format_value(member.name));
1156             a_test.done();
1157         } else if (this.is_global()) {
1158             assert_own_property(self, member.name,
1159                 "The global object must have a property " +
1160                 format_value(member.name));
1161             assert_false(member.name in self[this.name].prototype,
1162                 "The prototype object must not have a property " +
1163                 format_value(member.name));
1164
1165             var getter = Object.getOwnPropertyDescriptor(self, member.name).get;
1166             assert_equals(typeof(getter), "function",
1167                           format_value(member.name) + " must have a getter");
1168
1169             // Try/catch around the get here, since it can legitimately throw.
1170             // If it does, we obviously can't check for equality with direct
1171             // invocation of the getter.
1172             var gotValue;
1173             var propVal;
1174             try {
1175                 propVal = self[member.name];
1176                 gotValue = true;
1177             } catch (e) {
1178                 gotValue = false;
1179             }
1180             if (gotValue) {
1181                 assert_equals(propVal, getter.call(undefined),
1182                               "Gets on a global should not require an explicit this");
1183             }
1184
1185             // do_interface_attribute_asserts must be the last thing we do,
1186             // since it will call done() on a_test.
1187             this.do_interface_attribute_asserts(self, member, a_test);
1188         } else {
1189             assert_true(member.name in self[this.name].prototype,
1190                 "The prototype object must have a property " +
1191                 format_value(member.name));
1192
1193             if (!member.has_extended_attribute("LenientThis")) {
1194                 if (member.idlType.generic !== "Promise") {
1195                     assert_throws(new TypeError(), function() {
1196                         self[this.name].prototype[member.name];
1197                     }.bind(this), "getting property on prototype object must throw TypeError");
1198                     // do_interface_attribute_asserts must be the last thing we
1199                     // do, since it will call done() on a_test.
1200                     this.do_interface_attribute_asserts(self[this.name].prototype, member, a_test);
1201                 } else {
1202                     promise_rejects(a_test, new TypeError(),
1203                                     self[this.name].prototype[member.name])
1204                         .then(function() {
1205                             // do_interface_attribute_asserts must be the last
1206                             // thing we do, since it will call done() on a_test.
1207                             this.do_interface_attribute_asserts(self[this.name].prototype,
1208                                                                 member, a_test);
1209                         }.bind(this));
1210                 }
1211             } else {
1212                 assert_equals(self[this.name].prototype[member.name], undefined,
1213                               "getting property on prototype object must return undefined");
1214               // do_interface_attribute_asserts must be the last thing we do,
1215               // since it will call done() on a_test.
1216               this.do_interface_attribute_asserts(self[this.name].prototype, member, a_test);
1217             }
1218
1219         }
1220     }.bind(this));
1221 };
1222
1223 //@}
1224 IdlInterface.prototype.test_member_operation = function(member)
1225 //@{
1226 {
1227     var a_test = async_test(this.name + " interface: operation " + member.name +
1228                             "(" + member.arguments.map(
1229                                 function(m) {return m.idlType.idlType; } )
1230                             +")");
1231     a_test.step(function()
1232     {
1233         // This function tests WebIDL as of 2015-12-29.
1234         // https://heycam.github.io/webidl/#es-operations
1235
1236         if (this.is_callback() && !this.has_constants()) {
1237             a_test.done();
1238             return;
1239         }
1240
1241         assert_own_property(self, this.name,
1242                             "self does not have own property " + format_value(this.name));
1243
1244         if (this.is_callback()) {
1245             assert_false("prototype" in self[this.name],
1246                          this.name + ' should not have a "prototype" property');
1247             a_test.done();
1248             return;
1249         }
1250
1251         assert_own_property(self[this.name], "prototype",
1252                             'interface "' + this.name + '" does not have own property "prototype"');
1253
1254         // "For each unique identifier of an exposed operation defined on the
1255         // interface, there must exist a corresponding property, unless the
1256         // effective overload set for that identifier and operation and with an
1257         // argument count of 0 has no entries."
1258
1259         // TODO: Consider [Exposed].
1260
1261         // "The location of the property is determined as follows:"
1262         var memberHolderObject;
1263         // "* If the operation is static, then the property exists on the
1264         //    interface object."
1265         if (member["static"]) {
1266             assert_own_property(self[this.name], member.name,
1267                     "interface object missing static operation");
1268             memberHolderObject = self[this.name];
1269         // "* Otherwise, [...] if the interface was declared with the [Global]
1270         //    or [PrimaryGlobal] extended attribute, then the property exists
1271         //    on every object that implements the interface."
1272         } else if (this.is_global()) {
1273             assert_own_property(self, member.name,
1274                     "global object missing non-static operation");
1275             memberHolderObject = self;
1276         // "* Otherwise, the property exists solely on the interface’s
1277         //    interface prototype object."
1278         } else {
1279             assert_own_property(self[this.name].prototype, member.name,
1280                     "interface prototype object missing non-static operation");
1281             memberHolderObject = self[this.name].prototype;
1282         }
1283         this.do_member_operation_asserts(memberHolderObject, member, a_test);
1284     }.bind(this));
1285 };
1286
1287 //@}
1288 IdlInterface.prototype.do_member_operation_asserts = function(memberHolderObject, member, a_test)
1289 //@{
1290 {
1291     var done = a_test.done.bind(a_test);
1292     var operationUnforgeable = member.isUnforgeable;
1293     var desc = Object.getOwnPropertyDescriptor(memberHolderObject, member.name);
1294     // "The property has attributes { [[Writable]]: B,
1295     // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the
1296     // operation is unforgeable on the interface, and true otherwise".
1297     assert_false("get" in desc, "property has getter");
1298     assert_false("set" in desc, "property has setter");
1299     assert_equals(desc.writable, !operationUnforgeable,
1300                   "property should be writable if and only if not unforgeable");
1301     assert_true(desc.enumerable, "property is not enumerable");
1302     assert_equals(desc.configurable, !operationUnforgeable,
1303                   "property should be configurable if and only if not unforgeable");
1304     // "The value of the property is a Function object whose
1305     // behavior is as follows . . ."
1306     assert_equals(typeof memberHolderObject[member.name], "function",
1307                   "property must be a function");
1308     // "The value of the Function object’s “length” property is
1309     // a Number determined as follows:
1310     // ". . .
1311     // "Return the length of the shortest argument list of the
1312     // entries in S."
1313     assert_equals(memberHolderObject[member.name].length,
1314         minOverloadLength(this.members.filter(function(m) {
1315             return m.type == "operation" && m.name == member.name;
1316         })),
1317         "property has wrong .length");
1318
1319     // Make some suitable arguments
1320     var args = member.arguments.map(function(arg) {
1321         return create_suitable_object(arg.idlType);
1322     });
1323
1324     // "Let O be a value determined as follows:
1325     // ". . .
1326     // "Otherwise, throw a TypeError."
1327     // This should be hit if the operation is not static, there is
1328     // no [ImplicitThis] attribute, and the this value is null.
1329     //
1330     // TODO: We currently ignore the [ImplicitThis] case.  Except we manually
1331     // check for globals, since otherwise we'll invoke window.close().  And we
1332     // have to skip this test for anything that on the proto chain of "self",
1333     // since that does in fact have implicit-this behavior.
1334     if (!member["static"]) {
1335         var cb;
1336         if (!this.is_global() &&
1337             memberHolderObject[member.name] != self[member.name])
1338         {
1339             cb = awaitNCallbacks(2, done);
1340             throwOrReject(a_test, member, memberHolderObject[member.name], null, args,
1341                           "calling operation with this = null didn't throw TypeError", cb);
1342         } else {
1343             cb = awaitNCallbacks(1, done);
1344         }
1345
1346         // ". . . If O is not null and is also not a platform object
1347         // that implements interface I, throw a TypeError."
1348         //
1349         // TODO: Test a platform object that implements some other
1350         // interface.  (Have to be sure to get inheritance right.)
1351         throwOrReject(a_test, member, memberHolderObject[member.name], {}, args,
1352                       "calling operation with this = {} didn't throw TypeError", cb);
1353     } else {
1354         done();
1355     }
1356 }
1357
1358 //@}
1359 IdlInterface.prototype.add_iterable_members = function(member)
1360 //@{
1361 {
1362     this.members.push({type: "operation", name: "entries", idlType: "iterator", arguments: []});
1363     this.members.push({type: "operation", name: "keys", idlType: "iterator", arguments: []});
1364     this.members.push({type: "operation", name: "values", idlType: "iterator", arguments: []});
1365     this.members.push({type: "operation", name: "forEach", idlType: "void", arguments:
1366         [{ name: "callback", idlType: {idlType: "function"}},
1367         { name: "thisValue", idlType: {idlType: "any"}, optional: true}]});
1368 };
1369
1370 //@}
1371 IdlInterface.prototype.test_member_iterable = function(member)
1372 //@{
1373 {
1374     var interfaceName = this.name;
1375     var isPairIterator = member.idlType instanceof Array;
1376     test(function()
1377     {
1378         var descriptor = Object.getOwnPropertyDescriptor(self[interfaceName].prototype, Symbol.iterator);
1379         assert_true(descriptor.writable, "property is not writable");
1380         assert_true(descriptor.configurable, "property is not configurable");
1381         assert_false(descriptor.enumerable, "property is enumerable");
1382         assert_equals(self[interfaceName].prototype[Symbol.iterator].name, isPairIterator ? "entries" : "values", "@@iterator function does not have the right name");
1383     }, "Testing Symbol.iterator property of iterable interface " + interfaceName);
1384
1385     if (isPairIterator) {
1386         test(function() {
1387             assert_equals(self[interfaceName].prototype[Symbol.iterator], self[interfaceName].prototype["entries"], "entries method is not the same as @@iterator");
1388         }, "Testing pair iterable interface " + interfaceName);
1389     } else {
1390         test(function() {
1391             ["entries", "keys", "values", "forEach", Symbol.Iterator].forEach(function(property) {
1392                 assert_equals(self[interfaceName].prototype[property], Array.prototype[property], property + " function is not the same as Array one");
1393             });
1394         }, "Testing value iterable interface " + interfaceName);
1395     }
1396 };
1397
1398 //@}
1399 IdlInterface.prototype.test_member_stringifier = function(member)
1400 //@{
1401 {
1402     test(function()
1403     {
1404         if (this.is_callback() && !this.has_constants()) {
1405             return;
1406         }
1407
1408         assert_own_property(self, this.name,
1409                             "self does not have own property " + format_value(this.name));
1410
1411         if (this.is_callback()) {
1412             assert_false("prototype" in self[this.name],
1413                          this.name + ' should not have a "prototype" property');
1414             return;
1415         }
1416
1417         assert_own_property(self[this.name], "prototype",
1418                             'interface "' + this.name + '" does not have own property "prototype"');
1419
1420         // ". . . the property exists on the interface prototype object."
1421         var interfacePrototypeObject = self[this.name].prototype;
1422         assert_own_property(self[this.name].prototype, "toString",
1423                 "interface prototype object missing non-static operation");
1424
1425         var stringifierUnforgeable = member.isUnforgeable;
1426         var desc = Object.getOwnPropertyDescriptor(interfacePrototypeObject, "toString");
1427         // "The property has attributes { [[Writable]]: B,
1428         // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the
1429         // stringifier is unforgeable on the interface, and true otherwise."
1430         assert_false("get" in desc, "property has getter");
1431         assert_false("set" in desc, "property has setter");
1432         assert_equals(desc.writable, !stringifierUnforgeable,
1433                       "property should be writable if and only if not unforgeable");
1434         assert_true(desc.enumerable, "property is not enumerable");
1435         assert_equals(desc.configurable, !stringifierUnforgeable,
1436                       "property should be configurable if and only if not unforgeable");
1437         // "The value of the property is a Function object, which behaves as
1438         // follows . . ."
1439         assert_equals(typeof interfacePrototypeObject.toString, "function",
1440                       "property must be a function");
1441         // "The value of the Function object’s “length” property is the Number
1442         // value 0."
1443         assert_equals(interfacePrototypeObject.toString.length, 0,
1444             "property has wrong .length");
1445
1446         // "Let O be the result of calling ToObject on the this value."
1447         assert_throws(new TypeError(), function() {
1448             self[this.name].prototype.toString.apply(null, []);
1449         }, "calling stringifier with this = null didn't throw TypeError");
1450
1451         // "If O is not an object that implements the interface on which the
1452         // stringifier was declared, then throw a TypeError."
1453         //
1454         // TODO: Test a platform object that implements some other
1455         // interface.  (Have to be sure to get inheritance right.)
1456         assert_throws(new TypeError(), function() {
1457             self[this.name].prototype.toString.apply({}, []);
1458         }, "calling stringifier with this = {} didn't throw TypeError");
1459     }.bind(this), this.name + " interface: stringifier");
1460 };
1461
1462 //@}
1463 IdlInterface.prototype.test_members = function()
1464 //@{
1465 {
1466     for (var i = 0; i < this.members.length; i++)
1467     {
1468         var member = this.members[i];
1469         switch (member.type) {
1470         case "iterable":
1471             this.add_iterable_members(member);
1472             break;
1473         // TODO: add setlike and maplike handling.
1474         default:
1475             break;
1476         }
1477     }
1478
1479     for (var i = 0; i < this.members.length; i++)
1480     {
1481         var member = this.members[i];
1482         if (member.untested) {
1483             continue;
1484         }
1485
1486         switch (member.type) {
1487         case "const":
1488             this.test_member_const(member);
1489             break;
1490
1491         case "attribute":
1492             // For unforgeable attributes, we do the checks in
1493             // test_interface_of instead.
1494             if (!member.isUnforgeable)
1495             {
1496                 this.test_member_attribute(member);
1497             }
1498             if (member.stringifier) {
1499                 this.test_member_stringifier(member);
1500             }
1501             break;
1502
1503         case "operation":
1504             // TODO: Need to correctly handle multiple operations with the same
1505             // identifier.
1506             // For unforgeable operations, we do the checks in
1507             // test_interface_of instead.
1508             if (member.name) {
1509                 if (!member.isUnforgeable)
1510                 {
1511                     this.test_member_operation(member);
1512                 }
1513             } else if (member.stringifier) {
1514                 this.test_member_stringifier(member);
1515             }
1516             break;
1517
1518         case "iterable":
1519             this.test_member_iterable(member);
1520             break;
1521         default:
1522             // TODO: check more member types.
1523             break;
1524         }
1525     }
1526 };
1527
1528 //@}
1529 IdlInterface.prototype.test_object = function(desc)
1530 //@{
1531 {
1532     var obj, exception = null;
1533     try
1534     {
1535         obj = eval(desc);
1536     }
1537     catch(e)
1538     {
1539         exception = e;
1540     }
1541
1542     var expected_typeof =
1543         this.members.some(function(member) { return member.legacycaller; })
1544         ? "function"
1545         : "object";
1546
1547     this.test_primary_interface_of(desc, obj, exception, expected_typeof);
1548     var current_interface = this;
1549     while (current_interface)
1550     {
1551         if (!(current_interface.name in this.array.members))
1552         {
1553             throw "Interface " + current_interface.name + " not found (inherited by " + this.name + ")";
1554         }
1555         if (current_interface.prevent_multiple_testing && current_interface.already_tested)
1556         {
1557             return;
1558         }
1559         current_interface.test_interface_of(desc, obj, exception, expected_typeof);
1560         current_interface = this.array.members[current_interface.base];
1561     }
1562 };
1563
1564 //@}
1565 IdlInterface.prototype.test_primary_interface_of = function(desc, obj, exception, expected_typeof)
1566 //@{
1567 {
1568     // We can't easily test that its prototype is correct if there's no
1569     // interface object, or the object is from a different global environment
1570     // (not instanceof Object).  TODO: test in this case that its prototype at
1571     // least looks correct, even if we can't test that it's actually correct.
1572     if (!this.has_extended_attribute("NoInterfaceObject")
1573     && (typeof obj != expected_typeof || obj instanceof Object))
1574     {
1575         test(function()
1576         {
1577             assert_equals(exception, null, "Unexpected exception when evaluating object");
1578             assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1579             assert_own_property(self, this.name,
1580                                 "self does not have own property " + format_value(this.name));
1581             assert_own_property(self[this.name], "prototype",
1582                                 'interface "' + this.name + '" does not have own property "prototype"');
1583
1584             // "The value of the internal [[Prototype]] property of the
1585             // platform object is the interface prototype object of the primary
1586             // interface from the platform object’s associated global
1587             // environment."
1588             assert_equals(Object.getPrototypeOf(obj),
1589                           self[this.name].prototype,
1590                           desc + "'s prototype is not " + this.name + ".prototype");
1591         }.bind(this), this.name + " must be primary interface of " + desc);
1592     }
1593
1594     // "The class string of a platform object that implements one or more
1595     // interfaces must be the identifier of the primary interface of the
1596     // platform object."
1597     test(function()
1598     {
1599         assert_equals(exception, null, "Unexpected exception when evaluating object");
1600         assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1601         assert_class_string(obj, this.name, "class string of " + desc);
1602         if (!this.has_stringifier())
1603         {
1604             assert_equals(String(obj), "[object " + this.name + "]", "String(" + desc + ")");
1605         }
1606     }.bind(this), "Stringification of " + desc);
1607 };
1608
1609 //@}
1610 IdlInterface.prototype.test_interface_of = function(desc, obj, exception, expected_typeof)
1611 //@{
1612 {
1613     // TODO: Indexed and named properties, more checks on interface members
1614     this.already_tested = true;
1615
1616     for (var i = 0; i < this.members.length; i++)
1617     {
1618         var member = this.members[i];
1619         if (member.type == "attribute" && member.isUnforgeable)
1620         {
1621             var a_test = async_test(this.name + " interface: " + desc + ' must have own property "' + member.name + '"');
1622             a_test.step(function() {
1623                 assert_equals(exception, null, "Unexpected exception when evaluating object");
1624                 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1625                 // Call do_interface_attribute_asserts last, since it will call a_test.done()
1626                 this.do_interface_attribute_asserts(obj, member, a_test);
1627             }.bind(this));
1628         }
1629         else if (member.type == "operation" &&
1630                  member.name &&
1631                  member.isUnforgeable)
1632         {
1633             var a_test = async_test(this.name + " interface: " + desc + ' must have own property "' + member.name + '"');
1634             a_test.step(function()
1635             {
1636                 assert_equals(exception, null, "Unexpected exception when evaluating object");
1637                 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1638                 assert_own_property(obj, member.name,
1639                                     "Doesn't have the unforgeable operation property");
1640                 this.do_member_operation_asserts(obj, member, a_test);
1641             }.bind(this));
1642         }
1643         else if ((member.type == "const"
1644         || member.type == "attribute"
1645         || member.type == "operation")
1646         && member.name)
1647         {
1648             test(function()
1649             {
1650                 assert_equals(exception, null, "Unexpected exception when evaluating object");
1651                 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1652                 if (!member["static"]) {
1653                     if (!this.is_global()) {
1654                         assert_inherits(obj, member.name);
1655                     } else {
1656                         assert_own_property(obj, member.name);
1657                     }
1658
1659                     if (member.type == "const")
1660                     {
1661                         assert_equals(obj[member.name], constValue(member.value));
1662                     }
1663                     if (member.type == "attribute")
1664                     {
1665                         // Attributes are accessor properties, so they might
1666                         // legitimately throw an exception rather than returning
1667                         // anything.
1668                         var property, thrown = false;
1669                         try
1670                         {
1671                             property = obj[member.name];
1672                         }
1673                         catch (e)
1674                         {
1675                             thrown = true;
1676                         }
1677                         if (!thrown)
1678                         {
1679                             this.array.assert_type_is(property, member.idlType);
1680                         }
1681                     }
1682                     if (member.type == "operation")
1683                     {
1684                         assert_equals(typeof obj[member.name], "function");
1685                     }
1686                 }
1687             }.bind(this), this.name + " interface: " + desc + ' must inherit property "' + member.name + '" with the proper type (' + i + ')');
1688         }
1689         // TODO: This is wrong if there are multiple operations with the same
1690         // identifier.
1691         // TODO: Test passing arguments of the wrong type.
1692         if (member.type == "operation" && member.name && member.arguments.length)
1693         {
1694             var a_test = async_test( this.name + " interface: calling " + member.name +
1695             "(" + member.arguments.map(function(m) { return m.idlType.idlType; }) +
1696             ") on " + desc + " with too few arguments must throw TypeError");
1697             a_test.step(function()
1698             {
1699                 assert_equals(exception, null, "Unexpected exception when evaluating object");
1700                 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
1701                 if (!member["static"]) {
1702                     if (!this.is_global() && !member.isUnforgeable) {
1703                         assert_inherits(obj, member.name);
1704                     } else {
1705                         assert_own_property(obj, member.name);
1706                     }
1707                 }
1708                 else
1709                 {
1710                     assert_false(member.name in obj);
1711                 }
1712
1713                 var minLength = minOverloadLength(this.members.filter(function(m) {
1714                     return m.type == "operation" && m.name == member.name;
1715                 }));
1716                 var args = [];
1717                 var cb = awaitNCallbacks(minLength, a_test.done.bind(a_test));
1718                 for (var i = 0; i < minLength; i++) {
1719                     throwOrReject(a_test, member, obj[member.name], obj, args,  "Called with " + i + " arguments", cb);
1720
1721                     args.push(create_suitable_object(member.arguments[i].idlType));
1722                 }
1723                 if (minLength === 0) {
1724                     cb();
1725                 }
1726             }.bind(this));
1727         }
1728     }
1729 };
1730
1731 //@}
1732 IdlInterface.prototype.has_stringifier = function()
1733 //@{
1734 {
1735     if (this.members.some(function(member) { return member.stringifier; })) {
1736         return true;
1737     }
1738     if (this.base &&
1739         this.array.members[this.base].has_stringifier()) {
1740         return true;
1741     }
1742     return false;
1743 };
1744
1745 //@}
1746 IdlInterface.prototype.do_interface_attribute_asserts = function(obj, member, a_test)
1747 //@{
1748 {
1749     // This function tests WebIDL as of 2015-01-27.
1750     // TODO: Consider [Exposed].
1751
1752     // This is called by test_member_attribute() with the prototype as obj if
1753     // it is not a global, and the global otherwise, and by test_interface_of()
1754     // with the object as obj.
1755
1756     var pendingPromises = [];
1757
1758     // "For each exposed attribute of the interface, whether it was declared on
1759     // the interface itself or one of its consequential interfaces, there MUST
1760     // exist a corresponding property. The characteristics of this property are
1761     // as follows:"
1762
1763     // "The name of the property is the identifier of the attribute."
1764     assert_own_property(obj, member.name);
1765
1766     // "The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
1767     // true, [[Configurable]]: configurable }, where:
1768     // "configurable is false if the attribute was declared with the
1769     // [Unforgeable] extended attribute and true otherwise;
1770     // "G is the attribute getter, defined below; and
1771     // "S is the attribute setter, also defined below."
1772     var desc = Object.getOwnPropertyDescriptor(obj, member.name);
1773     assert_false("value" in desc, 'property descriptor has value but is supposed to be accessor');
1774     assert_false("writable" in desc, 'property descriptor has "writable" field but is supposed to be accessor');
1775     assert_true(desc.enumerable, "property is not enumerable");
1776     if (member.isUnforgeable)
1777     {
1778         assert_false(desc.configurable, "[Unforgeable] property must not be configurable");
1779     }
1780     else
1781     {
1782         assert_true(desc.configurable, "property must be configurable");
1783     }
1784
1785
1786     // "The attribute getter is a Function object whose behavior when invoked
1787     // is as follows:"
1788     assert_equals(typeof desc.get, "function", "getter must be Function");
1789
1790     // "If the attribute is a regular attribute, then:"
1791     if (!member["static"]) {
1792         // "If O is not a platform object that implements I, then:
1793         // "If the attribute was specified with the [LenientThis] extended
1794         // attribute, then return undefined.
1795         // "Otherwise, throw a TypeError."
1796         if (!member.has_extended_attribute("LenientThis")) {
1797             if (member.idlType.generic !== "Promise") {
1798                 assert_throws(new TypeError(), function() {
1799                     desc.get.call({});
1800                 }.bind(this), "calling getter on wrong object type must throw TypeError");
1801             } else {
1802                 pendingPromises.push(
1803                     promise_rejects(a_test, new TypeError(), desc.get.call({}),
1804                                     "calling getter on wrong object type must reject the return promise with TypeError"));
1805             }
1806         } else {
1807             assert_equals(desc.get.call({}), undefined,
1808                           "calling getter on wrong object type must return undefined");
1809         }
1810     }
1811
1812     // "The value of the Function object’s “length” property is the Number
1813     // value 0."
1814     assert_equals(desc.get.length, 0, "getter length must be 0");
1815
1816
1817     // TODO: Test calling setter on the interface prototype (should throw
1818     // TypeError in most cases).
1819     if (member.readonly
1820     && !member.has_extended_attribute("PutForwards")
1821     && !member.has_extended_attribute("Replaceable"))
1822     {
1823         // "The attribute setter is undefined if the attribute is declared
1824         // readonly and has neither a [PutForwards] nor a [Replaceable]
1825         // extended attribute declared on it."
1826         assert_equals(desc.set, undefined, "setter must be undefined for readonly attributes");
1827     }
1828     else
1829     {
1830         // "Otherwise, it is a Function object whose behavior when
1831         // invoked is as follows:"
1832         assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes");
1833
1834         // "If the attribute is a regular attribute, then:"
1835         if (!member["static"]) {
1836             // "If /validThis/ is false and the attribute was not specified
1837             // with the [LenientThis] extended attribute, then throw a
1838             // TypeError."
1839             // "If the attribute is declared with a [Replaceable] extended
1840             // attribute, then: ..."
1841             // "If validThis is false, then return."
1842             if (!member.has_extended_attribute("LenientThis")) {
1843                 assert_throws(new TypeError(), function() {
1844                     desc.set.call({});
1845                 }.bind(this), "calling setter on wrong object type must throw TypeError");
1846             } else {
1847                 assert_equals(desc.set.call({}), undefined,
1848                               "calling setter on wrong object type must return undefined");
1849             }
1850         }
1851
1852         // "The value of the Function object’s “length” property is the Number
1853         // value 1."
1854         assert_equals(desc.set.length, 1, "setter length must be 1");
1855     }
1856
1857     Promise.all(pendingPromises).then(a_test.done.bind(a_test));
1858 }
1859 //@}
1860
1861 /// IdlInterfaceMember ///
1862 function IdlInterfaceMember(obj)
1863 //@{
1864 {
1865     /**
1866      * obj is an object produced by the WebIDLParser.js "ifMember" production.
1867      * We just forward all properties to this object without modification,
1868      * except for special extAttrs handling.
1869      */
1870     for (var k in obj)
1871     {
1872         this[k] = obj[k];
1873     }
1874     if (!("extAttrs" in this))
1875     {
1876         this.extAttrs = [];
1877     }
1878
1879     this.isUnforgeable = this.has_extended_attribute("Unforgeable");
1880 }
1881
1882 //@}
1883 IdlInterfaceMember.prototype = Object.create(IdlObject.prototype);
1884
1885 /// Internal helper functions ///
1886 function create_suitable_object(type)
1887 //@{
1888 {
1889     /**
1890      * type is an object produced by the WebIDLParser.js "type" production.  We
1891      * return a JavaScript value that matches the type, if we can figure out
1892      * how.
1893      */
1894     if (type.nullable)
1895     {
1896         return null;
1897     }
1898     switch (type.idlType)
1899     {
1900         case "any":
1901         case "boolean":
1902             return true;
1903
1904         case "byte": case "octet": case "short": case "unsigned short":
1905         case "long": case "unsigned long": case "long long":
1906         case "unsigned long long": case "float": case "double":
1907         case "unrestricted float": case "unrestricted double":
1908             return 7;
1909
1910         case "DOMString":
1911         case "ByteString":
1912         case "USVString":
1913             return "foo";
1914
1915         case "object":
1916             return {a: "b"};
1917
1918         case "Node":
1919             return document.createTextNode("abc");
1920     }
1921     return null;
1922 }
1923 //@}
1924
1925 /// IdlEnum ///
1926 // Used for IdlArray.prototype.assert_type_is
1927 function IdlEnum(obj)
1928 //@{
1929 {
1930     /**
1931      * obj is an object produced by the WebIDLParser.js "dictionary"
1932      * production.
1933      */
1934
1935     /** Self-explanatory. */
1936     this.name = obj.name;
1937
1938     /** An array of values produced by the "enum" production. */
1939     this.values = obj.values;
1940
1941 }
1942 //@}
1943
1944 IdlEnum.prototype = Object.create(IdlObject.prototype);
1945
1946 /// IdlTypedef ///
1947 // Used for IdlArray.prototype.assert_type_is
1948 function IdlTypedef(obj)
1949 //@{
1950 {
1951     /**
1952      * obj is an object produced by the WebIDLParser.js "typedef"
1953      * production.
1954      */
1955
1956     /** Self-explanatory. */
1957     this.name = obj.name;
1958
1959     /** An array of values produced by the "typedef" production. */
1960     this.values = obj.values;
1961
1962 }
1963 //@}
1964
1965 IdlTypedef.prototype = Object.create(IdlObject.prototype);
1966
1967 }());
1968 // vim: set expandtab shiftwidth=4 tabstop=4 foldmarker=@{,@} foldmethod=marker: