c3045c49f909c9514597a6d6f8a9a3f7fe0f4df2
[WebKit-https.git] / LayoutTests / imported / w3c / web-platform-tests / 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/_writing-tests/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     if (cnt.type === "number") return +cnt.value;
59     return cnt.value;
60 }
61
62 //@}
63 function minOverloadLength(overloads)
64 //@{
65 {
66     if (!overloads.length) {
67         return 0;
68     }
69
70     return overloads.map(function(attr) {
71         return attr.arguments ? attr.arguments.filter(function(arg) {
72             return !arg.optional && !arg.variadic;
73         }).length : 0;
74     })
75     .reduce(function(m, n) { return Math.min(m, n); });
76 }
77
78 //@}
79 function throwOrReject(a_test, operation, fn, obj, args, message, cb)
80 //@{
81 {
82     if (operation.idlType.generic !== "Promise") {
83         assert_throws(new TypeError(), function() {
84             fn.apply(obj, args);
85         }, message);
86         cb();
87     } else {
88         try {
89             promise_rejects(a_test, new TypeError(), fn.apply(obj, args), message).then(cb, cb);
90         } catch (e){
91             a_test.step(function() {
92                 assert_unreached("Throws \"" + e + "\" instead of rejecting promise");
93                 cb();
94             });
95         }
96     }
97 }
98
99 //@}
100 function awaitNCallbacks(n, cb, ctx)
101 //@{
102 {
103     var counter = 0;
104     return function() {
105         counter++;
106         if (counter >= n) {
107             cb();
108         }
109     };
110 }
111
112 //@}
113 var fround =
114 //@{
115 (function(){
116     if (Math.fround) return Math.fround;
117
118     var arr = new Float32Array(1);
119     return function fround(n) {
120         arr[0] = n;
121         return arr[0];
122     };
123 })();
124 //@}
125
126 /// IdlHarnessError ///
127 // Entry point
128 self.IdlHarnessError = function(message)
129 //@{
130 {
131     /**
132      * Message to be printed as the error's toString invocation.
133      */
134     this.message = message;
135 };
136
137 IdlHarnessError.prototype = Object.create(Error.prototype);
138
139 //@}
140 IdlHarnessError.prototype.toString = function()
141 //@{
142 {
143     return this.message;
144 };
145
146 //@}
147
148 /// IdlArray ///
149 // Entry point
150 self.IdlArray = function()
151 //@{
152 {
153     /**
154      * A map from strings to the corresponding named IdlObject, such as
155      * IdlInterface or IdlException.  These are the things that test() will run
156      * tests on.
157      */
158     this.members = {};
159
160     /**
161      * A map from strings to arrays of strings.  The keys are interface or
162      * exception names, and are expected to also exist as keys in this.members
163      * (otherwise they'll be ignored).  This is populated by add_objects() --
164      * see documentation at the start of the file.  The actual tests will be
165      * run by calling this.members[name].test_object(obj) for each obj in
166      * this.objects[name].  obj is a string that will be eval'd to produce a
167      * JavaScript value, which is supposed to be an object implementing the
168      * given IdlObject (interface, exception, etc.).
169      */
170     this.objects = {};
171
172     /**
173      * When adding multiple collections of IDLs one at a time, an earlier one
174      * might contain a partial interface or implements statement that depends
175      * on a later one.  Save these up and handle them right before we run
176      * tests.
177      *
178      * .partials is simply an array of objects from WebIDLParser.js'
179      * "partialinterface" production.  .implements maps strings to arrays of
180      * strings, such that
181      *
182      *   A implements B;
183      *   A implements C;
184      *   D implements E;
185      *
186      * results in { A: ["B", "C"], D: ["E"] }.
187      */
188     this.partials = [];
189     this["implements"] = {};
190     this["includes"] = {};
191 };
192
193 //@}
194 IdlArray.prototype.add_idls = function(raw_idls, options)
195 //@{
196 {
197     /** Entry point.  See documentation at beginning of file. */
198     this.internal_add_idls(WebIDL2.parse(raw_idls), options);
199 };
200
201 //@}
202 IdlArray.prototype.add_untested_idls = function(raw_idls, options)
203 //@{
204 {
205     /** Entry point.  See documentation at beginning of file. */
206     var parsed_idls = WebIDL2.parse(raw_idls);
207     for (var i = 0; i < parsed_idls.length; i++)
208     {
209         parsed_idls[i].untested = true;
210         if ("members" in parsed_idls[i])
211         {
212             for (var j = 0; j < parsed_idls[i].members.length; j++)
213             {
214                 parsed_idls[i].members[j].untested = true;
215             }
216         }
217     }
218     this.internal_add_idls(parsed_idls, options);
219 };
220
221 //@}
222 IdlArray.prototype.internal_add_idls = function(parsed_idls, options)
223 //@{
224 {
225     /**
226      * Internal helper called by add_idls() and add_untested_idls().
227      *
228      * parsed_idls is an array of objects that come from WebIDLParser.js's
229      * "definitions" production.  The add_untested_idls() entry point
230      * additionally sets an .untested property on each object (and its
231      * .members) so that they'll be skipped by test() -- they'll only be
232      * used for base interfaces of tested interfaces, return types, etc.
233      *
234      * options is a dictionary that can have an only or except member which are
235      * arrays. If only is given then only members, partials and interface
236      * targets listed will be added, and if except is given only those that
237      * aren't listed will be added. Only one of only and except can be used.
238      */
239
240     if (options && options.only && options.except)
241     {
242         throw new IdlHarnessError("The only and except options can't be used together.");
243     }
244
245     function should_skip(name)
246     {
247         if (options && options.only && options.only.indexOf(name) == -1)
248         {
249             return true;
250         }
251         if (options && options.except && options.except.indexOf(name) != -1)
252         {
253             return true;
254         }
255         return false;
256     }
257
258     parsed_idls.forEach(function(parsed_idl)
259     {
260         if (parsed_idl.partial && ["interface", "dictionary"].includes(parsed_idl.type))
261         {
262             if (should_skip(parsed_idl.name))
263             {
264                 return;
265             }
266             this.partials.push(parsed_idl);
267             return;
268         }
269
270         if (parsed_idl.type == "implements")
271         {
272             if (should_skip(parsed_idl.target))
273             {
274                 return;
275             }
276             if (!(parsed_idl.target in this["implements"]))
277             {
278                 this["implements"][parsed_idl.target] = [];
279             }
280             this["implements"][parsed_idl.target].push(parsed_idl["implements"]);
281             return;
282         }
283
284         if (parsed_idl.type == "includes")
285         {
286             if (should_skip(parsed_idl.target))
287             {
288                 return;
289             }
290             if (!(parsed_idl.target in this["includes"]))
291             {
292                 this["includes"][parsed_idl.target] = [];
293             }
294             this["includes"][parsed_idl.target].push(parsed_idl["includes"]);
295             return;
296         }
297
298         parsed_idl.array = this;
299         if (should_skip(parsed_idl.name))
300         {
301             return;
302         }
303         if (parsed_idl.name in this.members)
304         {
305             throw new IdlHarnessError("Duplicate identifier " + parsed_idl.name);
306         }
307         switch(parsed_idl.type)
308         {
309         case "interface":
310             this.members[parsed_idl.name] =
311                 new IdlInterface(parsed_idl, /* is_callback = */ false, /* is_mixin = */ false);
312             break;
313
314         case "interface mixin":
315             this.members[parsed_idl.name] =
316                 new IdlInterface(parsed_idl, /* is_callback = */ false, /* is_mixin = */ true);
317             break;
318
319         case "dictionary":
320             // Nothing to test, but we need the dictionary info around for type
321             // checks
322             this.members[parsed_idl.name] = new IdlDictionary(parsed_idl);
323             break;
324
325         case "typedef":
326             this.members[parsed_idl.name] = new IdlTypedef(parsed_idl);
327             break;
328
329         case "callback":
330             // TODO
331             console.log("callback not yet supported");
332             break;
333
334         case "enum":
335             this.members[parsed_idl.name] = new IdlEnum(parsed_idl);
336             break;
337
338         case "callback interface":
339             this.members[parsed_idl.name] =
340                 new IdlInterface(parsed_idl, /* is_callback = */ true, /* is_mixin = */ false);
341             break;
342
343         default:
344             throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported";
345         }
346     }.bind(this));
347 };
348
349 //@}
350 IdlArray.prototype.add_objects = function(dict)
351 //@{
352 {
353     /** Entry point.  See documentation at beginning of file. */
354     for (var k in dict)
355     {
356         if (k in this.objects)
357         {
358             this.objects[k] = this.objects[k].concat(dict[k]);
359         }
360         else
361         {
362             this.objects[k] = dict[k];
363         }
364     }
365 };
366
367 //@}
368 IdlArray.prototype.prevent_multiple_testing = function(name)
369 //@{
370 {
371     /** Entry point.  See documentation at beginning of file. */
372     this.members[name].prevent_multiple_testing = true;
373 };
374
375 //@}
376 IdlArray.prototype.recursively_get_implements = function(interface_name)
377 //@{
378 {
379     /**
380      * Helper function for test().  Returns an array of things that implement
381      * interface_name, so if the IDL contains
382      *
383      *   A implements B;
384      *   B implements C;
385      *   B implements D;
386      *
387      * then recursively_get_implements("A") should return ["B", "C", "D"].
388      */
389     var ret = this["implements"][interface_name];
390     if (ret === undefined)
391     {
392         return [];
393     }
394     for (var i = 0; i < this["implements"][interface_name].length; i++)
395     {
396         ret = ret.concat(this.recursively_get_implements(ret[i]));
397         if (ret.indexOf(ret[i]) != ret.lastIndexOf(ret[i]))
398         {
399             throw new IdlHarnessError("Circular implements statements involving " + ret[i]);
400         }
401     }
402     return ret;
403 };
404
405 //@}
406 IdlArray.prototype.recursively_get_includes = function(interface_name)
407 //@{
408 {
409     /**
410      * Helper function for test().  Returns an array of things that implement
411      * interface_name, so if the IDL contains
412      *
413      *   A includes B;
414      *   B includes C;
415      *   B includes D;
416      *
417      * then recursively_get_includes("A") should return ["B", "C", "D"].
418      */
419     var ret = this["includes"][interface_name];
420     if (ret === undefined)
421     {
422         return [];
423     }
424     for (var i = 0; i < this["includes"][interface_name].length; i++)
425     {
426         ret = ret.concat(this.recursively_get_includes(ret[i]));
427         if (ret.indexOf(ret[i]) != ret.lastIndexOf(ret[i]))
428         {
429             throw new IdlHarnessError("Circular includes statements involving " + ret[i]);
430         }
431     }
432     return ret;
433 };
434
435 //@}
436 IdlArray.prototype.is_json_type = function(type)
437 //@{
438 {
439     /**
440      * Checks whether type is a JSON type as per
441      * https://heycam.github.io/webidl/#dfn-json-types
442      */
443
444     var idlType = type.idlType;
445
446     if (type.generic == "Promise") { return false; }
447
448     //  nullable and annotated types don't need to be handled separately,
449     //  as webidl2 doesn't represent them wrapped-up (as they're described
450     //  in WebIDL).
451
452     // union and record types
453     if (type.union || type.generic == "record") {
454         return idlType.every(this.is_json_type, this);
455     }
456
457     // sequence types
458     if (type.generic == "sequence" || type.generic == "FrozenArray") {
459         return this.is_json_type(idlType);
460     }
461
462     if (typeof idlType != "string") { throw new Error("Unexpected type " + JSON.stringify(idlType)); }
463
464     switch (idlType)
465     {
466        //  Numeric types
467        case "byte":
468        case "octet":
469        case "short":
470        case "unsigned short":
471        case "long":
472        case "unsigned long":
473        case "long long":
474        case "unsigned long long":
475        case "float":
476        case "double":
477        case "unrestricted float":
478        case "unrestricted double":
479        // boolean
480        case "boolean":
481        // string types
482        case "DOMString":
483        case "ByteString":
484        case "USVString":
485        // object type
486        case "object":
487            return true;
488        case "Error":
489        case "DOMException":
490        case "Int8Array":
491        case "Int16Array":
492        case "Int32Array":
493        case "Uint8Array":
494        case "Uint16Array":
495        case "Uint32Array":
496        case "Uint8ClampedArray":
497        case "Float32Array":
498        case "ArrayBuffer":
499        case "DataView":
500        case "any":
501            return false;
502        default:
503            var thing = this.members[idlType];
504            if (!thing) { throw new Error("Type " + idlType + " not found"); }
505            if (thing instanceof IdlEnum) { return true; }
506
507            if (thing instanceof IdlTypedef) {
508                return this.is_json_type(thing.idlType);
509            }
510
511            //  dictionaries where all of their members are JSON types
512            if (thing instanceof IdlDictionary) {
513                var stack = thing.get_inheritance_stack();
514                var map = new Map();
515                while (stack.length)
516                {
517                    stack.pop().members.forEach(function(m) {
518                        map.set(m.name, m.idlType)
519                    });
520                }
521                return Array.from(map.values()).every(this.is_json_type, this);
522            }
523
524            //  interface types that have a toJSON operation declared on themselves or
525            //  one of their inherited or consequential interfaces.
526            if (thing instanceof IdlInterface) {
527                var base;
528                while (thing)
529                {
530                    if (thing.has_to_json_regular_operation()) { return true; }
531                    var mixins = this.implements[thing.name] || this.includes[thing.name];
532                    if (mixins) {
533                        mixins = mixins.map(function(id) {
534                            var mixin = this.members[id];
535                            if (!mixin) {
536                                throw new Error("Interface " + id + " not found (implemented by " + thing.name + ")");
537                            }
538                            return mixin;
539                        }, this);
540                        if (mixins.some(function(m) { return m.has_to_json_regular_operation() } )) { return true; }
541                    }
542                    if (!thing.base) { return false; }
543                    base = this.members[thing.base];
544                    if (!base) {
545                        throw new Error("Interface " + thing.base + " not found (inherited by " + thing.name + ")");
546                    }
547                    thing = base;
548                }
549                return false;
550            }
551            return false;
552     }
553 };
554
555 function exposure_set(object, default_set) {
556     var exposed = object.extAttrs.filter(function(a) { return a.name == "Exposed" });
557     if (exposed.length > 1 || exposed.length < 0) {
558         throw new IdlHarnessError("Unexpected Exposed extended attributes on " + memberName + ": " + exposed);
559     }
560
561     if (exposed.length === 0) {
562         return default_set;
563     }
564
565     var set = exposed[0].rhs.value;
566     // Could be a list or a string.
567     if (typeof set == "string") {
568         set = [ set ];
569     }
570     return set;
571 }
572
573 function exposed_in(globals) {
574     if ('document' in self) {
575         return globals.indexOf("Window") >= 0;
576     }
577     if ('DedicatedWorkerGlobalScope' in self &&
578         self instanceof DedicatedWorkerGlobalScope) {
579         return globals.indexOf("Worker") >= 0 ||
580                globals.indexOf("DedicatedWorker") >= 0;
581     }
582     if ('SharedWorkerGlobalScope' in self &&
583         self instanceof SharedWorkerGlobalScope) {
584         return globals.indexOf("Worker") >= 0 ||
585                globals.indexOf("SharedWorker") >= 0;
586     }
587     if ('ServiceWorkerGlobalScope' in self &&
588         self instanceof ServiceWorkerGlobalScope) {
589         return globals.indexOf("Worker") >= 0 ||
590                globals.indexOf("ServiceWorker") >= 0;
591     }
592     throw new IdlHarnessError("Unexpected global object");
593 }
594
595 //@}
596 /**
597  * Asserts that the given error message is thrown for the given function.
598  * @param {string|IdlHarnessError} error Expected Error message.
599  * @param {Function} idlArrayFunc Function operating on an IdlArray that should throw.
600  */
601 IdlArray.prototype.assert_throws = function(error, idlArrayFunc)
602 //@{
603 {
604     try {
605         idlArrayFunc.call(this, this);
606     } catch (e) {
607         if (e instanceof AssertionError) {
608             throw e;
609         }
610         // Assertions for behaviour of the idlharness.js engine.
611         if (error instanceof IdlHarnessError) {
612             error = error.message;
613         }
614         if (e.message !== error) {
615             throw new IdlHarnessError(`${idlArrayFunc} threw "${e}", not the expected IdlHarnessError "${error}"`);
616         }
617         return;
618     }
619     throw new IdlHarnessError(`${idlArrayFunc} did not throw the expected IdlHarnessError`);
620 }
621
622 //@}
623 IdlArray.prototype.test = function()
624 //@{
625 {
626     /** Entry point.  See documentation at beginning of file. */
627
628     // First merge in all the partial interfaces and implements statements we
629     // encountered.
630     this.partials.forEach(function(parsed_idl)
631     {
632         if (!(parsed_idl.name in this.members)
633             || !(this.members[parsed_idl.name] instanceof IdlInterface
634                  || this.members[parsed_idl.name] instanceof IdlDictionary))
635         {
636             throw new IdlHarnessError(`Partial ${parsed_idl.type} ${parsed_idl.name} with no original ${parsed_idl.type}`);
637         }
638         if (parsed_idl.extAttrs)
639         {
640             parsed_idl.extAttrs.forEach(function(extAttr)
641             {
642                 this.members[parsed_idl.name].extAttrs.push(extAttr);
643             }.bind(this));
644         }
645         parsed_idl.members.forEach(function(member)
646         {
647             this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member));
648         }.bind(this));
649     }.bind(this));
650     this.partials = [];
651
652     for (var lhs in this["implements"])
653     {
654         this.recursively_get_implements(lhs).forEach(function(rhs)
655         {
656             var errStr = lhs + " implements " + rhs + ", but ";
657             if (!(lhs in this.members)) throw errStr + lhs + " is undefined.";
658             if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface.";
659             if (!(rhs in this.members)) throw errStr + rhs + " is undefined.";
660             if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface.";
661             this.members[rhs].members.forEach(function(member)
662             {
663                 this.members[lhs].members.push(new IdlInterfaceMember(member));
664             }.bind(this));
665         }.bind(this));
666     }
667     this["implements"] = {};
668
669     for (var lhs in this["includes"])
670     {
671         this.recursively_get_includes(lhs).forEach(function(rhs)
672         {
673             var errStr = lhs + " includes " + rhs + ", but ";
674             if (!(lhs in this.members)) throw errStr + lhs + " is undefined.";
675             if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface.";
676             if (!(rhs in this.members)) throw errStr + rhs + " is undefined.";
677             if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface.";
678             this.members[rhs].members.forEach(function(member)
679             {
680                 this.members[lhs].members.push(new IdlInterfaceMember(member));
681             }.bind(this));
682         }.bind(this));
683     }
684     this["includes"] = {};
685
686     Object.getOwnPropertyNames(this.members).forEach(function(memberName) {
687         var member = this.members[memberName];
688         if (!(member instanceof IdlInterface)) {
689             return;
690         }
691
692         var globals = exposure_set(member, ["Window"]);
693         member.exposed = exposed_in(globals);
694         member.exposureSet = globals;
695     }.bind(this));
696
697     // Now run test() on every member, and test_object() for every object.
698     for (var name in this.members)
699     {
700         this.members[name].test();
701         if (name in this.objects)
702         {
703             this.objects[name].forEach(function(str)
704             {
705                 this.members[name].test_object(str);
706             }.bind(this));
707         }
708     }
709 };
710
711 //@}
712 IdlArray.prototype.assert_type_is = function(value, type)
713 //@{
714 {
715     if (type.idlType in this.members
716     && this.members[type.idlType] instanceof IdlTypedef) {
717         this.assert_type_is(value, this.members[type.idlType].idlType);
718         return;
719     }
720     if (type.union) {
721         for (var i = 0; i < type.idlType.length; i++) {
722             try {
723                 this.assert_type_is(value, type.idlType[i]);
724                 // No AssertionError, so we match one type in the union
725                 return;
726             } catch(e) {
727                 if (e instanceof AssertionError) {
728                     // We didn't match this type, let's try some others
729                     continue;
730                 }
731                 throw e;
732             }
733         }
734         // TODO: Is there a nice way to list the union's types in the message?
735         assert_true(false, "Attribute has value " + format_value(value)
736                     + " which doesn't match any of the types in the union");
737
738     }
739
740     /**
741      * Helper function that tests that value is an instance of type according
742      * to the rules of WebIDL.  value is any JavaScript value, and type is an
743      * object produced by WebIDLParser.js' "type" production.  That production
744      * is fairly elaborate due to the complexity of WebIDL's types, so it's
745      * best to look at the grammar to figure out what properties it might have.
746      */
747     if (type.idlType == "any")
748     {
749         // No assertions to make
750         return;
751     }
752
753     if (type.nullable && value === null)
754     {
755         // This is fine
756         return;
757     }
758
759     if (type.array)
760     {
761         // TODO: not supported yet
762         return;
763     }
764
765     if (type.sequence)
766     {
767         assert_true(Array.isArray(value), "should be an Array");
768         if (!value.length)
769         {
770             // Nothing we can do.
771             return;
772         }
773         this.assert_type_is(value[0], type.idlType);
774         return;
775     }
776
777     if (type.generic === "Promise") {
778         assert_true("then" in value, "Attribute with a Promise type should have a then property");
779         // TODO: Ideally, we would check on project fulfillment
780         // that we get the right type
781         // but that would require making the type check async
782         return;
783     }
784
785     if (type.generic === "FrozenArray") {
786         assert_true(Array.isArray(value), "Value should be array");
787         assert_true(Object.isFrozen(value), "Value should be frozen");
788         if (!value.length)
789         {
790             // Nothing we can do.
791             return;
792         }
793         this.assert_type_is(value[0], type.idlType);
794         return;
795     }
796
797     type = type.idlType;
798
799     switch(type)
800     {
801         case "void":
802             assert_equals(value, undefined);
803             return;
804
805         case "boolean":
806             assert_equals(typeof value, "boolean");
807             return;
808
809         case "byte":
810             assert_equals(typeof value, "number");
811             assert_equals(value, Math.floor(value), "should be an integer");
812             assert_true(-128 <= value && value <= 127, "byte " + value + " should be in range [-128, 127]");
813             return;
814
815         case "octet":
816             assert_equals(typeof value, "number");
817             assert_equals(value, Math.floor(value), "should be an integer");
818             assert_true(0 <= value && value <= 255, "octet " + value + " should be in range [0, 255]");
819             return;
820
821         case "short":
822             assert_equals(typeof value, "number");
823             assert_equals(value, Math.floor(value), "should be an integer");
824             assert_true(-32768 <= value && value <= 32767, "short " + value + " should be in range [-32768, 32767]");
825             return;
826
827         case "unsigned short":
828             assert_equals(typeof value, "number");
829             assert_equals(value, Math.floor(value), "should be an integer");
830             assert_true(0 <= value && value <= 65535, "unsigned short " + value + " should be in range [0, 65535]");
831             return;
832
833         case "long":
834             assert_equals(typeof value, "number");
835             assert_equals(value, Math.floor(value), "should be an integer");
836             assert_true(-2147483648 <= value && value <= 2147483647, "long " + value + " should be in range [-2147483648, 2147483647]");
837             return;
838
839         case "unsigned long":
840             assert_equals(typeof value, "number");
841             assert_equals(value, Math.floor(value), "should be an integer");
842             assert_true(0 <= value && value <= 4294967295, "unsigned long " + value + " should be in range [0, 4294967295]");
843             return;
844
845         case "long long":
846             assert_equals(typeof value, "number");
847             return;
848
849         case "unsigned long long":
850         case "DOMTimeStamp":
851             assert_equals(typeof value, "number");
852             assert_true(0 <= value, "unsigned long long should be positive");
853             return;
854
855         case "float":
856             assert_equals(typeof value, "number");
857             assert_equals(value, fround(value), "float rounded to 32-bit float should be itself");
858             assert_not_equals(value, Infinity);
859             assert_not_equals(value, -Infinity);
860             assert_not_equals(value, NaN);
861             return;
862
863         case "DOMHighResTimeStamp":
864         case "double":
865             assert_equals(typeof value, "number");
866             assert_not_equals(value, Infinity);
867             assert_not_equals(value, -Infinity);
868             assert_not_equals(value, NaN);
869             return;
870
871         case "unrestricted float":
872             assert_equals(typeof value, "number");
873             assert_equals(value, fround(value), "unrestricted float rounded to 32-bit float should be itself");
874             return;
875
876         case "unrestricted double":
877             assert_equals(typeof value, "number");
878             return;
879
880         case "DOMString":
881             assert_equals(typeof value, "string");
882             return;
883
884         case "ByteString":
885             assert_equals(typeof value, "string");
886             assert_regexp_match(value, /^[\x00-\x7F]*$/);
887             return;
888
889         case "USVString":
890             assert_equals(typeof value, "string");
891             assert_regexp_match(value, /^([\x00-\ud7ff\ue000-\uffff]|[\ud800-\udbff][\udc00-\udfff])*$/);
892             return;
893
894         case "object":
895             assert_in_array(typeof value, ["object", "function"], "wrong type: not object or function");
896             return;
897     }
898
899     if (!(type in this.members))
900     {
901         throw new IdlHarnessError("Unrecognized type " + type);
902     }
903
904     if (this.members[type] instanceof IdlInterface)
905     {
906         // We don't want to run the full
907         // IdlInterface.prototype.test_instance_of, because that could result
908         // in an infinite loop.  TODO: This means we don't have tests for
909         // NoInterfaceObject interfaces, and we also can't test objects that
910         // come from another self.
911         assert_in_array(typeof value, ["object", "function"], "wrong type: not object or function");
912         if (value instanceof Object
913         && !this.members[type].has_extended_attribute("NoInterfaceObject")
914         && type in self)
915         {
916             assert_true(value instanceof self[type], "instanceof " + type);
917         }
918     }
919     else if (this.members[type] instanceof IdlEnum)
920     {
921         assert_equals(typeof value, "string");
922     }
923     else if (this.members[type] instanceof IdlDictionary)
924     {
925         // TODO: Test when we actually have something to test this on
926     }
927     else
928     {
929         throw new IdlHarnessError("Type " + type + " isn't an interface or dictionary");
930     }
931 };
932 //@}
933
934 /// IdlObject ///
935 function IdlObject() {}
936 IdlObject.prototype.test = function()
937 //@{
938 {
939     /**
940      * By default, this does nothing, so no actual tests are run for IdlObjects
941      * that don't define any (e.g., IdlDictionary at the time of this writing).
942      */
943 };
944
945 //@}
946 IdlObject.prototype.has_extended_attribute = function(name)
947 //@{
948 {
949     /**
950      * This is only meaningful for things that support extended attributes,
951      * such as interfaces, exceptions, and members.
952      */
953     return this.extAttrs.some(function(o)
954     {
955         return o.name == name;
956     });
957 };
958
959 //@}
960
961 /// IdlDictionary ///
962 // Used for IdlArray.prototype.assert_type_is
963 function IdlDictionary(obj)
964 //@{
965 {
966     /**
967      * obj is an object produced by the WebIDLParser.js "dictionary"
968      * production.
969      */
970
971     /** Self-explanatory. */
972     this.name = obj.name;
973
974     /** A back-reference to our IdlArray. */
975     this.array = obj.array;
976
977     /** An array of objects produced by the "dictionaryMember" production. */
978     this.members = obj.members;
979
980     /**
981      * The name (as a string) of the dictionary type we inherit from, or null
982      * if there is none.
983      */
984     this.base = obj.inheritance;
985 }
986
987 //@}
988 IdlDictionary.prototype = Object.create(IdlObject.prototype);
989
990 IdlDictionary.prototype.get_inheritance_stack = function() {
991     return IdlInterface.prototype.get_inheritance_stack.call(this);
992 };
993
994 /// IdlInterface ///
995 function IdlInterface(obj, is_callback, is_mixin)
996 //@{
997 {
998     /**
999      * obj is an object produced by the WebIDLParser.js "interface" production.
1000      */
1001
1002     /** Self-explanatory. */
1003     this.name = obj.name;
1004
1005     /** A back-reference to our IdlArray. */
1006     this.array = obj.array;
1007
1008     /**
1009      * An indicator of whether we should run tests on the interface object and
1010      * interface prototype object. Tests on members are controlled by .untested
1011      * on each member, not this.
1012      */
1013     this.untested = obj.untested;
1014
1015     /** An array of objects produced by the "ExtAttr" production. */
1016     this.extAttrs = obj.extAttrs;
1017
1018     /** An array of IdlInterfaceMembers. */
1019     this.members = obj.members.map(function(m){return new IdlInterfaceMember(m); });
1020     if (this.has_extended_attribute("Unforgeable")) {
1021         this.members
1022             .filter(function(m) { return !m["static"] && (m.type == "attribute" || m.type == "operation"); })
1023             .forEach(function(m) { return m.isUnforgeable = true; });
1024     }
1025
1026     /**
1027      * The name (as a string) of the type we inherit from, or null if there is
1028      * none.
1029      */
1030     this.base = obj.inheritance;
1031
1032     this._is_callback = is_callback;
1033     this._is_mixin = is_mixin;
1034 }
1035 //@}
1036 IdlInterface.prototype = Object.create(IdlObject.prototype);
1037 IdlInterface.prototype.is_callback = function()
1038 //@{
1039 {
1040     return this._is_callback;
1041 };
1042 //@}
1043
1044 IdlInterface.prototype.is_mixin = function()
1045 //@{
1046 {
1047     return this._is_mixin;
1048 };
1049 //@}
1050
1051 IdlInterface.prototype.has_constants = function()
1052 //@{
1053 {
1054     return this.members.some(function(member) {
1055         return member.type === "const";
1056     });
1057 };
1058 //@}
1059
1060 IdlInterface.prototype.get_unscopables = function()
1061 {
1062     return this.members.filter(function(member) {
1063         return member.isUnscopable;
1064     });
1065 };
1066
1067 IdlInterface.prototype.is_global = function()
1068 //@{
1069 {
1070     return this.extAttrs.some(function(attribute) {
1071         return attribute.name === "Global";
1072     });
1073 };
1074 //@}
1075
1076 IdlInterface.prototype.has_to_json_regular_operation = function() {
1077     return this.members.some(function(m) {
1078         return m.is_to_json_regular_operation();
1079     });
1080 };
1081
1082 IdlInterface.prototype.has_default_to_json_regular_operation = function() {
1083     return this.members.some(function(m) {
1084         return m.is_to_json_regular_operation() && m.has_extended_attribute("Default");
1085     });
1086 };
1087
1088 IdlInterface.prototype.get_inheritance_stack = function() {
1089     /**
1090      * See https://heycam.github.io/webidl/#create-an-inheritance-stack
1091      *
1092      * Returns an array of IdlInterface objects which contains itself
1093      * and all of its inherited interfaces.
1094      *
1095      * So given:
1096      *
1097      *   A : B {};
1098      *   B : C {};
1099      *   C {};
1100      *
1101      * then A.get_inheritance_stack() should return [A, B, C],
1102      * and B.get_inheritance_stack() should return [B, C].
1103      *
1104      * Note: as dictionary inheritance is expressed identically by the AST,
1105      * this works just as well for getting a stack of inherited dictionaries.
1106      */
1107
1108     var stack = [this];
1109     var idl_interface = this;
1110     while (idl_interface.base) {
1111         var base = this.array.members[idl_interface.base];
1112         if (!base) {
1113             throw new Error(idl_interface.type + " " + idl_interface.base + " not found (inherited by " + idl_interface.name + ")");
1114         }
1115         idl_interface = base;
1116         stack.push(idl_interface);
1117     }
1118     return stack;
1119 };
1120
1121 /**
1122  * Implementation of
1123  * https://heycam.github.io/webidl/#default-tojson-operation
1124  * for testing purposes.
1125  *
1126  * Collects the IDL types of the attributes that meet the criteria
1127  * for inclusion in the default toJSON operation for easy
1128  * comparison with actual value
1129  */
1130 IdlInterface.prototype.default_to_json_operation = function(callback) {
1131     var map = new Map(), isDefault = false;
1132     this.traverse_inherited_and_consequential_interfaces(function(I) {
1133         if (I.has_default_to_json_regular_operation()) {
1134             isDefault = true;
1135             I.members.forEach(function(m) {
1136                 if (!m.static && m.type == "attribute" && I.array.is_json_type(m.idlType)) {
1137                     map.set(m.name, m.idlType);
1138                 }
1139             });
1140         } else if (I.has_to_json_regular_operation()) {
1141             isDefault = false;
1142         }
1143     });
1144     return isDefault ? map : null;
1145 };
1146
1147 /**
1148  * Traverses inherited interfaces from the top down
1149  * and imeplemented interfaces inside out.
1150  * Invokes |callback| on each interface.
1151  *
1152  * This is an abstract implementation of the traversal
1153  * algorithm specified in:
1154  * https://heycam.github.io/webidl/#collect-attribute-values
1155  * Given the following inheritance tree:
1156  *
1157  *           F
1158  *           |
1159  *       C   E - I
1160  *       |   |
1161  *       B - D
1162  *       |
1163  *   G - A - H - J
1164  *
1165  * Invoking traverse_inherited_and_consequential_interfaces() on A
1166  * would traverse the tree in the following order:
1167  * C -> B -> F -> E -> I -> D -> A -> G -> H -> J
1168  */
1169
1170 IdlInterface.prototype.traverse_inherited_and_consequential_interfaces = function(callback) {
1171     if (typeof callback != "function") {
1172         throw new TypeError();
1173     }
1174     var stack = this.get_inheritance_stack();
1175     _traverse_inherited_and_consequential_interfaces(stack, callback);
1176 };
1177
1178 function _traverse_inherited_and_consequential_interfaces(stack, callback) {
1179     var I = stack.pop();
1180     callback(I);
1181     var mixins = I.array["implements"][I.name] || I.array["includes"][I.name];
1182     if (mixins) {
1183         mixins.forEach(function(id) {
1184             var mixin = I.array.members[id];
1185             if (!mixin) {
1186                 throw new Error("Interface " + id + " not found (implemented by " + I.name + ")");
1187             }
1188             var interfaces = mixin.get_inheritance_stack();
1189             _traverse_inherited_and_consequential_interfaces(interfaces, callback);
1190         });
1191     }
1192     if (stack.length > 0) {
1193         _traverse_inherited_and_consequential_interfaces(stack, callback);
1194     }
1195 }
1196
1197 IdlInterface.prototype.test = function()
1198 //@{
1199 {
1200     if (this.has_extended_attribute("NoInterfaceObject") || this.is_mixin())
1201     {
1202         // No tests to do without an instance.  TODO: We should still be able
1203         // to run tests on the prototype object, if we obtain one through some
1204         // other means.
1205         return;
1206     }
1207
1208     if (!this.exposed) {
1209         test(function() {
1210             assert_false(this.name in self);
1211         }.bind(this), this.name + " interface: existence and properties of interface object");
1212         return;
1213     }
1214
1215     if (!this.untested)
1216     {
1217         // First test things to do with the exception/interface object and
1218         // exception/interface prototype object.
1219         this.test_self();
1220     }
1221     // Then test things to do with its members (constants, fields, attributes,
1222     // operations, . . .).  These are run even if .untested is true, because
1223     // members might themselves be marked as .untested.  This might happen to
1224     // interfaces if the interface itself is untested but a partial interface
1225     // that extends it is tested -- then the interface itself and its initial
1226     // members will be marked as untested, but the members added by the partial
1227     // interface are still tested.
1228     this.test_members();
1229 };
1230 //@}
1231
1232 IdlInterface.prototype.test_self = function()
1233 //@{
1234 {
1235     test(function()
1236     {
1237         // This function tests WebIDL as of 2015-01-13.
1238
1239         // "For every interface that is exposed in a given ECMAScript global
1240         // environment and:
1241         // * is a callback interface that has constants declared on it, or
1242         // * is a non-callback interface that is not declared with the
1243         //   [NoInterfaceObject] extended attribute,
1244         // a corresponding property MUST exist on the ECMAScript global object.
1245         // The name of the property is the identifier of the interface, and its
1246         // value is an object called the interface object.
1247         // The property has the attributes { [[Writable]]: true,
1248         // [[Enumerable]]: false, [[Configurable]]: true }."
1249         if (this.is_callback() && !this.has_constants()) {
1250             return;
1251         }
1252
1253         // TODO: Should we test here that the property is actually writable
1254         // etc., or trust getOwnPropertyDescriptor?
1255         assert_own_property(self, this.name,
1256                             "self does not have own property " + format_value(this.name));
1257         var desc = Object.getOwnPropertyDescriptor(self, this.name);
1258         assert_false("get" in desc, "self's property " + format_value(this.name) + " should not have a getter");
1259         assert_false("set" in desc, "self's property " + format_value(this.name) + " should not have a setter");
1260         assert_true(desc.writable, "self's property " + format_value(this.name) + " should be writable");
1261         assert_false(desc.enumerable, "self's property " + format_value(this.name) + " should not be enumerable");
1262         assert_true(desc.configurable, "self's property " + format_value(this.name) + " should be configurable");
1263
1264         if (this.is_callback()) {
1265             // "The internal [[Prototype]] property of an interface object for
1266             // a callback interface must be the Function.prototype object."
1267             assert_equals(Object.getPrototypeOf(self[this.name]), Function.prototype,
1268                           "prototype of self's property " + format_value(this.name) + " is not Object.prototype");
1269
1270             return;
1271         }
1272
1273         // "The interface object for a given non-callback interface is a
1274         // function object."
1275         // "If an object is defined to be a function object, then it has
1276         // characteristics as follows:"
1277
1278         // Its [[Prototype]] internal property is otherwise specified (see
1279         // below).
1280
1281         // "* Its [[Get]] internal property is set as described in ECMA-262
1282         //    section 9.1.8."
1283         // Not much to test for this.
1284
1285         // "* Its [[Construct]] internal property is set as described in
1286         //    ECMA-262 section 19.2.2.3."
1287         // Tested below if no constructor is defined.  TODO: test constructors
1288         // if defined.
1289
1290         // "* Its @@hasInstance property is set as described in ECMA-262
1291         //    section 19.2.3.8, unless otherwise specified."
1292         // TODO
1293
1294         // ES6 (rev 30) 19.1.3.6:
1295         // "Else, if O has a [[Call]] internal method, then let builtinTag be
1296         // "Function"."
1297         assert_class_string(self[this.name], "Function", "class string of " + this.name);
1298
1299         // "The [[Prototype]] internal property of an interface object for a
1300         // non-callback interface is determined as follows:"
1301         var prototype = Object.getPrototypeOf(self[this.name]);
1302         if (this.base) {
1303             // "* If the interface inherits from some other interface, the
1304             //    value of [[Prototype]] is the interface object for that other
1305             //    interface."
1306             var has_interface_object =
1307                 !this.array
1308                      .members[this.base]
1309                      .has_extended_attribute("NoInterfaceObject");
1310             if (has_interface_object) {
1311                 assert_own_property(self, this.base,
1312                                     'should inherit from ' + this.base +
1313                                     ', but self has no such property');
1314                 assert_equals(prototype, self[this.base],
1315                               'prototype of ' + this.name + ' is not ' +
1316                               this.base);
1317             }
1318         } else {
1319             // "If the interface doesn't inherit from any other interface, the
1320             // value of [[Prototype]] is %FunctionPrototype% ([ECMA-262],
1321             // section 6.1.7.4)."
1322             assert_equals(prototype, Function.prototype,
1323                           "prototype of self's property " + format_value(this.name) + " is not Function.prototype");
1324         }
1325
1326         if (!this.has_extended_attribute("Constructor")) {
1327             // "The internal [[Call]] method of the interface object behaves as
1328             // follows . . .
1329             //
1330             // "If I was not declared with a [Constructor] extended attribute,
1331             // then throw a TypeError."
1332             assert_throws(new TypeError(), function() {
1333                 self[this.name]();
1334             }.bind(this), "interface object didn't throw TypeError when called as a function");
1335             assert_throws(new TypeError(), function() {
1336                 new self[this.name]();
1337             }.bind(this), "interface object didn't throw TypeError when called as a constructor");
1338         }
1339     }.bind(this), this.name + " interface: existence and properties of interface object");
1340
1341     if (!this.is_callback()) {
1342         test(function() {
1343             // This function tests WebIDL as of 2014-10-25.
1344             // https://heycam.github.io/webidl/#es-interface-call
1345
1346             assert_own_property(self, this.name,
1347                                 "self does not have own property " + format_value(this.name));
1348
1349             // "Interface objects for non-callback interfaces MUST have a
1350             // property named “length” with attributes { [[Writable]]: false,
1351             // [[Enumerable]]: false, [[Configurable]]: true } whose value is
1352             // a Number."
1353             assert_own_property(self[this.name], "length");
1354             var desc = Object.getOwnPropertyDescriptor(self[this.name], "length");
1355             assert_false("get" in desc, this.name + ".length should not have a getter");
1356             assert_false("set" in desc, this.name + ".length should not have a setter");
1357             assert_false(desc.writable, this.name + ".length should not be writable");
1358             assert_false(desc.enumerable, this.name + ".length should not be enumerable");
1359             assert_true(desc.configurable, this.name + ".length should be configurable");
1360
1361             var constructors = this.extAttrs
1362                 .filter(function(attr) { return attr.name == "Constructor"; });
1363             var expected_length = minOverloadLength(constructors);
1364             assert_equals(self[this.name].length, expected_length, "wrong value for " + this.name + ".length");
1365         }.bind(this), this.name + " interface object length");
1366     }
1367
1368     if (!this.is_callback() || this.has_constants()) {
1369         test(function() {
1370             // This function tests WebIDL as of 2015-11-17.
1371             // https://heycam.github.io/webidl/#interface-object
1372
1373             assert_own_property(self, this.name,
1374                                 "self does not have own property " + format_value(this.name));
1375
1376             // "All interface objects must have a property named “name” with
1377             // attributes { [[Writable]]: false, [[Enumerable]]: false,
1378             // [[Configurable]]: true } whose value is the identifier of the
1379             // corresponding interface."
1380
1381             assert_own_property(self[this.name], "name");
1382             var desc = Object.getOwnPropertyDescriptor(self[this.name], "name");
1383             assert_false("get" in desc, this.name + ".name should not have a getter");
1384             assert_false("set" in desc, this.name + ".name should not have a setter");
1385             assert_false(desc.writable, this.name + ".name should not be writable");
1386             assert_false(desc.enumerable, this.name + ".name should not be enumerable");
1387             assert_true(desc.configurable, this.name + ".name should be configurable");
1388             assert_equals(self[this.name].name, this.name, "wrong value for " + this.name + ".name");
1389         }.bind(this), this.name + " interface object name");
1390     }
1391
1392
1393     if (this.has_extended_attribute("LegacyWindowAlias")) {
1394         test(function()
1395         {
1396             var aliasAttrs = this.extAttrs.filter(function(o) { return o.name === "LegacyWindowAlias"; });
1397             if (aliasAttrs.length > 1) {
1398                 throw new IdlHarnessError("Invalid IDL: multiple LegacyWindowAlias extended attributes on " + this.name);
1399             }
1400             if (this.is_callback()) {
1401                 throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on non-interface " + this.name);
1402             }
1403             if (this.exposureSet.indexOf("Window") === -1) {
1404                 throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " which is not exposed in Window");
1405             }
1406             // TODO: when testing of [NoInterfaceObject] interfaces is supported,
1407             // check that it's not specified together with LegacyWindowAlias.
1408
1409             // TODO: maybe check that [LegacyWindowAlias] is not specified on a partial interface.
1410
1411             var rhs = aliasAttrs[0].rhs;
1412             if (!rhs) {
1413                 throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " without identifier");
1414             }
1415             var aliases;
1416             if (rhs.type === "identifier-list") {
1417                 aliases = rhs.value;
1418             } else { // rhs.type === identifier
1419                 aliases = [ rhs.value ];
1420             }
1421
1422             // OK now actually check the aliases...
1423             var alias;
1424             if (exposed_in(exposure_set(this, this.exposureSet)) && 'document' in self) {
1425                 for (alias of aliases) {
1426                     assert_true(alias in self, alias + " should exist");
1427                     assert_equals(self[alias], self[this.name], "self." + alias + " should be the same value as self." + this.name);
1428                     var desc = Object.getOwnPropertyDescriptor(self, alias);
1429                     assert_equals(desc.value, self[this.name], "wrong value in " + alias + " property descriptor");
1430                     assert_true(desc.writable, alias + " should be writable");
1431                     assert_false(desc.enumerable, alias + " should not be enumerable");
1432                     assert_true(desc.configurable, alias + " should be configurable");
1433                     assert_false('get' in desc, alias + " should not have a getter");
1434                     assert_false('set' in desc, alias + " should not have a setter");
1435                 }
1436             } else {
1437                 for (alias of aliases) {
1438                     assert_false(alias in self, alias + " should not exist");
1439                 }
1440             }
1441
1442         }.bind(this), this.name + " interface: legacy window alias");
1443     }
1444     // TODO: Test named constructors if I find any interfaces that have them.
1445
1446     test(function()
1447     {
1448         // This function tests WebIDL as of 2015-01-21.
1449         // https://heycam.github.io/webidl/#interface-object
1450
1451         if (this.is_callback() && !this.has_constants()) {
1452             return;
1453         }
1454
1455         assert_own_property(self, this.name,
1456                             "self does not have own property " + format_value(this.name));
1457
1458         if (this.is_callback()) {
1459             assert_false("prototype" in self[this.name],
1460                          this.name + ' should not have a "prototype" property');
1461             return;
1462         }
1463
1464         // "An interface object for a non-callback interface must have a
1465         // property named “prototype” with attributes { [[Writable]]: false,
1466         // [[Enumerable]]: false, [[Configurable]]: false } whose value is an
1467         // object called the interface prototype object. This object has
1468         // properties that correspond to the regular attributes and regular
1469         // operations defined on the interface, and is described in more detail
1470         // in section 4.5.4 below."
1471         assert_own_property(self[this.name], "prototype",
1472                             'interface "' + this.name + '" does not have own property "prototype"');
1473         var desc = Object.getOwnPropertyDescriptor(self[this.name], "prototype");
1474         assert_false("get" in desc, this.name + ".prototype should not have a getter");
1475         assert_false("set" in desc, this.name + ".prototype should not have a setter");
1476         assert_false(desc.writable, this.name + ".prototype should not be writable");
1477         assert_false(desc.enumerable, this.name + ".prototype should not be enumerable");
1478         assert_false(desc.configurable, this.name + ".prototype should not be configurable");
1479
1480         // Next, test that the [[Prototype]] of the interface prototype object
1481         // is correct. (This is made somewhat difficult by the existence of
1482         // [NoInterfaceObject].)
1483         // TODO: Aryeh thinks there's at least other place in this file where
1484         //       we try to figure out if an interface prototype object is
1485         //       correct. Consolidate that code.
1486
1487         // "The interface prototype object for a given interface A must have an
1488         // internal [[Prototype]] property whose value is returned from the
1489         // following steps:
1490         // "If A is declared with the [Global] extended
1491         // attribute, and A supports named properties, then return the named
1492         // properties object for A, as defined in §3.6.4 Named properties
1493         // object.
1494         // "Otherwise, if A is declared to inherit from another interface, then
1495         // return the interface prototype object for the inherited interface.
1496         // "Otherwise, if A is declared with the [LegacyArrayClass] extended
1497         // attribute, then return %ArrayPrototype%.
1498         // "Otherwise, return %ObjectPrototype%.
1499         //
1500         // "In the ECMAScript binding, the DOMException type has some additional
1501         // requirements:
1502         //
1503         //     "Unlike normal interface types, the interface prototype object
1504         //     for DOMException must have as its [[Prototype]] the intrinsic
1505         //     object %ErrorPrototype%."
1506         //
1507         if (this.name === "Window") {
1508             assert_class_string(Object.getPrototypeOf(self[this.name].prototype),
1509                                 'WindowProperties',
1510                                 'Class name for prototype of Window' +
1511                                 '.prototype is not "WindowProperties"');
1512         } else {
1513             var inherit_interface, inherit_interface_has_interface_object;
1514             if (this.base) {
1515                 inherit_interface = this.base;
1516                 inherit_interface_has_interface_object =
1517                     !this.array
1518                          .members[inherit_interface]
1519                          .has_extended_attribute("NoInterfaceObject");
1520             } else if (this.has_extended_attribute('LegacyArrayClass')) {
1521                 inherit_interface = 'Array';
1522                 inherit_interface_has_interface_object = true;
1523             } else if (this.name === "DOMException") {
1524                 inherit_interface = 'Error';
1525                 inherit_interface_has_interface_object = true;
1526             } else {
1527                 inherit_interface = 'Object';
1528                 inherit_interface_has_interface_object = true;
1529             }
1530             if (inherit_interface_has_interface_object) {
1531                 assert_own_property(self, inherit_interface,
1532                                     'should inherit from ' + inherit_interface + ', but self has no such property');
1533                 assert_own_property(self[inherit_interface], 'prototype',
1534                                     'should inherit from ' + inherit_interface + ', but that object has no "prototype" property');
1535                 assert_equals(Object.getPrototypeOf(self[this.name].prototype),
1536                               self[inherit_interface].prototype,
1537                               'prototype of ' + this.name + '.prototype is not ' + inherit_interface + '.prototype');
1538             } else {
1539                 // We can't test that we get the correct object, because this is the
1540                 // only way to get our hands on it. We only test that its class
1541                 // string, at least, is correct.
1542                 assert_class_string(Object.getPrototypeOf(self[this.name].prototype),
1543                                     inherit_interface + 'Prototype',
1544                                     'Class name for prototype of ' + this.name +
1545                                     '.prototype is not "' + inherit_interface + 'Prototype"');
1546             }
1547         }
1548
1549         // "The class string of an interface prototype object is the
1550         // concatenation of the interface’s identifier and the string
1551         // “Prototype”."
1552
1553         // Skip these tests for now due to a specification issue about
1554         // prototype name.
1555         // https://www.w3.org/Bugs/Public/show_bug.cgi?id=28244
1556
1557         // assert_class_string(self[this.name].prototype, this.name + "Prototype",
1558         //                     "class string of " + this.name + ".prototype");
1559
1560         // String() should end up calling {}.toString if nothing defines a
1561         // stringifier.
1562         if (!this.has_stringifier()) {
1563             // assert_equals(String(self[this.name].prototype), "[object " + this.name + "Prototype]",
1564             //         "String(" + this.name + ".prototype)");
1565         }
1566     }.bind(this), this.name + " interface: existence and properties of interface prototype object");
1567
1568     // "If the interface is declared with the [Global]
1569     // extended attribute, or the interface is in the set of inherited
1570     // interfaces for any other interface that is declared with one of these
1571     // attributes, then the interface prototype object must be an immutable
1572     // prototype exotic object."
1573     // https://heycam.github.io/webidl/#interface-prototype-object
1574     if (this.is_global()) {
1575         this.test_immutable_prototype("interface prototype object", self[this.name].prototype);
1576     }
1577
1578     test(function()
1579     {
1580         if (this.is_callback() && !this.has_constants()) {
1581             return;
1582         }
1583
1584         assert_own_property(self, this.name,
1585                             "self does not have own property " + format_value(this.name));
1586
1587         if (this.is_callback()) {
1588             assert_false("prototype" in self[this.name],
1589                          this.name + ' should not have a "prototype" property');
1590             return;
1591         }
1592
1593         assert_own_property(self[this.name], "prototype",
1594                             'interface "' + this.name + '" does not have own property "prototype"');
1595
1596         // "If the [NoInterfaceObject] extended attribute was not specified on
1597         // the interface, then the interface prototype object must also have a
1598         // property named “constructor” with attributes { [[Writable]]: true,
1599         // [[Enumerable]]: false, [[Configurable]]: true } whose value is a
1600         // reference to the interface object for the interface."
1601         assert_own_property(self[this.name].prototype, "constructor",
1602                             this.name + '.prototype does not have own property "constructor"');
1603         var desc = Object.getOwnPropertyDescriptor(self[this.name].prototype, "constructor");
1604         assert_false("get" in desc, this.name + ".prototype.constructor should not have a getter");
1605         assert_false("set" in desc, this.name + ".prototype.constructor should not have a setter");
1606         assert_true(desc.writable, this.name + ".prototype.constructor should be writable");
1607         assert_false(desc.enumerable, this.name + ".prototype.constructor should not be enumerable");
1608         assert_true(desc.configurable, this.name + ".prototype.constructor should be configurable");
1609         assert_equals(self[this.name].prototype.constructor, self[this.name],
1610                       this.name + '.prototype.constructor is not the same object as ' + this.name);
1611     }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property');
1612
1613
1614     test(function()
1615     {
1616         if (this.is_callback() && !this.has_constants()) {
1617             return;
1618         }
1619
1620         assert_own_property(self, this.name,
1621                             "self does not have own property " + format_value(this.name));
1622
1623         if (this.is_callback()) {
1624             assert_false("prototype" in self[this.name],
1625                          this.name + ' should not have a "prototype" property');
1626             return;
1627         }
1628
1629         assert_own_property(self[this.name], "prototype",
1630                             'interface "' + this.name + '" does not have own property "prototype"');
1631
1632         // If the interface has any member declared with the [Unscopable] extended
1633         // attribute, then there must be a property on the interface prototype object
1634         // whose name is the @@unscopables symbol, which has the attributes
1635         // { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true },
1636         // and whose value is an object created as follows...
1637         var unscopables = this.get_unscopables().map(m => m.name);
1638         var proto = self[this.name].prototype;
1639         if (unscopables.length != 0) {
1640             assert_own_property(
1641                 proto, Symbol.unscopables,
1642                 this.name + '.prototype should have an @@unscopables property');
1643             var desc = Object.getOwnPropertyDescriptor(proto, Symbol.unscopables);
1644             assert_false("get" in desc,
1645                          this.name + ".prototype[Symbol.unscopables] should not have a getter");
1646             assert_false("set" in desc, this.name + ".prototype[Symbol.unscopables] should not have a setter");
1647             assert_false(desc.writable, this.name + ".prototype[Symbol.unscopables] should not be writable");
1648             assert_false(desc.enumerable, this.name + ".prototype[Symbol.unscopables] should not be enumerable");
1649             assert_true(desc.configurable, this.name + ".prototype[Symbol.unscopables] should be configurable");
1650             assert_equals(desc.value, proto[Symbol.unscopables],
1651                           this.name + '.prototype[Symbol.unscopables] should be in the descriptor');
1652             assert_equals(typeof desc.value, "object",
1653                           this.name + '.prototype[Symbol.unscopables] should be an object');
1654             assert_equals(Object.getPrototypeOf(desc.value), null,
1655                           this.name + '.prototype[Symbol.unscopables] should have a null prototype');
1656             assert_equals(Object.getOwnPropertySymbols(desc.value).length,
1657                           0,
1658                           this.name + '.prototype[Symbol.unscopables] should have the right number of symbol-named properties');
1659
1660             // Check that we do not have _extra_ unscopables.  Checking that we
1661             // have all the ones we should will happen in the per-member tests.
1662             var observed = Object.getOwnPropertyNames(desc.value);
1663             for (var prop of observed) {
1664                 assert_not_equals(unscopables.indexOf(prop),
1665                                   -1,
1666                                   this.name + '.prototype[Symbol.unscopables] has unexpected property "' + prop + '"');
1667             }
1668         } else {
1669             assert_equals(Object.getOwnPropertyDescriptor(self[this.name].prototype, Symbol.unscopables),
1670                           undefined,
1671                           this.name + '.prototype should not have @@unscopables');
1672         }
1673     }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s @@unscopables property');
1674
1675 };
1676
1677 //@}
1678 IdlInterface.prototype.test_immutable_prototype = function(type, obj)
1679 //@{
1680 {
1681     if (typeof Object.setPrototypeOf !== "function") {
1682         return;
1683     }
1684
1685     test(function(t) {
1686         var originalValue = Object.getPrototypeOf(obj);
1687         var newValue = Object.create(null);
1688
1689         t.add_cleanup(function() {
1690             try {
1691                 Object.setPrototypeOf(obj, originalValue);
1692             } catch (err) {}
1693         });
1694
1695         assert_throws(new TypeError(), function() {
1696             Object.setPrototypeOf(obj, newValue);
1697         });
1698
1699         assert_equals(
1700                 Object.getPrototypeOf(obj),
1701                 originalValue,
1702                 "original value not modified"
1703             );
1704     }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +
1705         "of " + type + " - setting to a new value via Object.setPrototypeOf " +
1706         "should throw a TypeError");
1707
1708     test(function(t) {
1709         var originalValue = Object.getPrototypeOf(obj);
1710         var newValue = Object.create(null);
1711
1712         t.add_cleanup(function() {
1713             var setter = Object.getOwnPropertyDescriptor(
1714                 Object.prototype, '__proto__'
1715             ).set;
1716
1717             try {
1718                 setter.call(obj, originalValue);
1719             } catch (err) {}
1720         });
1721
1722         assert_throws(new TypeError(), function() {
1723             obj.__proto__ = newValue;
1724         });
1725
1726         assert_equals(
1727                 Object.getPrototypeOf(obj),
1728                 originalValue,
1729                 "original value not modified"
1730             );
1731     }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +
1732         "of " + type + " - setting to a new value via __proto__ " +
1733         "should throw a TypeError");
1734
1735     test(function(t) {
1736         var originalValue = Object.getPrototypeOf(obj);
1737         var newValue = Object.create(null);
1738
1739         t.add_cleanup(function() {
1740             try {
1741                 Reflect.setPrototypeOf(obj, originalValue);
1742             } catch (err) {}
1743         });
1744
1745         assert_false(Reflect.setPrototypeOf(obj, newValue));
1746
1747         assert_equals(
1748                 Object.getPrototypeOf(obj),
1749                 originalValue,
1750                 "original value not modified"
1751             );
1752     }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +
1753         "of " + type + " - setting to a new value via Reflect.setPrototypeOf " +
1754         "should return false");
1755
1756     test(function() {
1757         var originalValue = Object.getPrototypeOf(obj);
1758
1759         Object.setPrototypeOf(obj, originalValue);
1760     }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +
1761         "of " + type + " - setting to its original value via Object.setPrototypeOf " +
1762         "should not throw");
1763
1764     test(function() {
1765         var originalValue = Object.getPrototypeOf(obj);
1766
1767         obj.__proto__ = originalValue;
1768     }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +
1769         "of " + type + " - setting to its original value via __proto__ " +
1770         "should not throw");
1771
1772     test(function() {
1773         var originalValue = Object.getPrototypeOf(obj);
1774
1775         assert_true(Reflect.setPrototypeOf(obj, originalValue));
1776     }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +
1777         "of " + type + " - setting to its original value via Reflect.setPrototypeOf " +
1778         "should return true");
1779 };
1780
1781 //@}
1782 IdlInterface.prototype.test_member_const = function(member)
1783 //@{
1784 {
1785     if (!this.has_constants()) {
1786         throw new IdlHarnessError("Internal error: test_member_const called without any constants");
1787     }
1788
1789     test(function()
1790     {
1791         assert_own_property(self, this.name,
1792                             "self does not have own property " + format_value(this.name));
1793
1794         // "For each constant defined on an interface A, there must be
1795         // a corresponding property on the interface object, if it
1796         // exists."
1797         assert_own_property(self[this.name], member.name);
1798         // "The value of the property is that which is obtained by
1799         // converting the constant’s IDL value to an ECMAScript
1800         // value."
1801         assert_equals(self[this.name][member.name], constValue(member.value),
1802                       "property has wrong value");
1803         // "The property has attributes { [[Writable]]: false,
1804         // [[Enumerable]]: true, [[Configurable]]: false }."
1805         var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name);
1806         assert_false("get" in desc, "property should not have a getter");
1807         assert_false("set" in desc, "property should not have a setter");
1808         assert_false(desc.writable, "property should not be writable");
1809         assert_true(desc.enumerable, "property should be enumerable");
1810         assert_false(desc.configurable, "property should not be configurable");
1811     }.bind(this), this.name + " interface: constant " + member.name + " on interface object");
1812
1813     // "In addition, a property with the same characteristics must
1814     // exist on the interface prototype object."
1815     test(function()
1816     {
1817         assert_own_property(self, this.name,
1818                             "self does not have own property " + format_value(this.name));
1819
1820         if (this.is_callback()) {
1821             assert_false("prototype" in self[this.name],
1822                          this.name + ' should not have a "prototype" property');
1823             return;
1824         }
1825
1826         assert_own_property(self[this.name], "prototype",
1827                             'interface "' + this.name + '" does not have own property "prototype"');
1828
1829         assert_own_property(self[this.name].prototype, member.name);
1830         assert_equals(self[this.name].prototype[member.name], constValue(member.value),
1831                       "property has wrong value");
1832         var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name);
1833         assert_false("get" in desc, "property should not have a getter");
1834         assert_false("set" in desc, "property should not have a setter");
1835         assert_false(desc.writable, "property should not be writable");
1836         assert_true(desc.enumerable, "property should be enumerable");
1837         assert_false(desc.configurable, "property should not be configurable");
1838     }.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object");
1839 };
1840
1841
1842 //@}
1843 IdlInterface.prototype.test_member_attribute = function(member)
1844 //@{
1845   {
1846     var a_test = async_test(this.name + " interface: attribute " + member.name);
1847     a_test.step(function()
1848     {
1849         if (this.is_callback() && !this.has_constants()) {
1850             a_test.done()
1851             return;
1852         }
1853
1854         assert_own_property(self, this.name,
1855                             "self does not have own property " + format_value(this.name));
1856         assert_own_property(self[this.name], "prototype",
1857                             'interface "' + this.name + '" does not have own property "prototype"');
1858
1859         if (member["static"]) {
1860             assert_own_property(self[this.name], member.name,
1861                 "The interface object must have a property " +
1862                 format_value(member.name));
1863             a_test.done();
1864         } else if (this.is_global()) {
1865             assert_own_property(self, member.name,
1866                 "The global object must have a property " +
1867                 format_value(member.name));
1868             assert_false(member.name in self[this.name].prototype,
1869                 "The prototype object should not have a property " +
1870                 format_value(member.name));
1871
1872             var getter = Object.getOwnPropertyDescriptor(self, member.name).get;
1873             assert_equals(typeof(getter), "function",
1874                           format_value(member.name) + " must have a getter");
1875
1876             // Try/catch around the get here, since it can legitimately throw.
1877             // If it does, we obviously can't check for equality with direct
1878             // invocation of the getter.
1879             var gotValue;
1880             var propVal;
1881             try {
1882                 propVal = self[member.name];
1883                 gotValue = true;
1884             } catch (e) {
1885                 gotValue = false;
1886             }
1887             if (gotValue) {
1888                 assert_equals(propVal, getter.call(undefined),
1889                               "Gets on a global should not require an explicit this");
1890             }
1891
1892             // do_interface_attribute_asserts must be the last thing we do,
1893             // since it will call done() on a_test.
1894             this.do_interface_attribute_asserts(self, member, a_test);
1895         } else {
1896             assert_true(member.name in self[this.name].prototype,
1897                 "The prototype object must have a property " +
1898                 format_value(member.name));
1899
1900             if (!member.has_extended_attribute("LenientThis")) {
1901                 if (member.idlType.generic !== "Promise") {
1902                     assert_throws(new TypeError(), function() {
1903                         self[this.name].prototype[member.name];
1904                     }.bind(this), "getting property on prototype object must throw TypeError");
1905                     // do_interface_attribute_asserts must be the last thing we
1906                     // do, since it will call done() on a_test.
1907                     this.do_interface_attribute_asserts(self[this.name].prototype, member, a_test);
1908                 } else {
1909                     promise_rejects(a_test, new TypeError(),
1910                                     self[this.name].prototype[member.name])
1911                         .then(function() {
1912                             // do_interface_attribute_asserts must be the last
1913                             // thing we do, since it will call done() on a_test.
1914                             this.do_interface_attribute_asserts(self[this.name].prototype,
1915                                                                 member, a_test);
1916                         }.bind(this));
1917                 }
1918             } else {
1919                 assert_equals(self[this.name].prototype[member.name], undefined,
1920                               "getting property on prototype object must return undefined");
1921               // do_interface_attribute_asserts must be the last thing we do,
1922               // since it will call done() on a_test.
1923               this.do_interface_attribute_asserts(self[this.name].prototype, member, a_test);
1924             }
1925
1926         }
1927     }.bind(this));
1928
1929     test(function () {
1930         this.do_member_unscopable_asserts(member);
1931     }.bind(this), 'Unscopable handled correctly for ' + member.name + ' property on ' + this.name);
1932 };
1933
1934 //@}
1935 IdlInterface.prototype.test_member_operation = function(member)
1936 //@{
1937 {
1938     var a_test = async_test(this.name + " interface: operation " + member.name +
1939                             "(" + member.arguments.map(
1940                                 function(m) {return m.idlType.idlType; } ).join(", ")
1941                             +")");
1942     a_test.step(function()
1943     {
1944         // This function tests WebIDL as of 2015-12-29.
1945         // https://heycam.github.io/webidl/#es-operations
1946
1947         if (this.is_callback() && !this.has_constants()) {
1948             a_test.done();
1949             return;
1950         }
1951
1952         assert_own_property(self, this.name,
1953                             "self does not have own property " + format_value(this.name));
1954
1955         if (this.is_callback()) {
1956             assert_false("prototype" in self[this.name],
1957                          this.name + ' should not have a "prototype" property');
1958             a_test.done();
1959             return;
1960         }
1961
1962         assert_own_property(self[this.name], "prototype",
1963                             'interface "' + this.name + '" does not have own property "prototype"');
1964
1965         // "For each unique identifier of an exposed operation defined on the
1966         // interface, there must exist a corresponding property, unless the
1967         // effective overload set for that identifier and operation and with an
1968         // argument count of 0 has no entries."
1969
1970         // TODO: Consider [Exposed].
1971
1972         // "The location of the property is determined as follows:"
1973         var memberHolderObject;
1974         // "* If the operation is static, then the property exists on the
1975         //    interface object."
1976         if (member["static"]) {
1977             assert_own_property(self[this.name], member.name,
1978                     "interface object missing static operation");
1979             memberHolderObject = self[this.name];
1980         // "* Otherwise, [...] if the interface was declared with the [Global]
1981         //    extended attribute, then the property exists
1982         //    on every object that implements the interface."
1983         } else if (this.is_global()) {
1984             assert_own_property(self, member.name,
1985                     "global object missing non-static operation");
1986             memberHolderObject = self;
1987         // "* Otherwise, the property exists solely on the interface’s
1988         //    interface prototype object."
1989         } else {
1990             assert_own_property(self[this.name].prototype, member.name,
1991                     "interface prototype object missing non-static operation");
1992             memberHolderObject = self[this.name].prototype;
1993         }
1994         this.do_member_operation_asserts(memberHolderObject, member, a_test);
1995     }.bind(this));
1996
1997     test(function () {
1998         this.do_member_unscopable_asserts(member);
1999     }.bind(this),
2000          'Unscopable handled correctly for ' + member.name + "(" +
2001          member.arguments.map(
2002              function(m) {return m.idlType.idlType; } ).join(", ")
2003          + ")" + ' on ' + this.name);
2004 };
2005
2006 IdlInterface.prototype.do_member_unscopable_asserts = function(member)
2007 {
2008     // Check that if the member is unscopable then it's in the
2009     // @@unscopables object properly.
2010     if (!member.isUnscopable) {
2011         return;
2012     }
2013
2014     var unscopables = self[this.name].prototype[Symbol.unscopables];
2015     var prop = member.name;
2016     var propDesc = Object.getOwnPropertyDescriptor(unscopables, prop);
2017     assert_equals(typeof propDesc, "object",
2018                   this.name + '.prototype[Symbol.unscopables].' + prop + ' must exist')
2019     assert_false("get" in propDesc,
2020                  this.name + '.prototype[Symbol.unscopables].' + prop + ' must have no getter');
2021     assert_false("set" in propDesc,
2022                  this.name + '.prototype[Symbol.unscopables].' + prop + ' must have no setter');
2023     assert_true(propDesc.writable,
2024                 this.name + '.prototype[Symbol.unscopables].' + prop + ' must be writable');
2025     assert_true(propDesc.enumerable,
2026                 this.name + '.prototype[Symbol.unscopables].' + prop + ' must be enumerable');
2027     assert_true(propDesc.configurable,
2028                 this.name + '.prototype[Symbol.unscopables].' + prop + ' must be configurable');
2029     assert_equals(propDesc.value, true,
2030                   this.name + '.prototype[Symbol.unscopables].' + prop + ' must have the value `true`');
2031 };
2032
2033 //@}
2034 IdlInterface.prototype.do_member_operation_asserts = function(memberHolderObject, member, a_test)
2035 //@{
2036 {
2037     var done = a_test.done.bind(a_test);
2038     var operationUnforgeable = member.isUnforgeable;
2039     var desc = Object.getOwnPropertyDescriptor(memberHolderObject, member.name);
2040     // "The property has attributes { [[Writable]]: B,
2041     // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the
2042     // operation is unforgeable on the interface, and true otherwise".
2043     assert_false("get" in desc, "property should not have a getter");
2044     assert_false("set" in desc, "property should not have a setter");
2045     assert_equals(desc.writable, !operationUnforgeable,
2046                   "property should be writable if and only if not unforgeable");
2047     assert_true(desc.enumerable, "property should be enumerable");
2048     assert_equals(desc.configurable, !operationUnforgeable,
2049                   "property should be configurable if and only if not unforgeable");
2050     // "The value of the property is a Function object whose
2051     // behavior is as follows . . ."
2052     assert_equals(typeof memberHolderObject[member.name], "function",
2053                   "property must be a function");
2054     // "The value of the Function object’s “length” property is
2055     // a Number determined as follows:
2056     // ". . .
2057     // "Return the length of the shortest argument list of the
2058     // entries in S."
2059     assert_equals(memberHolderObject[member.name].length,
2060         minOverloadLength(this.members.filter(function(m) {
2061             return m.type == "operation" && m.name == member.name;
2062         })),
2063         "property has wrong .length");
2064
2065     // Make some suitable arguments
2066     var args = member.arguments.map(function(arg) {
2067         return create_suitable_object(arg.idlType);
2068     });
2069
2070     // "Let O be a value determined as follows:
2071     // ". . .
2072     // "Otherwise, throw a TypeError."
2073     // This should be hit if the operation is not static, there is
2074     // no [ImplicitThis] attribute, and the this value is null.
2075     //
2076     // TODO: We currently ignore the [ImplicitThis] case.  Except we manually
2077     // check for globals, since otherwise we'll invoke window.close().  And we
2078     // have to skip this test for anything that on the proto chain of "self",
2079     // since that does in fact have implicit-this behavior.
2080     if (!member["static"]) {
2081         var cb;
2082         if (!this.is_global() &&
2083             memberHolderObject[member.name] != self[member.name])
2084         {
2085             cb = awaitNCallbacks(2, done);
2086             throwOrReject(a_test, member, memberHolderObject[member.name], null, args,
2087                           "calling operation with this = null didn't throw TypeError", cb);
2088         } else {
2089             cb = awaitNCallbacks(1, done);
2090         }
2091
2092         // ". . . If O is not null and is also not a platform object
2093         // that implements interface I, throw a TypeError."
2094         //
2095         // TODO: Test a platform object that implements some other
2096         // interface.  (Have to be sure to get inheritance right.)
2097         throwOrReject(a_test, member, memberHolderObject[member.name], {}, args,
2098                       "calling operation with this = {} didn't throw TypeError", cb);
2099     } else {
2100         done();
2101     }
2102 }
2103
2104 //@}
2105 IdlInterface.prototype.add_iterable_members = function(member)
2106 //@{
2107 {
2108     this.members.push(new IdlInterfaceMember(
2109         { type: "operation", name: "entries", idlType: "iterator", arguments: []}));
2110     this.members.push(new IdlInterfaceMember(
2111         { type: "operation", name: "keys", idlType: "iterator", arguments: []}));
2112     this.members.push(new IdlInterfaceMember(
2113         { type: "operation", name: "values", idlType: "iterator", arguments: []}));
2114     this.members.push(new IdlInterfaceMember(
2115         { type: "operation", name: "forEach", idlType: "void",
2116           arguments:
2117           [{ name: "callback", idlType: {idlType: "function"}},
2118            { name: "thisValue", idlType: {idlType: "any"}, optional: true}]}));
2119 };
2120
2121 IdlInterface.prototype.test_to_json_operation = function(memberHolderObject, member) {
2122     var instanceName = memberHolderObject.constructor.name;
2123     if (member.has_extended_attribute("Default")) {
2124         var map = this.default_to_json_operation();
2125         test(function() {
2126             var json = memberHolderObject.toJSON();
2127             map.forEach(function(type, k) {
2128                 assert_true(k in json, "property " + JSON.stringify(k) + " should be present in the output of " + this.name + ".prototype.toJSON()");
2129                 var descriptor = Object.getOwnPropertyDescriptor(json, k);
2130                 assert_true(descriptor.writable, "property " + k + " should be writable");
2131                 assert_true(descriptor.configurable, "property " + k + " should be configurable");
2132                 assert_true(descriptor.enumerable, "property " + k + " should be enumerable");
2133                 this.array.assert_type_is(json[k], type);
2134                 delete json[k];
2135             }, this);
2136         }.bind(this), "Test default toJSON operation of " + instanceName);
2137     } else {
2138         test(function() {
2139             assert_true(this.array.is_json_type(member.idlType), JSON.stringify(member.idlType) + " is not an appropriate return value for the toJSON operation of " + instanceName);
2140             this.array.assert_type_is(memberHolderObject.toJSON(), member.idlType);
2141         }.bind(this), "Test toJSON operation of " + instanceName);
2142     }
2143 };
2144
2145 //@}
2146 IdlInterface.prototype.test_member_iterable = function(member)
2147 //@{
2148 {
2149     var interfaceName = this.name;
2150     var isPairIterator = member.idlType instanceof Array;
2151     test(function()
2152     {
2153         var descriptor = Object.getOwnPropertyDescriptor(self[interfaceName].prototype, Symbol.iterator);
2154         assert_true(descriptor.writable, "property should be writable");
2155         assert_true(descriptor.configurable, "property should be configurable");
2156         assert_false(descriptor.enumerable, "property should not be enumerable");
2157         assert_equals(self[interfaceName].prototype[Symbol.iterator].name, isPairIterator ? "entries" : "values", "@@iterator function does not have the right name");
2158     }, "Testing Symbol.iterator property of iterable interface " + interfaceName);
2159
2160     if (isPairIterator) {
2161         test(function() {
2162             assert_equals(self[interfaceName].prototype[Symbol.iterator], self[interfaceName].prototype["entries"], "entries method is not the same as @@iterator");
2163         }, "Testing pair iterable interface " + interfaceName);
2164     } else {
2165         test(function() {
2166             ["entries", "keys", "values", "forEach", Symbol.Iterator].forEach(function(property) {
2167                 assert_equals(self[interfaceName].prototype[property], Array.prototype[property], property + " function is not the same as Array one");
2168             });
2169         }, "Testing value iterable interface " + interfaceName);
2170     }
2171 };
2172
2173 //@}
2174 IdlInterface.prototype.test_member_stringifier = function(member)
2175 //@{
2176 {
2177     test(function()
2178     {
2179         if (this.is_callback() && !this.has_constants()) {
2180             return;
2181         }
2182
2183         assert_own_property(self, this.name,
2184                             "self does not have own property " + format_value(this.name));
2185
2186         if (this.is_callback()) {
2187             assert_false("prototype" in self[this.name],
2188                          this.name + ' should not have a "prototype" property');
2189             return;
2190         }
2191
2192         assert_own_property(self[this.name], "prototype",
2193                             'interface "' + this.name + '" does not have own property "prototype"');
2194
2195         // ". . . the property exists on the interface prototype object."
2196         var interfacePrototypeObject = self[this.name].prototype;
2197         assert_own_property(self[this.name].prototype, "toString",
2198                 "interface prototype object missing non-static operation");
2199
2200         var stringifierUnforgeable = member.isUnforgeable;
2201         var desc = Object.getOwnPropertyDescriptor(interfacePrototypeObject, "toString");
2202         // "The property has attributes { [[Writable]]: B,
2203         // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the
2204         // stringifier is unforgeable on the interface, and true otherwise."
2205         assert_false("get" in desc, "property should not have a getter");
2206         assert_false("set" in desc, "property should not have a setter");
2207         assert_equals(desc.writable, !stringifierUnforgeable,
2208                       "property should be writable if and only if not unforgeable");
2209         assert_true(desc.enumerable, "property should be enumerable");
2210         assert_equals(desc.configurable, !stringifierUnforgeable,
2211                       "property should be configurable if and only if not unforgeable");
2212         // "The value of the property is a Function object, which behaves as
2213         // follows . . ."
2214         assert_equals(typeof interfacePrototypeObject.toString, "function",
2215                       "property must be a function");
2216         // "The value of the Function object’s “length” property is the Number
2217         // value 0."
2218         assert_equals(interfacePrototypeObject.toString.length, 0,
2219             "property has wrong .length");
2220
2221         // "Let O be the result of calling ToObject on the this value."
2222         assert_throws(new TypeError(), function() {
2223             self[this.name].prototype.toString.apply(null, []);
2224         }, "calling stringifier with this = null didn't throw TypeError");
2225
2226         // "If O is not an object that implements the interface on which the
2227         // stringifier was declared, then throw a TypeError."
2228         //
2229         // TODO: Test a platform object that implements some other
2230         // interface.  (Have to be sure to get inheritance right.)
2231         assert_throws(new TypeError(), function() {
2232             self[this.name].prototype.toString.apply({}, []);
2233         }, "calling stringifier with this = {} didn't throw TypeError");
2234     }.bind(this), this.name + " interface: stringifier");
2235 };
2236
2237 //@}
2238 IdlInterface.prototype.test_members = function()
2239 //@{
2240 {
2241     for (var i = 0; i < this.members.length; i++)
2242     {
2243         var member = this.members[i];
2244         switch (member.type) {
2245         case "iterable":
2246             this.add_iterable_members(member);
2247             break;
2248         // TODO: add setlike and maplike handling.
2249         default:
2250             break;
2251         }
2252     }
2253
2254     for (var i = 0; i < this.members.length; i++)
2255     {
2256         var member = this.members[i];
2257         if (member.untested) {
2258             continue;
2259         }
2260
2261         if (!exposed_in(exposure_set(member, this.exposureSet))) {
2262             test(function() {
2263                 // It's not exposed, so we shouldn't find it anywhere.
2264                 assert_false(member.name in self[this.name],
2265                              "The interface object must not have a property " +
2266                              format_value(member.name));
2267                 assert_false(member.name in self[this.name].prototype,
2268                              "The prototype object must not have a property " +
2269                              format_value(member.name));
2270             }.bind(this), this.name + " interface: member " + member.name);
2271             continue;
2272         }
2273
2274         switch (member.type) {
2275         case "const":
2276             this.test_member_const(member);
2277             break;
2278
2279         case "attribute":
2280             // For unforgeable attributes, we do the checks in
2281             // test_interface_of instead.
2282             if (!member.isUnforgeable)
2283             {
2284                 this.test_member_attribute(member);
2285             }
2286             if (member.stringifier) {
2287                 this.test_member_stringifier(member);
2288             }
2289             break;
2290
2291         case "operation":
2292             // TODO: Need to correctly handle multiple operations with the same
2293             // identifier.
2294             // For unforgeable operations, we do the checks in
2295             // test_interface_of instead.
2296             if (member.name) {
2297                 if (!member.isUnforgeable)
2298                 {
2299                     this.test_member_operation(member);
2300                 }
2301             } else if (member.stringifier) {
2302                 this.test_member_stringifier(member);
2303             }
2304             break;
2305
2306         case "iterable":
2307             this.test_member_iterable(member);
2308             break;
2309         default:
2310             // TODO: check more member types.
2311             break;
2312         }
2313     }
2314 };
2315
2316 //@}
2317 IdlInterface.prototype.test_object = function(desc)
2318 //@{
2319 {
2320     var obj, exception = null;
2321     try
2322     {
2323         obj = eval(desc);
2324     }
2325     catch(e)
2326     {
2327         exception = e;
2328     }
2329
2330     var expected_typeof =
2331         this.members.some(function(member) { return member.legacycaller; })
2332         ? "function"
2333         : "object";
2334
2335     this.test_primary_interface_of(desc, obj, exception, expected_typeof);
2336
2337     var current_interface = this;
2338     while (current_interface)
2339     {
2340         if (!(current_interface.name in this.array.members))
2341         {
2342             throw new IdlHarnessError("Interface " + current_interface.name + " not found (inherited by " + this.name + ")");
2343         }
2344         if (current_interface.prevent_multiple_testing && current_interface.already_tested)
2345         {
2346             return;
2347         }
2348         current_interface.test_interface_of(desc, obj, exception, expected_typeof);
2349         current_interface = this.array.members[current_interface.base];
2350     }
2351 };
2352
2353 //@}
2354 IdlInterface.prototype.test_primary_interface_of = function(desc, obj, exception, expected_typeof)
2355 //@{
2356 {
2357     // Only the object itself, not its members, are tested here, so if the
2358     // interface is untested, there is nothing to do.
2359     if (this.untested)
2360     {
2361         return;
2362     }
2363
2364     // "The internal [[SetPrototypeOf]] method of every platform object that
2365     // implements an interface with the [Global] extended
2366     // attribute must execute the same algorithm as is defined for the
2367     // [[SetPrototypeOf]] internal method of an immutable prototype exotic
2368     // object."
2369     // https://heycam.github.io/webidl/#platform-object-setprototypeof
2370     if (this.is_global())
2371     {
2372         this.test_immutable_prototype("global platform object", obj);
2373     }
2374
2375
2376     // We can't easily test that its prototype is correct if there's no
2377     // interface object, or the object is from a different global environment
2378     // (not instanceof Object).  TODO: test in this case that its prototype at
2379     // least looks correct, even if we can't test that it's actually correct.
2380     if (!this.has_extended_attribute("NoInterfaceObject")
2381     && (typeof obj != expected_typeof || obj instanceof Object))
2382     {
2383         test(function()
2384         {
2385             assert_equals(exception, null, "Unexpected exception when evaluating object");
2386             assert_equals(typeof obj, expected_typeof, "wrong typeof object");
2387             assert_own_property(self, this.name,
2388                                 "self does not have own property " + format_value(this.name));
2389             assert_own_property(self[this.name], "prototype",
2390                                 'interface "' + this.name + '" does not have own property "prototype"');
2391
2392             // "The value of the internal [[Prototype]] property of the
2393             // platform object is the interface prototype object of the primary
2394             // interface from the platform object’s associated global
2395             // environment."
2396             assert_equals(Object.getPrototypeOf(obj),
2397                           self[this.name].prototype,
2398                           desc + "'s prototype is not " + this.name + ".prototype");
2399         }.bind(this), this.name + " must be primary interface of " + desc);
2400     }
2401
2402     // "The class string of a platform object that implements one or more
2403     // interfaces must be the identifier of the primary interface of the
2404     // platform object."
2405     test(function()
2406     {
2407         assert_equals(exception, null, "Unexpected exception when evaluating object");
2408         assert_equals(typeof obj, expected_typeof, "wrong typeof object");
2409         assert_class_string(obj, this.name, "class string of " + desc);
2410         if (!this.has_stringifier())
2411         {
2412             assert_equals(String(obj), "[object " + this.name + "]", "String(" + desc + ")");
2413         }
2414     }.bind(this), "Stringification of " + desc);
2415 };
2416
2417 //@}
2418 IdlInterface.prototype.test_interface_of = function(desc, obj, exception, expected_typeof)
2419 //@{
2420 {
2421     // TODO: Indexed and named properties, more checks on interface members
2422     this.already_tested = true;
2423
2424     for (var i = 0; i < this.members.length; i++)
2425     {
2426         var member = this.members[i];
2427         if (member.untested) {
2428             continue;
2429         }
2430         if (!exposed_in(exposure_set(member, this.exposureSet))) {
2431             test(function() {
2432                 assert_false(member.name in obj);
2433             }.bind(this), this.name + " interface: " + desc + ' must not have property "' + member.name + '"');
2434             continue;
2435         }
2436         if (member.type == "attribute" && member.isUnforgeable)
2437         {
2438             var a_test = async_test(this.name + " interface: " + desc + ' must have own property "' + member.name + '"');
2439             a_test.step(function() {
2440                 assert_equals(exception, null, "Unexpected exception when evaluating object");
2441                 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
2442                 // Call do_interface_attribute_asserts last, since it will call a_test.done()
2443                 this.do_interface_attribute_asserts(obj, member, a_test);
2444             }.bind(this));
2445         }
2446         else if (member.type == "operation" &&
2447                  member.name &&
2448                  member.isUnforgeable)
2449         {
2450             var a_test = async_test(this.name + " interface: " + desc + ' must have own property "' + member.name + '"');
2451             a_test.step(function()
2452             {
2453                 assert_equals(exception, null, "Unexpected exception when evaluating object");
2454                 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
2455                 assert_own_property(obj, member.name,
2456                                     "Doesn't have the unforgeable operation property");
2457                 this.do_member_operation_asserts(obj, member, a_test);
2458             }.bind(this));
2459         }
2460         else if ((member.type == "const"
2461         || member.type == "attribute"
2462         || member.type == "operation")
2463         && member.name)
2464         {
2465             var described_name = member.name;
2466             if (member.type == "operation")
2467             {
2468                 described_name += "(" + member.arguments.map(arg => arg.idlType.idlType).join(", ") + ")";
2469             }
2470             test(function()
2471             {
2472                 assert_equals(exception, null, "Unexpected exception when evaluating object");
2473                 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
2474                 if (!member["static"]) {
2475                     if (!this.is_global()) {
2476                         assert_inherits(obj, member.name);
2477                     } else {
2478                         assert_own_property(obj, member.name);
2479                     }
2480
2481                     if (member.type == "const")
2482                     {
2483                         assert_equals(obj[member.name], constValue(member.value));
2484                     }
2485                     if (member.type == "attribute")
2486                     {
2487                         // Attributes are accessor properties, so they might
2488                         // legitimately throw an exception rather than returning
2489                         // anything.
2490                         var property, thrown = false;
2491                         try
2492                         {
2493                             property = obj[member.name];
2494                         }
2495                         catch (e)
2496                         {
2497                             thrown = true;
2498                         }
2499                         if (!thrown)
2500                         {
2501                             this.array.assert_type_is(property, member.idlType);
2502                         }
2503                     }
2504                     if (member.type == "operation")
2505                     {
2506                         assert_equals(typeof obj[member.name], "function");
2507                     }
2508                 }
2509             }.bind(this), this.name + " interface: " + desc + ' must inherit property "' + described_name + '" with the proper type');
2510         }
2511         // TODO: This is wrong if there are multiple operations with the same
2512         // identifier.
2513         // TODO: Test passing arguments of the wrong type.
2514         if (member.type == "operation" && member.name && member.arguments.length)
2515         {
2516             var a_test = async_test( this.name + " interface: calling " + member.name +
2517             "(" + member.arguments.map(function(m) { return m.idlType.idlType; }).join(", ") +
2518             ") on " + desc + " with too few arguments must throw TypeError");
2519             a_test.step(function()
2520             {
2521                 assert_equals(exception, null, "Unexpected exception when evaluating object");
2522                 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
2523                 var fn;
2524                 if (!member["static"]) {
2525                     if (!this.is_global() && !member.isUnforgeable) {
2526                         assert_inherits(obj, member.name);
2527                     } else {
2528                         assert_own_property(obj, member.name);
2529                     }
2530                     fn = obj[member.name];
2531                 }
2532                 else
2533                 {
2534                     assert_own_property(obj.constructor, member.name, "interface object must have static operation as own property");
2535                     fn = obj.constructor[member.name];
2536                 }
2537
2538                 var minLength = minOverloadLength(this.members.filter(function(m) {
2539                     return m.type == "operation" && m.name == member.name;
2540                 }));
2541                 var args = [];
2542                 var cb = awaitNCallbacks(minLength, a_test.done.bind(a_test));
2543                 for (var i = 0; i < minLength; i++) {
2544                     throwOrReject(a_test, member, fn, obj, args, "Called with " + i + " arguments", cb);
2545
2546                     args.push(create_suitable_object(member.arguments[i].idlType));
2547                 }
2548                 if (minLength === 0) {
2549                     cb();
2550                 }
2551             }.bind(this));
2552         }
2553
2554         if (member.is_to_json_regular_operation()) {
2555             this.test_to_json_operation(obj, member);
2556         }
2557     }
2558 };
2559
2560 //@}
2561 IdlInterface.prototype.has_stringifier = function()
2562 //@{
2563 {
2564     if (this.name === "DOMException") {
2565         // toString is inherited from Error, so don't assume we have the
2566         // default stringifer
2567         return true;
2568     }
2569     if (this.members.some(function(member) { return member.stringifier; })) {
2570         return true;
2571     }
2572     if (this.base &&
2573         this.array.members[this.base].has_stringifier()) {
2574         return true;
2575     }
2576     return false;
2577 };
2578
2579 //@}
2580 IdlInterface.prototype.do_interface_attribute_asserts = function(obj, member, a_test)
2581 //@{
2582 {
2583     // This function tests WebIDL as of 2015-01-27.
2584     // TODO: Consider [Exposed].
2585
2586     // This is called by test_member_attribute() with the prototype as obj if
2587     // it is not a global, and the global otherwise, and by test_interface_of()
2588     // with the object as obj.
2589
2590     var pendingPromises = [];
2591
2592     // "For each exposed attribute of the interface, whether it was declared on
2593     // the interface itself or one of its consequential interfaces, there MUST
2594     // exist a corresponding property. The characteristics of this property are
2595     // as follows:"
2596
2597     // "The name of the property is the identifier of the attribute."
2598     assert_own_property(obj, member.name);
2599
2600     // "The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
2601     // true, [[Configurable]]: configurable }, where:
2602     // "configurable is false if the attribute was declared with the
2603     // [Unforgeable] extended attribute and true otherwise;
2604     // "G is the attribute getter, defined below; and
2605     // "S is the attribute setter, also defined below."
2606     var desc = Object.getOwnPropertyDescriptor(obj, member.name);
2607     assert_false("value" in desc, 'property descriptor should not have a "value" field');
2608     assert_false("writable" in desc, 'property descriptor should not have a "writable" field');
2609     assert_true(desc.enumerable, "property should be enumerable");
2610     if (member.isUnforgeable)
2611     {
2612         assert_false(desc.configurable, "[Unforgeable] property must not be configurable");
2613     }
2614     else
2615     {
2616         assert_true(desc.configurable, "property must be configurable");
2617     }
2618
2619
2620     // "The attribute getter is a Function object whose behavior when invoked
2621     // is as follows:"
2622     assert_equals(typeof desc.get, "function", "getter must be Function");
2623
2624     // "If the attribute is a regular attribute, then:"
2625     if (!member["static"]) {
2626         // "If O is not a platform object that implements I, then:
2627         // "If the attribute was specified with the [LenientThis] extended
2628         // attribute, then return undefined.
2629         // "Otherwise, throw a TypeError."
2630         if (!member.has_extended_attribute("LenientThis")) {
2631             if (member.idlType.generic !== "Promise") {
2632                 assert_throws(new TypeError(), function() {
2633                     desc.get.call({});
2634                 }.bind(this), "calling getter on wrong object type must throw TypeError");
2635             } else {
2636                 pendingPromises.push(
2637                     promise_rejects(a_test, new TypeError(), desc.get.call({}),
2638                                     "calling getter on wrong object type must reject the return promise with TypeError"));
2639             }
2640         } else {
2641             assert_equals(desc.get.call({}), undefined,
2642                           "calling getter on wrong object type must return undefined");
2643         }
2644     }
2645
2646     // "The value of the Function object’s “length” property is the Number
2647     // value 0."
2648     assert_equals(desc.get.length, 0, "getter length must be 0");
2649
2650
2651     // TODO: Test calling setter on the interface prototype (should throw
2652     // TypeError in most cases).
2653     if (member.readonly
2654     && !member.has_extended_attribute("LenientSetter")
2655     && !member.has_extended_attribute("PutForwards")
2656     && !member.has_extended_attribute("Replaceable"))
2657     {
2658         // "The attribute setter is undefined if the attribute is declared
2659         // readonly and has neither a [PutForwards] nor a [Replaceable]
2660         // extended attribute declared on it."
2661         assert_equals(desc.set, undefined, "setter must be undefined for readonly attributes");
2662     }
2663     else
2664     {
2665         // "Otherwise, it is a Function object whose behavior when
2666         // invoked is as follows:"
2667         assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes");
2668
2669         // "If the attribute is a regular attribute, then:"
2670         if (!member["static"]) {
2671             // "If /validThis/ is false and the attribute was not specified
2672             // with the [LenientThis] extended attribute, then throw a
2673             // TypeError."
2674             // "If the attribute is declared with a [Replaceable] extended
2675             // attribute, then: ..."
2676             // "If validThis is false, then return."
2677             if (!member.has_extended_attribute("LenientThis")) {
2678                 assert_throws(new TypeError(), function() {
2679                     desc.set.call({});
2680                 }.bind(this), "calling setter on wrong object type must throw TypeError");
2681             } else {
2682                 assert_equals(desc.set.call({}), undefined,
2683                               "calling setter on wrong object type must return undefined");
2684             }
2685         }
2686
2687         // "The value of the Function object’s “length” property is the Number
2688         // value 1."
2689         assert_equals(desc.set.length, 1, "setter length must be 1");
2690     }
2691
2692     Promise.all(pendingPromises).then(a_test.done.bind(a_test));
2693 }
2694 //@}
2695
2696 /// IdlInterfaceMember ///
2697 function IdlInterfaceMember(obj)
2698 //@{
2699 {
2700     /**
2701      * obj is an object produced by the WebIDLParser.js "ifMember" production.
2702      * We just forward all properties to this object without modification,
2703      * except for special extAttrs handling.
2704      */
2705     for (var k in obj)
2706     {
2707         this[k] = obj[k];
2708     }
2709     if (!("extAttrs" in this))
2710     {
2711         this.extAttrs = [];
2712     }
2713
2714     this.isUnforgeable = this.has_extended_attribute("Unforgeable");
2715     this.isUnscopable = this.has_extended_attribute("Unscopable");
2716 }
2717
2718 //@}
2719 IdlInterfaceMember.prototype = Object.create(IdlObject.prototype);
2720
2721 IdlInterfaceMember.prototype.is_to_json_regular_operation = function() {
2722     return this.type == "operation" && !this.static && this.name == "toJSON";
2723 };
2724
2725 /// Internal helper functions ///
2726 function create_suitable_object(type)
2727 //@{
2728 {
2729     /**
2730      * type is an object produced by the WebIDLParser.js "type" production.  We
2731      * return a JavaScript value that matches the type, if we can figure out
2732      * how.
2733      */
2734     if (type.nullable)
2735     {
2736         return null;
2737     }
2738     switch (type.idlType)
2739     {
2740         case "any":
2741         case "boolean":
2742             return true;
2743
2744         case "byte": case "octet": case "short": case "unsigned short":
2745         case "long": case "unsigned long": case "long long":
2746         case "unsigned long long": case "float": case "double":
2747         case "unrestricted float": case "unrestricted double":
2748             return 7;
2749
2750         case "DOMString":
2751         case "ByteString":
2752         case "USVString":
2753             return "foo";
2754
2755         case "object":
2756             return {a: "b"};
2757
2758         case "Node":
2759             return document.createTextNode("abc");
2760     }
2761     return null;
2762 }
2763 //@}
2764
2765 /// IdlEnum ///
2766 // Used for IdlArray.prototype.assert_type_is
2767 function IdlEnum(obj)
2768 //@{
2769 {
2770     /**
2771      * obj is an object produced by the WebIDLParser.js "dictionary"
2772      * production.
2773      */
2774
2775     /** Self-explanatory. */
2776     this.name = obj.name;
2777
2778     /** An array of values produced by the "enum" production. */
2779     this.values = obj.values;
2780
2781 }
2782 //@}
2783
2784 IdlEnum.prototype = Object.create(IdlObject.prototype);
2785
2786 /// IdlTypedef ///
2787 // Used for IdlArray.prototype.assert_type_is
2788 function IdlTypedef(obj)
2789 //@{
2790 {
2791     /**
2792      * obj is an object produced by the WebIDLParser.js "typedef"
2793      * production.
2794      */
2795
2796     /** Self-explanatory. */
2797     this.name = obj.name;
2798
2799     /** The idlType that we are supposed to be typedeffing to. */
2800     this.idlType = obj.idlType;
2801
2802 }
2803 //@}
2804
2805 IdlTypedef.prototype = Object.create(IdlObject.prototype);
2806
2807 }());
2808 // vim: set expandtab shiftwidth=4 tabstop=4 foldmarker=@{,@} foldmethod=marker: