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