Best JavaScript code snippet using wpt
idlharness.js
Source:idlharness.js  
1/* For user documentation see docs/_writing-tests/idlharness.md */2/**3 * Notes for people who want to edit this file (not just use it as a library):4 *5 * Most of the interesting stuff happens in the derived classes of IdlObject,6 * especially IdlInterface.  The entry point for all IdlObjects is .test(),7 * which is called by IdlArray.test().  An IdlObject is conceptually just8 * "thing we want to run tests on", and an IdlArray is an array of IdlObjects9 * with some additional data thrown in.10 *11 * The object model is based on what WebIDLParser.js produces, which is in turn12 * based on its pegjs grammar.  If you want to figure out what properties an13 * object will have from WebIDLParser.js, the best way is to look at the14 * grammar:15 *16 *   https://github.com/darobin/webidl.js/blob/master/lib/grammar.peg17 *18 * So for instance:19 *20 *   // interface definition21 *   interface22 *       =   extAttrs:extendedAttributeList? S? "interface" S name:identifier w herit:ifInheritance? w "{" w mem:ifMember* w "}" w ";" w23 *           { return { type: "interface", name: name, inheritance: herit, members: mem, extAttrs: extAttrs }; }24 *25 * This means that an "interface" object will have a .type property equal to26 * the string "interface", a .name property equal to the identifier that the27 * parser found, an .inheritance property equal to either null or the result of28 * the "ifInheritance" production found elsewhere in the grammar, and so on.29 * After each grammatical production is a JavaScript function in curly braces30 * that gets called with suitable arguments and returns some JavaScript value.31 *32 * (Note that the version of WebIDLParser.js we use might sometimes be33 * out-of-date or forked.)34 *35 * The members and methods of the classes defined by this file are all at least36 * briefly documented, hopefully.37 */38(function(){39"use strict";40// Support subsetTestByKey from /common/subset-tests-by-key.js, but make it optional41if (!('subsetTestByKey' in self)) {42    self.subsetTestByKey = function(key, callback, ...args) {43      return callback(...args);44    }45    self.shouldRunSubTest = () => true;46}47/// Helpers ///48function constValue (cnt)49{50    if (cnt.type === "null") return null;51    if (cnt.type === "NaN") return NaN;52    if (cnt.type === "Infinity") return cnt.negative ? -Infinity : Infinity;53    if (cnt.type === "number") return +cnt.value;54    return cnt.value;55}56function minOverloadLength(overloads)57{58    // "The value of the Function objectâs âlengthâ property is59    // a Number determined as follows:60    // ". . .61    // "Return the length of the shortest argument list of the62    // entries in S."63    if (!overloads.length) {64        return 0;65    }66    return overloads.map(function(attr) {67        return attr.arguments ? attr.arguments.filter(function(arg) {68            return !arg.optional && !arg.variadic;69        }).length : 0;70    })71    .reduce(function(m, n) { return Math.min(m, n); });72}73// A helper to get the global of a Function object.  This is needed to determine74// which global exceptions the function throws will come from.75function globalOf(func)76{77    try {78        // Use the fact that .constructor for a Function object is normally the79        // Function constructor, which can be used to mint a new function in the80        // right global.81        return func.constructor("return this;")();82    } catch (e) {83    }84    // If the above fails, because someone gave us a non-function, or a function85    // with a weird proto chain or weird .constructor property, just fall back86    // to 'self'.87    return self;88}89// https://esdiscuss.org/topic/isconstructor#content-1190function isConstructor(o) {91    try {92        new (new Proxy(o, {construct: () => ({})}));93        return true;94    } catch(e) {95        return false;96    }97}98function throwOrReject(a_test, operation, fn, obj, args, message, cb)99{100    if (operation.idlType.generic !== "Promise") {101        assert_throws_js(globalOf(fn).TypeError, function() {102            fn.apply(obj, args);103        }, message);104        cb();105    } else {106        try {107            promise_rejects_js(a_test, TypeError, fn.apply(obj, args), message).then(cb, cb);108        } catch (e){109            a_test.step(function() {110                assert_unreached("Throws \"" + e + "\" instead of rejecting promise");111                cb();112            });113        }114    }115}116function awaitNCallbacks(n, cb, ctx)117{118    var counter = 0;119    return function() {120        counter++;121        if (counter >= n) {122            cb();123        }124    };125}126/// IdlHarnessError ///127// Entry point128self.IdlHarnessError = function(message)129{130    /**131     * Message to be printed as the error's toString invocation.132     */133    this.message = message;134};135IdlHarnessError.prototype = Object.create(Error.prototype);136IdlHarnessError.prototype.toString = function()137{138    return this.message;139};140/// IdlArray ///141// Entry point142self.IdlArray = function()143{144    /**145     * A map from strings to the corresponding named IdlObject, such as146     * IdlInterface or IdlException.  These are the things that test() will run147     * tests on.148     */149    this.members = {};150    /**151     * A map from strings to arrays of strings.  The keys are interface or152     * exception names, and are expected to also exist as keys in this.members153     * (otherwise they'll be ignored).  This is populated by add_objects() --154     * see documentation at the start of the file.  The actual tests will be155     * run by calling this.members[name].test_object(obj) for each obj in156     * this.objects[name].  obj is a string that will be eval'd to produce a157     * JavaScript value, which is supposed to be an object implementing the158     * given IdlObject (interface, exception, etc.).159     */160    this.objects = {};161    /**162     * When adding multiple collections of IDLs one at a time, an earlier one163     * might contain a partial interface or includes statement that depends164     * on a later one.  Save these up and handle them right before we run165     * tests.166     *167     * Both this.partials and this.includes will be the objects as parsed by168     * WebIDLParser.js, not wrapped in IdlInterface or similar.169     */170    this.partials = [];171    this.includes = [];172    /**173     * Record of skipped IDL items, in case we later realize that they are a174     * dependency (to retroactively process them).175     */176    this.skipped = new Map();177};178IdlArray.prototype.add_idls = function(raw_idls, options)179{180    /** Entry point.  See documentation at beginning of file. */181    this.internal_add_idls(WebIDL2.parse(raw_idls), options);182};183IdlArray.prototype.add_untested_idls = function(raw_idls, options)184{185    /** Entry point.  See documentation at beginning of file. */186    var parsed_idls = WebIDL2.parse(raw_idls);187    this.mark_as_untested(parsed_idls);188    this.internal_add_idls(parsed_idls, options);189};190IdlArray.prototype.mark_as_untested = function (parsed_idls)191{192    for (var i = 0; i < parsed_idls.length; i++) {193        parsed_idls[i].untested = true;194        if ("members" in parsed_idls[i]) {195            for (var j = 0; j < parsed_idls[i].members.length; j++) {196                parsed_idls[i].members[j].untested = true;197            }198        }199    }200};201IdlArray.prototype.is_excluded_by_options = function (name, options)202{203    return options &&204        (options.except && options.except.includes(name)205         || options.only && !options.only.includes(name));206};207IdlArray.prototype.add_dependency_idls = function(raw_idls, options)208{209    return this.internal_add_dependency_idls(WebIDL2.parse(raw_idls), options);210};211IdlArray.prototype.internal_add_dependency_idls = function(parsed_idls, options)212{213    const new_options = { only: [] }214    const all_deps = new Set();215    Object.values(this.members).forEach(v => {216        if (v.base) {217            all_deps.add(v.base);218        }219    });220    // Add both 'A' and 'B' for each 'A includes B' entry.221    this.includes.forEach(i => {222        all_deps.add(i.target);223        all_deps.add(i.includes);224    });225    this.partials.forEach(p => all_deps.add(p.name));226    // Add 'TypeOfType' for each "typedef TypeOfType MyType;" entry.227    Object.entries(this.members).forEach(([k, v]) => {228        if (v instanceof IdlTypedef) {229            let defs = v.idlType.union230                ? v.idlType.idlType.map(t => t.idlType)231                : [v.idlType.idlType];232            defs.forEach(d => all_deps.add(d));233        }234    });235    // Add the attribute idlTypes of all the nested members of idls.236    const attrDeps = parsedIdls => {237        return parsedIdls.reduce((deps, parsed) => {238            if (parsed.members) {239                for (const attr of Object.values(parsed.members).filter(m => m.type === 'attribute')) {240                    let attrType = attr.idlType;241                    // Check for generic members (e.g. FrozenArray<MyType>)242                    if (attrType.generic) {243                        deps.add(attrType.generic);244                        attrType = attrType.idlType;245                    }246                    deps.add(attrType.idlType);247                }248            }249            if (parsed.base in this.members) {250                attrDeps([this.members[parsed.base]]).forEach(dep => deps.add(dep));251            }252            return deps;253        }, new Set());254    };255    const testedMembers = Object.values(this.members).filter(m => !m.untested && m.members);256    attrDeps(testedMembers).forEach(dep => all_deps.add(dep));257    const testedPartials = this.partials.filter(m => !m.untested && m.members);258    attrDeps(testedPartials).forEach(dep => all_deps.add(dep));259    if (options && options.except && options.only) {260        throw new IdlHarnessError("The only and except options can't be used together.");261    }262    const defined_or_untested = name => {263        // NOTE: Deps are untested, so we're lenient, and skip re-encountered definitions.264        // e.g. for 'idl' containing A:B, B:C, C:D265        //      array.add_idls(idl, {only: ['A','B']}).266        //      array.add_dependency_idls(idl);267        // B would be encountered as tested, and encountered as a dep, so we ignore.268        return name in this.members269            || this.is_excluded_by_options(name, options);270    }271    // Maps name -> [parsed_idl, ...]272    const process = function(parsed) {273        var deps = [];274        if (parsed.name) {275            deps.push(parsed.name);276        } else if (parsed.type === "includes") {277            deps.push(parsed.target);278            deps.push(parsed.includes);279        }280        deps = deps.filter(function(name) {281            if (!name282                || name === parsed.name && defined_or_untested(name)283                || !all_deps.has(name)) {284                // Flag as skipped, if it's not already processed, so we can285                // come back to it later if we retrospectively call it a dep.286                if (name && !(name in this.members)) {287                    this.skipped.has(name)288                        ? this.skipped.get(name).push(parsed)289                        : this.skipped.set(name, [parsed]);290                }291                return false;292            }293            return true;294        }.bind(this));295        deps.forEach(function(name) {296            if (!new_options.only.includes(name)) {297                new_options.only.push(name);298            }299            const follow_up = new Set();300            for (const dep_type of ["inheritance", "includes"]) {301                if (parsed[dep_type]) {302                    const inheriting = parsed[dep_type];303                    const inheritor = parsed.name || parsed.target;304                    const deps = [inheriting];305                    // For A includes B, we can ignore A, unless B (or some of its306                    // members) is being tested.307                    if (dep_type !== "includes"308                        || inheriting in this.members && !this.members[inheriting].untested309                        || this.partials.some(function(p) {310                                return p.name === inheriting;311                            })) {312                        deps.push(inheritor);313                    }314                    for (const dep of deps) {315                        if (!new_options.only.includes(dep)) {316                            new_options.only.push(dep);317                        }318                        all_deps.add(dep);319                        follow_up.add(dep);320                    }321                }322            }323            for (const deferred of follow_up) {324                if (this.skipped.has(deferred)) {325                    const next = this.skipped.get(deferred);326                    this.skipped.delete(deferred);327                    next.forEach(process);328                }329            }330        }.bind(this));331    }.bind(this);332    for (let parsed of parsed_idls) {333        process(parsed);334    }335    this.mark_as_untested(parsed_idls);336    if (new_options.only.length) {337        this.internal_add_idls(parsed_idls, new_options);338    }339}340IdlArray.prototype.internal_add_idls = function(parsed_idls, options)341{342    /**343     * Internal helper called by add_idls() and add_untested_idls().344     *345     * parsed_idls is an array of objects that come from WebIDLParser.js's346     * "definitions" production.  The add_untested_idls() entry point347     * additionally sets an .untested property on each object (and its348     * .members) so that they'll be skipped by test() -- they'll only be349     * used for base interfaces of tested interfaces, return types, etc.350     *351     * options is a dictionary that can have an only or except member which are352     * arrays. If only is given then only members, partials and interface353     * targets listed will be added, and if except is given only those that354     * aren't listed will be added. Only one of only and except can be used.355     */356    if (options && options.only && options.except)357    {358        throw new IdlHarnessError("The only and except options can't be used together.");359    }360    var should_skip = name => {361        return this.is_excluded_by_options(name, options);362    }363    parsed_idls.forEach(function(parsed_idl)364    {365        var partial_types = [366            "interface",367            "interface mixin",368            "dictionary",369            "namespace",370        ];371        if (parsed_idl.partial && partial_types.includes(parsed_idl.type))372        {373            if (should_skip(parsed_idl.name))374            {375                return;376            }377            this.partials.push(parsed_idl);378            return;379        }380        if (parsed_idl.type == "includes")381        {382            if (should_skip(parsed_idl.target))383            {384                return;385            }386            this.includes.push(parsed_idl);387            return;388        }389        parsed_idl.array = this;390        if (should_skip(parsed_idl.name))391        {392            return;393        }394        if (parsed_idl.name in this.members)395        {396            throw new IdlHarnessError("Duplicate identifier " + parsed_idl.name);397        }398        switch(parsed_idl.type)399        {400        case "interface":401            this.members[parsed_idl.name] =402                new IdlInterface(parsed_idl, /* is_callback = */ false, /* is_mixin = */ false);403            break;404        case "interface mixin":405            this.members[parsed_idl.name] =406                new IdlInterface(parsed_idl, /* is_callback = */ false, /* is_mixin = */ true);407            break;408        case "dictionary":409            // Nothing to test, but we need the dictionary info around for type410            // checks411            this.members[parsed_idl.name] = new IdlDictionary(parsed_idl);412            break;413        case "typedef":414            this.members[parsed_idl.name] = new IdlTypedef(parsed_idl);415            break;416        case "callback":417            this.members[parsed_idl.name] = new IdlCallback(parsed_idl);418            break;419        case "enum":420            this.members[parsed_idl.name] = new IdlEnum(parsed_idl);421            break;422        case "callback interface":423            this.members[parsed_idl.name] =424                new IdlInterface(parsed_idl, /* is_callback = */ true, /* is_mixin = */ false);425            break;426        case "namespace":427            this.members[parsed_idl.name] = new IdlNamespace(parsed_idl);428            break;429        default:430            throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported";431        }432    }.bind(this));433};434IdlArray.prototype.add_objects = function(dict)435{436    /** Entry point.  See documentation at beginning of file. */437    for (var k in dict)438    {439        if (k in this.objects)440        {441            this.objects[k] = this.objects[k].concat(dict[k]);442        }443        else444        {445            this.objects[k] = dict[k];446        }447    }448};449IdlArray.prototype.prevent_multiple_testing = function(name)450{451    /** Entry point.  See documentation at beginning of file. */452    this.members[name].prevent_multiple_testing = true;453};454IdlArray.prototype.is_json_type = function(type)455{456    /**457     * Checks whether type is a JSON type as per458     * https://webidl.spec.whatwg.org/#dfn-json-types459     */460    var idlType = type.idlType;461    if (type.generic == "Promise") { return false; }462    //  nullable and annotated types don't need to be handled separately,463    //  as webidl2 doesn't represent them wrapped-up (as they're described464    //  in WebIDL).465    // union and record types466    if (type.union || type.generic == "record") {467        return idlType.every(this.is_json_type, this);468    }469    // sequence types470    if (type.generic == "sequence" || type.generic == "FrozenArray") {471        return this.is_json_type(idlType[0]);472    }473    if (typeof idlType != "string") { throw new Error("Unexpected type " + JSON.stringify(idlType)); }474    switch (idlType)475    {476       //  Numeric types477       case "byte":478       case "octet":479       case "short":480       case "unsigned short":481       case "long":482       case "unsigned long":483       case "long long":484       case "unsigned long long":485       case "float":486       case "double":487       case "unrestricted float":488       case "unrestricted double":489       // boolean490       case "boolean":491       // string types492       case "DOMString":493       case "ByteString":494       case "USVString":495       // object type496       case "object":497           return true;498       case "Error":499       case "DOMException":500       case "Int8Array":501       case "Int16Array":502       case "Int32Array":503       case "Uint8Array":504       case "Uint16Array":505       case "Uint32Array":506       case "Uint8ClampedArray":507       case "BigInt64Array":508       case "BigUint64Array":509       case "Float32Array":510       case "Float64Array":511       case "ArrayBuffer":512       case "DataView":513       case "any":514           return false;515       default:516           var thing = this.members[idlType];517           if (!thing) { throw new Error("Type " + idlType + " not found"); }518           if (thing instanceof IdlEnum) { return true; }519           if (thing instanceof IdlTypedef) {520               return this.is_json_type(thing.idlType);521           }522           //  dictionaries where all of their members are JSON types523           if (thing instanceof IdlDictionary) {524               const map = new Map();525               for (const dict of thing.get_reverse_inheritance_stack()) {526                   for (const m of dict.members) {527                       map.set(m.name, m.idlType);528                   }529               }530               return Array.from(map.values()).every(this.is_json_type, this);531           }532           //  interface types that have a toJSON operation declared on themselves or533           //  one of their inherited interfaces.534           if (thing instanceof IdlInterface) {535               var base;536               while (thing)537               {538                   if (thing.has_to_json_regular_operation()) { return true; }539                   var mixins = this.includes[thing.name];540                   if (mixins) {541                       mixins = mixins.map(function(id) {542                           var mixin = this.members[id];543                           if (!mixin) {544                               throw new Error("Interface " + id + " not found (implemented by " + thing.name + ")");545                           }546                           return mixin;547                       }, this);548                       if (mixins.some(function(m) { return m.has_to_json_regular_operation() } )) { return true; }549                   }550                   if (!thing.base) { return false; }551                   base = this.members[thing.base];552                   if (!base) {553                       throw new Error("Interface " + thing.base + " not found (inherited by " + thing.name + ")");554                   }555                   thing = base;556               }557               return false;558           }559           return false;560    }561};562function exposure_set(object, default_set) {563    var exposed = object.extAttrs && object.extAttrs.filter(a => a.name === "Exposed");564    if (exposed && exposed.length > 1) {565        throw new IdlHarnessError(566            `Multiple 'Exposed' extended attributes on ${object.name}`);567    }568    let result = default_set || ["Window"];569    if (result && !(result instanceof Set)) {570        result = new Set(result);571    }572    if (exposed && exposed.length) {573        const { rhs } = exposed[0];574        // Could be a list or a string.575        const set =576            rhs.type === "*" ?577            [ "*" ] :578            rhs.type === "identifier-list" ?579            rhs.value.map(id => id.value) :580            [ rhs.value ];581        result = new Set(set);582    }583    if (result && result.has("*")) {584        return "*";585    }586    if (result && result.has("Worker")) {587        result.delete("Worker");588        result.add("DedicatedWorker");589        result.add("ServiceWorker");590        result.add("SharedWorker");591    }592    return result;593}594function exposed_in(globals) {595    if (globals === "*") {596        return true;597    }598    if ('Window' in self) {599        return globals.has("Window");600    }601    if ('DedicatedWorkerGlobalScope' in self &&602        self instanceof DedicatedWorkerGlobalScope) {603        return globals.has("DedicatedWorker");604    }605    if ('SharedWorkerGlobalScope' in self &&606        self instanceof SharedWorkerGlobalScope) {607        return globals.has("SharedWorker");608    }609    if ('ServiceWorkerGlobalScope' in self &&610        self instanceof ServiceWorkerGlobalScope) {611        return globals.has("ServiceWorker");612    }613    if (Object.getPrototypeOf(self) === Object.prototype) {614        // ShadowRealm - only exposed with `"*"`.615        return false;616    }617    throw new IdlHarnessError("Unexpected global object");618}619/**620 * Asserts that the given error message is thrown for the given function.621 * @param {string|IdlHarnessError} error Expected Error message.622 * @param {Function} idlArrayFunc Function operating on an IdlArray that should throw.623 */624IdlArray.prototype.assert_throws = function(error, idlArrayFunc)625{626    try {627        idlArrayFunc.call(this, this);628    } catch (e) {629        if (e instanceof AssertionError) {630            throw e;631        }632        // Assertions for behaviour of the idlharness.js engine.633        if (error instanceof IdlHarnessError) {634            error = error.message;635        }636        if (e.message !== error) {637            throw new IdlHarnessError(`${idlArrayFunc} threw "${e}", not the expected IdlHarnessError "${error}"`);638        }639        return;640    }641    throw new IdlHarnessError(`${idlArrayFunc} did not throw the expected IdlHarnessError`);642}643IdlArray.prototype.test = function()644{645    /** Entry point.  See documentation at beginning of file. */646    // First merge in all partial definitions and interface mixins.647    this.merge_partials();648    this.merge_mixins();649    // Assert B defined for A : B650    for (const member of Object.values(this.members).filter(m => m.base)) {651        const lhs = member.name;652        const rhs = member.base;653        if (!(rhs in this.members)) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${rhs} is undefined.`);654        const lhs_is_interface = this.members[lhs] instanceof IdlInterface;655        const rhs_is_interface = this.members[rhs] instanceof IdlInterface;656        if (rhs_is_interface != lhs_is_interface) {657            if (!lhs_is_interface) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${lhs} is not an interface.`);658            if (!rhs_is_interface) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${rhs} is not an interface.`);659        }660        // Check for circular dependencies.661        member.get_reverse_inheritance_stack();662    }663    Object.getOwnPropertyNames(this.members).forEach(function(memberName) {664        var member = this.members[memberName];665        if (!(member instanceof IdlInterface)) {666            return;667        }668        var globals = exposure_set(member);669        member.exposed = exposed_in(globals);670        member.exposureSet = globals;671    }.bind(this));672    // Now run test() on every member, and test_object() for every object.673    for (var name in this.members)674    {675        this.members[name].test();676        if (name in this.objects)677        {678            const objects = this.objects[name];679            if (!objects || !Array.isArray(objects)) {680                throw new IdlHarnessError(`Invalid or empty objects for member ${name}`);681            }682            objects.forEach(function(str)683            {684                if (!this.members[name] || !(this.members[name] instanceof IdlInterface)) {685                    throw new IdlHarnessError(`Invalid object member name ${name}`);686                }687                this.members[name].test_object(str);688            }.bind(this));689        }690    }691};692IdlArray.prototype.merge_partials = function()693{694    const testedPartials = new Map();695    this.partials.forEach(function(parsed_idl)696    {697        const originalExists = parsed_idl.name in this.members698            && (this.members[parsed_idl.name] instanceof IdlInterface699                || this.members[parsed_idl.name] instanceof IdlDictionary700                || this.members[parsed_idl.name] instanceof IdlNamespace);701        // Ensure unique test name in case of multiple partials.702        let partialTestName = parsed_idl.name;703        let partialTestCount = 1;704        if (testedPartials.has(parsed_idl.name)) {705            partialTestCount += testedPartials.get(parsed_idl.name);706            partialTestName = `${partialTestName}[${partialTestCount}]`;707        }708        testedPartials.set(parsed_idl.name, partialTestCount);709        if (!parsed_idl.untested) {710            test(function () {711                assert_true(originalExists, `Original ${parsed_idl.type} should be defined`);712                var expected;713                switch (parsed_idl.type) {714                    case 'dictionary': expected = IdlDictionary; break;715                    case 'namespace': expected = IdlNamespace; break;716                    case 'interface':717                    case 'interface mixin':718                    default:719                        expected = IdlInterface; break;720                }721                assert_true(722                    expected.prototype.isPrototypeOf(this.members[parsed_idl.name]),723                    `Original ${parsed_idl.name} definition should have type ${parsed_idl.type}`);724            }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: original ${parsed_idl.type} defined`);725        }726        if (!originalExists) {727            // Not good.. but keep calm and carry on.728            return;729        }730        if (parsed_idl.extAttrs)731        {732            // Special-case "Exposed". Must be a subset of original interface's exposure.733            // Exposed on a partial is the equivalent of having the same Exposed on all nested members.734            // See https://github.com/heycam/webidl/issues/154 for discrepency between Exposed and735            // other extended attributes on partial interfaces.736            const exposureAttr = parsed_idl.extAttrs.find(a => a.name === "Exposed");737            if (exposureAttr) {738                if (!parsed_idl.untested) {739                    test(function () {740                        const partialExposure = exposure_set(parsed_idl);741                        const memberExposure = exposure_set(this.members[parsed_idl.name]);742                        if (memberExposure === "*") {743                            return;744                        }745                        if (partialExposure === "*") {746                            throw new IdlHarnessError(747                                `Partial ${parsed_idl.name} ${parsed_idl.type} is exposed everywhere, the original ${parsed_idl.type} is not.`);748                        }749                        partialExposure.forEach(name => {750                            if (!memberExposure || !memberExposure.has(name)) {751                                throw new IdlHarnessError(752                                    `Partial ${parsed_idl.name} ${parsed_idl.type} is exposed to '${name}', the original ${parsed_idl.type} is not.`);753                            }754                        });755                    }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: valid exposure set`);756                }757                parsed_idl.members.forEach(function (member) {758                    member.extAttrs.push(exposureAttr);759                }.bind(this));760            }761            parsed_idl.extAttrs.forEach(function(extAttr)762            {763                // "Exposed" already handled above.764                if (extAttr.name === "Exposed") {765                    return;766                }767                this.members[parsed_idl.name].extAttrs.push(extAttr);768            }.bind(this));769        }770        if (parsed_idl.members.length) {771            test(function () {772                var clash = parsed_idl.members.find(function(member) {773                    return this.members[parsed_idl.name].members.find(function(m) {774                        return this.are_duplicate_members(m, member);775                    }.bind(this));776                }.bind(this));777                parsed_idl.members.forEach(function(member)778                {779                    this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member));780                }.bind(this));781                assert_true(!clash, "member " + (clash && clash.name) + " is unique");782            }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: member names are unique`);783        }784    }.bind(this));785    this.partials = [];786}787IdlArray.prototype.merge_mixins = function()788{789    for (const parsed_idl of this.includes)790    {791        const lhs = parsed_idl.target;792        const rhs = parsed_idl.includes;793        var errStr = lhs + " includes " + rhs + ", but ";794        if (!(lhs in this.members)) throw errStr + lhs + " is undefined.";795        if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface.";796        if (!(rhs in this.members)) throw errStr + rhs + " is undefined.";797        if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface.";798        if (this.members[rhs].members.length) {799            test(function () {800                var clash = this.members[rhs].members.find(function(member) {801                    return this.members[lhs].members.find(function(m) {802                        return this.are_duplicate_members(m, member);803                    }.bind(this));804                }.bind(this));805                this.members[rhs].members.forEach(function(member) {806                    assert_true(807                        this.members[lhs].members.every(m => !this.are_duplicate_members(m, member)),808                        "member " + member.name + " is unique");809                    this.members[lhs].members.push(new IdlInterfaceMember(member));810                }.bind(this));811                assert_true(!clash, "member " + (clash && clash.name) + " is unique");812            }.bind(this), lhs + " includes " + rhs + ": member names are unique");813        }814    }815    this.includes = [];816}817IdlArray.prototype.are_duplicate_members = function(m1, m2) {818    if (m1.name !== m2.name) {819        return false;820    }821    if (m1.type === 'operation' && m2.type === 'operation'822        && m1.arguments.length !== m2.arguments.length) {823        // Method overload. TODO: Deep comparison of arguments.824        return false;825    }826    return true;827}828IdlArray.prototype.assert_type_is = function(value, type)829{830    if (type.idlType in this.members831    && this.members[type.idlType] instanceof IdlTypedef) {832        this.assert_type_is(value, this.members[type.idlType].idlType);833        return;834    }835    if (type.nullable && value === null)836    {837        // This is fine838        return;839    }840    if (type.union) {841        for (var i = 0; i < type.idlType.length; i++) {842            try {843                this.assert_type_is(value, type.idlType[i]);844                // No AssertionError, so we match one type in the union845                return;846            } catch(e) {847                if (e instanceof AssertionError) {848                    // We didn't match this type, let's try some others849                    continue;850                }851                throw e;852            }853        }854        // TODO: Is there a nice way to list the union's types in the message?855        assert_true(false, "Attribute has value " + format_value(value)856                    + " which doesn't match any of the types in the union");857    }858    /**859     * Helper function that tests that value is an instance of type according860     * to the rules of WebIDL.  value is any JavaScript value, and type is an861     * object produced by WebIDLParser.js' "type" production.  That production862     * is fairly elaborate due to the complexity of WebIDL's types, so it's863     * best to look at the grammar to figure out what properties it might have.864     */865    if (type.idlType == "any")866    {867        // No assertions to make868        return;869    }870    if (type.array)871    {872        // TODO: not supported yet873        return;874    }875    if (type.generic === "sequence" || type.generic == "ObservableArray")876    {877        assert_true(Array.isArray(value), "should be an Array");878        if (!value.length)879        {880            // Nothing we can do.881            return;882        }883        this.assert_type_is(value[0], type.idlType[0]);884        return;885    }886    if (type.generic === "Promise") {887        assert_true("then" in value, "Attribute with a Promise type should have a then property");888        // TODO: Ideally, we would check on project fulfillment889        // that we get the right type890        // but that would require making the type check async891        return;892    }893    if (type.generic === "FrozenArray") {894        assert_true(Array.isArray(value), "Value should be array");895        assert_true(Object.isFrozen(value), "Value should be frozen");896        if (!value.length)897        {898            // Nothing we can do.899            return;900        }901        this.assert_type_is(value[0], type.idlType[0]);902        return;903    }904    type = Array.isArray(type.idlType) ? type.idlType[0] : type.idlType;905    switch(type)906    {907        case "undefined":908            assert_equals(value, undefined);909            return;910        case "boolean":911            assert_equals(typeof value, "boolean");912            return;913        case "byte":914            assert_equals(typeof value, "number");915            assert_equals(value, Math.floor(value), "should be an integer");916            assert_true(-128 <= value && value <= 127, "byte " + value + " should be in range [-128, 127]");917            return;918        case "octet":919            assert_equals(typeof value, "number");920            assert_equals(value, Math.floor(value), "should be an integer");921            assert_true(0 <= value && value <= 255, "octet " + value + " should be in range [0, 255]");922            return;923        case "short":924            assert_equals(typeof value, "number");925            assert_equals(value, Math.floor(value), "should be an integer");926            assert_true(-32768 <= value && value <= 32767, "short " + value + " should be in range [-32768, 32767]");927            return;928        case "unsigned short":929            assert_equals(typeof value, "number");930            assert_equals(value, Math.floor(value), "should be an integer");931            assert_true(0 <= value && value <= 65535, "unsigned short " + value + " should be in range [0, 65535]");932            return;933        case "long":934            assert_equals(typeof value, "number");935            assert_equals(value, Math.floor(value), "should be an integer");936            assert_true(-2147483648 <= value && value <= 2147483647, "long " + value + " should be in range [-2147483648, 2147483647]");937            return;938        case "unsigned long":939            assert_equals(typeof value, "number");940            assert_equals(value, Math.floor(value), "should be an integer");941            assert_true(0 <= value && value <= 4294967295, "unsigned long " + value + " should be in range [0, 4294967295]");942            return;943        case "long long":944            assert_equals(typeof value, "number");945            return;946        case "unsigned long long":947        case "DOMTimeStamp":948            assert_equals(typeof value, "number");949            assert_true(0 <= value, "unsigned long long should be positive");950            return;951        case "float":952            assert_equals(typeof value, "number");953            assert_equals(value, Math.fround(value), "float rounded to 32-bit float should be itself");954            assert_not_equals(value, Infinity);955            assert_not_equals(value, -Infinity);956            assert_not_equals(value, NaN);957            return;958        case "DOMHighResTimeStamp":959        case "double":960            assert_equals(typeof value, "number");961            assert_not_equals(value, Infinity);962            assert_not_equals(value, -Infinity);963            assert_not_equals(value, NaN);964            return;965        case "unrestricted float":966            assert_equals(typeof value, "number");967            assert_equals(value, Math.fround(value), "unrestricted float rounded to 32-bit float should be itself");968            return;969        case "unrestricted double":970            assert_equals(typeof value, "number");971            return;972        case "DOMString":973            assert_equals(typeof value, "string");974            return;975        case "ByteString":976            assert_equals(typeof value, "string");977            assert_regexp_match(value, /^[\x00-\x7F]*$/);978            return;979        case "USVString":980            assert_equals(typeof value, "string");981            assert_regexp_match(value, /^([\x00-\ud7ff\ue000-\uffff]|[\ud800-\udbff][\udc00-\udfff])*$/);982            return;983        case "ArrayBufferView":984            assert_true(ArrayBuffer.isView(value));985            return;986        case "object":987            assert_in_array(typeof value, ["object", "function"], "wrong type: not object or function");988            return;989    }990    // This is a catch-all for any IDL type name which follows JS class991    // semantics. This includes some non-interface IDL types (e.g. Int8Array,992    // Function, ...), as well as any interface types that are not in the IDL993    // that is fed to the harness. If an IDL type does not follow JS class994    // semantics then it should go in the switch statement above. If an IDL995    // type needs full checking, then the test should include it in the IDL it996    // feeds to the harness.997    if (!(type in this.members))998    {999        assert_true(value instanceof self[type], "wrong type: not a " + type);1000        return;1001    }1002    if (this.members[type] instanceof IdlInterface)1003    {1004        // We don't want to run the full1005        // IdlInterface.prototype.test_instance_of, because that could result1006        // in an infinite loop.  TODO: This means we don't have tests for1007        // LegacyNoInterfaceObject interfaces, and we also can't test objects1008        // that come from another self.1009        assert_in_array(typeof value, ["object", "function"], "wrong type: not object or function");1010        if (value instanceof Object1011        && !this.members[type].has_extended_attribute("LegacyNoInterfaceObject")1012        && type in self)1013        {1014            assert_true(value instanceof self[type], "instanceof " + type);1015        }1016    }1017    else if (this.members[type] instanceof IdlEnum)1018    {1019        assert_equals(typeof value, "string");1020    }1021    else if (this.members[type] instanceof IdlDictionary)1022    {1023        // TODO: Test when we actually have something to test this on1024    }1025    else if (this.members[type] instanceof IdlCallback)1026    {1027        assert_equals(typeof value, "function");1028    }1029    else1030    {1031        throw new IdlHarnessError("Type " + type + " isn't an interface, callback or dictionary");1032    }1033};1034/// IdlObject ///1035function IdlObject() {}1036IdlObject.prototype.test = function()1037{1038    /**1039     * By default, this does nothing, so no actual tests are run for IdlObjects1040     * that don't define any (e.g., IdlDictionary at the time of this writing).1041     */1042};1043IdlObject.prototype.has_extended_attribute = function(name)1044{1045    /**1046     * This is only meaningful for things that support extended attributes,1047     * such as interfaces, exceptions, and members.1048     */1049    return this.extAttrs.some(function(o)1050    {1051        return o.name == name;1052    });1053};1054/// IdlDictionary ///1055// Used for IdlArray.prototype.assert_type_is1056function IdlDictionary(obj)1057{1058    /**1059     * obj is an object produced by the WebIDLParser.js "dictionary"1060     * production.1061     */1062    /** Self-explanatory. */1063    this.name = obj.name;1064    /** A back-reference to our IdlArray. */1065    this.array = obj.array;1066    /** An array of objects produced by the "dictionaryMember" production. */1067    this.members = obj.members;1068    /**1069     * The name (as a string) of the dictionary type we inherit from, or null1070     * if there is none.1071     */1072    this.base = obj.inheritance;1073}1074IdlDictionary.prototype = Object.create(IdlObject.prototype);1075IdlDictionary.prototype.get_reverse_inheritance_stack = function() {1076    return IdlInterface.prototype.get_reverse_inheritance_stack.call(this);1077};1078/// IdlInterface ///1079function IdlInterface(obj, is_callback, is_mixin)1080{1081    /**1082     * obj is an object produced by the WebIDLParser.js "interface" production.1083     */1084    /** Self-explanatory. */1085    this.name = obj.name;1086    /** A back-reference to our IdlArray. */1087    this.array = obj.array;1088    /**1089     * An indicator of whether we should run tests on the interface object and1090     * interface prototype object. Tests on members are controlled by .untested1091     * on each member, not this.1092     */1093    this.untested = obj.untested;1094    /** An array of objects produced by the "ExtAttr" production. */1095    this.extAttrs = obj.extAttrs;1096    /** An array of IdlInterfaceMembers. */1097    this.members = obj.members.map(function(m){return new IdlInterfaceMember(m); });1098    if (this.has_extended_attribute("LegacyUnforgeable")) {1099        this.members1100            .filter(function(m) { return m.special !== "static" && (m.type == "attribute" || m.type == "operation"); })1101            .forEach(function(m) { return m.isUnforgeable = true; });1102    }1103    /**1104     * The name (as a string) of the type we inherit from, or null if there is1105     * none.1106     */1107    this.base = obj.inheritance;1108    this._is_callback = is_callback;1109    this._is_mixin = is_mixin;1110}1111IdlInterface.prototype = Object.create(IdlObject.prototype);1112IdlInterface.prototype.is_callback = function()1113{1114    return this._is_callback;1115};1116IdlInterface.prototype.is_mixin = function()1117{1118    return this._is_mixin;1119};1120IdlInterface.prototype.has_constants = function()1121{1122    return this.members.some(function(member) {1123        return member.type === "const";1124    });1125};1126IdlInterface.prototype.get_unscopables = function()1127{1128    return this.members.filter(function(member) {1129        return member.isUnscopable;1130    });1131};1132IdlInterface.prototype.is_global = function()1133{1134    return this.extAttrs.some(function(attribute) {1135        return attribute.name === "Global";1136    });1137};1138/**1139 * Value of the LegacyNamespace extended attribute, if any.1140 *1141 * https://webidl.spec.whatwg.org/#LegacyNamespace1142 */1143IdlInterface.prototype.get_legacy_namespace = function()1144{1145    var legacyNamespace = this.extAttrs.find(function(attribute) {1146        return attribute.name === "LegacyNamespace";1147    });1148    return legacyNamespace ? legacyNamespace.rhs.value : undefined;1149};1150IdlInterface.prototype.get_interface_object_owner = function()1151{1152    var legacyNamespace = this.get_legacy_namespace();1153    return legacyNamespace ? self[legacyNamespace] : self;1154};1155IdlInterface.prototype.should_have_interface_object = function()1156{1157    // "For every interface that is exposed in a given ECMAScript global1158    // environment and:1159    // * is a callback interface that has constants declared on it, or1160    // * is a non-callback interface that is not declared with the1161    //   [LegacyNoInterfaceObject] extended attribute,1162    // a corresponding property MUST exist on the ECMAScript global object.1163    return this.is_callback() ? this.has_constants() : !this.has_extended_attribute("LegacyNoInterfaceObject");1164};1165IdlInterface.prototype.assert_interface_object_exists = function()1166{1167    var owner = this.get_legacy_namespace() || "self";1168    assert_own_property(self[owner], this.name, owner + " does not have own property " + format_value(this.name));1169};1170IdlInterface.prototype.get_interface_object = function() {1171    if (!this.should_have_interface_object()) {1172        var reason = this.is_callback() ? "lack of declared constants" : "declared [LegacyNoInterfaceObject] attribute";1173        throw new IdlHarnessError(this.name + " has no interface object due to " + reason);1174    }1175    return this.get_interface_object_owner()[this.name];1176};1177IdlInterface.prototype.get_qualified_name = function() {1178    // https://webidl.spec.whatwg.org/#qualified-name1179    var legacyNamespace = this.get_legacy_namespace();1180    if (legacyNamespace) {1181        return legacyNamespace + "." + this.name;1182    }1183    return this.name;1184};1185IdlInterface.prototype.has_to_json_regular_operation = function() {1186    return this.members.some(function(m) {1187        return m.is_to_json_regular_operation();1188    });1189};1190IdlInterface.prototype.has_default_to_json_regular_operation = function() {1191    return this.members.some(function(m) {1192        return m.is_to_json_regular_operation() && m.has_extended_attribute("Default");1193    });1194};1195/**1196 * Implementation of https://webidl.spec.whatwg.org/#create-an-inheritance-stack1197 * with the order reversed.1198 *1199 * The order is reversed so that the base class comes first in the list, because1200 * this is what all call sites need.1201 *1202 * So given:1203 *1204 *   A : B {};1205 *   B : C {};1206 *   C {};1207 *1208 * then A.get_reverse_inheritance_stack() returns [C, B, A],1209 * and B.get_reverse_inheritance_stack() returns [C, B].1210 *1211 * Note: as dictionary inheritance is expressed identically by the AST,1212 * this works just as well for getting a stack of inherited dictionaries.1213 */1214IdlInterface.prototype.get_reverse_inheritance_stack = function() {1215    const stack = [this];1216    let idl_interface = this;1217    while (idl_interface.base) {1218        const base = this.array.members[idl_interface.base];1219        if (!base) {1220            throw new Error(idl_interface.type + " " + idl_interface.base + " not found (inherited by " + idl_interface.name + ")");1221        } else if (stack.indexOf(base) > -1) {1222            stack.unshift(base);1223            const dep_chain = stack.map(i => i.name).join(',');1224            throw new IdlHarnessError(`${this.name} has a circular dependency: ${dep_chain}`);1225        }1226        idl_interface = base;1227        stack.unshift(idl_interface);1228    }1229    return stack;1230};1231/**1232 * Implementation of1233 * https://webidl.spec.whatwg.org/#default-tojson-operation1234 * for testing purposes.1235 *1236 * Collects the IDL types of the attributes that meet the criteria1237 * for inclusion in the default toJSON operation for easy1238 * comparison with actual value1239 */1240IdlInterface.prototype.default_to_json_operation = function() {1241    const map = new Map()1242    let isDefault = false;1243    for (const I of this.get_reverse_inheritance_stack()) {1244        if (I.has_default_to_json_regular_operation()) {1245            isDefault = true;1246            for (const m of I.members) {1247                if (m.special !== "static" && m.type == "attribute" && I.array.is_json_type(m.idlType)) {1248                    map.set(m.name, m.idlType);1249                }1250            }1251        } else if (I.has_to_json_regular_operation()) {1252            isDefault = false;1253        }1254    }1255    return isDefault ? map : null;1256};1257IdlInterface.prototype.test = function()1258{1259    if (this.has_extended_attribute("LegacyNoInterfaceObject") || this.is_mixin())1260    {1261        // No tests to do without an instance.  TODO: We should still be able1262        // to run tests on the prototype object, if we obtain one through some1263        // other means.1264        return;1265    }1266    // If the interface object is not exposed, only test that. Members can't be1267    // tested either, but objects could still be tested in |test_object|.1268    if (!this.exposed)1269    {1270        if (!this.untested)1271        {1272            subsetTestByKey(this.name, test, function() {1273                assert_false(this.name in self);1274            }.bind(this), this.name + " interface: existence and properties of interface object");1275        }1276        return;1277    }1278    if (!this.untested)1279    {1280        // First test things to do with the exception/interface object and1281        // exception/interface prototype object.1282        this.test_self();1283    }1284    // Then test things to do with its members (constants, fields, attributes,1285    // operations, . . .).  These are run even if .untested is true, because1286    // members might themselves be marked as .untested.  This might happen to1287    // interfaces if the interface itself is untested but a partial interface1288    // that extends it is tested -- then the interface itself and its initial1289    // members will be marked as untested, but the members added by the partial1290    // interface are still tested.1291    this.test_members();1292};1293IdlInterface.prototype.constructors = function()1294{1295    return this.members1296        .filter(function(m) { return m.type == "constructor"; });1297}1298IdlInterface.prototype.test_self = function()1299{1300    subsetTestByKey(this.name, test, function()1301    {1302        if (!this.should_have_interface_object()) {1303            return;1304        }1305        // The name of the property is the identifier of the interface, and its1306        // value is an object called the interface object.1307        // The property has the attributes { [[Writable]]: true,1308        // [[Enumerable]]: false, [[Configurable]]: true }."1309        // TODO: Should we test here that the property is actually writable1310        // etc., or trust getOwnPropertyDescriptor?1311        this.assert_interface_object_exists();1312        var desc = Object.getOwnPropertyDescriptor(this.get_interface_object_owner(), this.name);1313        assert_false("get" in desc, "self's property " + format_value(this.name) + " should not have a getter");1314        assert_false("set" in desc, "self's property " + format_value(this.name) + " should not have a setter");1315        assert_true(desc.writable, "self's property " + format_value(this.name) + " should be writable");1316        assert_false(desc.enumerable, "self's property " + format_value(this.name) + " should not be enumerable");1317        assert_true(desc.configurable, "self's property " + format_value(this.name) + " should be configurable");1318        if (this.is_callback()) {1319            // "The internal [[Prototype]] property of an interface object for1320            // a callback interface must be the Function.prototype object."1321            assert_equals(Object.getPrototypeOf(this.get_interface_object()), Function.prototype,1322                          "prototype of self's property " + format_value(this.name) + " is not Object.prototype");1323            return;1324        }1325        // "The interface object for a given non-callback interface is a1326        // function object."1327        // "If an object is defined to be a function object, then it has1328        // characteristics as follows:"1329        // Its [[Prototype]] internal property is otherwise specified (see1330        // below).1331        // "* Its [[Get]] internal property is set as described in ECMA-2621332        //    section 9.1.8."1333        // Not much to test for this.1334        // "* Its [[Construct]] internal property is set as described in1335        //    ECMA-262 section 19.2.2.3."1336        // "* Its @@hasInstance property is set as described in ECMA-2621337        //    section 19.2.3.8, unless otherwise specified."1338        // TODO1339        // ES6 (rev 30) 19.1.3.6:1340        // "Else, if O has a [[Call]] internal method, then let builtinTag be1341        // "Function"."1342        assert_class_string(this.get_interface_object(), "Function", "class string of " + this.name);1343        // "The [[Prototype]] internal property of an interface object for a1344        // non-callback interface is determined as follows:"1345        var prototype = Object.getPrototypeOf(this.get_interface_object());1346        if (this.base) {1347            // "* If the interface inherits from some other interface, the1348            //    value of [[Prototype]] is the interface object for that other1349            //    interface."1350            var inherited_interface = this.array.members[this.base];1351            if (!inherited_interface.has_extended_attribute("LegacyNoInterfaceObject")) {1352                inherited_interface.assert_interface_object_exists();1353                assert_equals(prototype, inherited_interface.get_interface_object(),1354                              'prototype of ' + this.name + ' is not ' +1355                              this.base);1356            }1357        } else {1358            // "If the interface doesn't inherit from any other interface, the1359            // value of [[Prototype]] is %FunctionPrototype% ([ECMA-262],1360            // section 6.1.7.4)."1361            assert_equals(prototype, Function.prototype,1362                          "prototype of self's property " + format_value(this.name) + " is not Function.prototype");1363        }1364        // Always test for [[Construct]]:1365        // https://github.com/heycam/webidl/issues/6981366        assert_true(isConstructor(this.get_interface_object()), "interface object must pass IsConstructor check");1367        var interface_object = this.get_interface_object();1368        assert_throws_js(globalOf(interface_object).TypeError, function() {1369            interface_object();1370        }, "interface object didn't throw TypeError when called as a function");1371        if (!this.constructors().length) {1372            assert_throws_js(globalOf(interface_object).TypeError, function() {1373                new interface_object();1374            }, "interface object didn't throw TypeError when called as a constructor");1375        }1376    }.bind(this), this.name + " interface: existence and properties of interface object");1377    if (this.should_have_interface_object() && !this.is_callback()) {1378        subsetTestByKey(this.name, test, function() {1379            // This function tests WebIDL as of 2014-10-25.1380            // https://webidl.spec.whatwg.org/#es-interface-call1381            this.assert_interface_object_exists();1382            // "Interface objects for non-callback interfaces MUST have a1383            // property named âlengthâ with attributes { [[Writable]]: false,1384            // [[Enumerable]]: false, [[Configurable]]: true } whose value is1385            // a Number."1386            assert_own_property(this.get_interface_object(), "length");1387            var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "length");1388            assert_false("get" in desc, this.name + ".length should not have a getter");1389            assert_false("set" in desc, this.name + ".length should not have a setter");1390            assert_false(desc.writable, this.name + ".length should not be writable");1391            assert_false(desc.enumerable, this.name + ".length should not be enumerable");1392            assert_true(desc.configurable, this.name + ".length should be configurable");1393            var constructors = this.constructors();1394            var expected_length = minOverloadLength(constructors);1395            assert_equals(this.get_interface_object().length, expected_length, "wrong value for " + this.name + ".length");1396        }.bind(this), this.name + " interface object length");1397    }1398    if (this.should_have_interface_object()) {1399        subsetTestByKey(this.name, test, function() {1400            // This function tests WebIDL as of 2015-11-17.1401            // https://webidl.spec.whatwg.org/#interface-object1402            this.assert_interface_object_exists();1403            // "All interface objects must have a property named ânameâ with1404            // attributes { [[Writable]]: false, [[Enumerable]]: false,1405            // [[Configurable]]: true } whose value is the identifier of the1406            // corresponding interface."1407            assert_own_property(this.get_interface_object(), "name");1408            var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "name");1409            assert_false("get" in desc, this.name + ".name should not have a getter");1410            assert_false("set" in desc, this.name + ".name should not have a setter");1411            assert_false(desc.writable, this.name + ".name should not be writable");1412            assert_false(desc.enumerable, this.name + ".name should not be enumerable");1413            assert_true(desc.configurable, this.name + ".name should be configurable");1414            assert_equals(this.get_interface_object().name, this.name, "wrong value for " + this.name + ".name");1415        }.bind(this), this.name + " interface object name");1416    }1417    if (this.has_extended_attribute("LegacyWindowAlias")) {1418        subsetTestByKey(this.name, test, function()1419        {1420            var aliasAttrs = this.extAttrs.filter(function(o) { return o.name === "LegacyWindowAlias"; });1421            if (aliasAttrs.length > 1) {1422                throw new IdlHarnessError("Invalid IDL: multiple LegacyWindowAlias extended attributes on " + this.name);1423            }1424            if (this.is_callback()) {1425                throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on non-interface " + this.name);1426            }1427            if (!(this.exposureSet === "*" || this.exposureSet.has("Window"))) {1428                throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " which is not exposed in Window");1429            }1430            // TODO: when testing of [LegacyNoInterfaceObject] interfaces is supported,1431            // check that it's not specified together with LegacyWindowAlias.1432            // TODO: maybe check that [LegacyWindowAlias] is not specified on a partial interface.1433            var rhs = aliasAttrs[0].rhs;1434            if (!rhs) {1435                throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " without identifier");1436            }1437            var aliases;1438            if (rhs.type === "identifier-list") {1439                aliases = rhs.value.map(id => id.value);1440            } else { // rhs.type === identifier1441                aliases = [ rhs.value ];1442            }1443            // OK now actually check the aliases...1444            var alias;1445            if (exposed_in(exposure_set(this, this.exposureSet)) && 'document' in self) {1446                for (alias of aliases) {1447                    assert_true(alias in self, alias + " should exist");1448                    assert_equals(self[alias], this.get_interface_object(), "self." + alias + " should be the same value as self." + this.get_qualified_name());1449                    var desc = Object.getOwnPropertyDescriptor(self, alias);1450                    assert_equals(desc.value, this.get_interface_object(), "wrong value in " + alias + " property descriptor");1451                    assert_true(desc.writable, alias + " should be writable");1452                    assert_false(desc.enumerable, alias + " should not be enumerable");1453                    assert_true(desc.configurable, alias + " should be configurable");1454                    assert_false('get' in desc, alias + " should not have a getter");1455                    assert_false('set' in desc, alias + " should not have a setter");1456                }1457            } else {1458                for (alias of aliases) {1459                    assert_false(alias in self, alias + " should not exist");1460                }1461            }1462        }.bind(this), this.name + " interface: legacy window alias");1463    }1464    if (this.has_extended_attribute("LegacyFactoryFunction")) {1465        var constructors = this.extAttrs1466            .filter(function(attr) { return attr.name == "LegacyFactoryFunction"; });1467        if (constructors.length !== 1) {1468            throw new IdlHarnessError("Internal error: missing support for multiple LegacyFactoryFunction extended attributes");1469        }1470        var constructor = constructors[0];1471        var min_length = minOverloadLength([constructor]);1472        subsetTestByKey(this.name, test, function()1473        {1474            // This function tests WebIDL as of 2019-01-14.1475            // "for every [LegacyFactoryFunction] extended attribute on an exposed1476            // interface, a corresponding property must exist on the ECMAScript1477            // global object. The name of the property is the1478            // [LegacyFactoryFunction]'s identifier, and its value is an object1479            // called a named constructor, ... . The property has the attributes1480            // { [[Writable]]: true, [[Enumerable]]: false,1481            // [[Configurable]]: true }."1482            var name = constructor.rhs.value;1483            assert_own_property(self, name);1484            var desc = Object.getOwnPropertyDescriptor(self, name);1485            assert_equals(desc.value, self[name], "wrong value in " + name + " property descriptor");1486            assert_true(desc.writable, name + " should be writable");1487            assert_false(desc.enumerable, name + " should not be enumerable");1488            assert_true(desc.configurable, name + " should be configurable");1489            assert_false("get" in desc, name + " should not have a getter");1490            assert_false("set" in desc, name + " should not have a setter");1491        }.bind(this), this.name + " interface: named constructor");1492        subsetTestByKey(this.name, test, function()1493        {1494            // This function tests WebIDL as of 2019-01-14.1495            // "2. Let F be ! CreateBuiltinFunction(realm, steps,1496            //     realm.[[Intrinsics]].[[%FunctionPrototype%]])."1497            var name = constructor.rhs.value;1498            var value = self[name];1499            assert_equals(typeof value, "function", "type of value in " + name + " property descriptor");1500            assert_not_equals(value, this.get_interface_object(), "wrong value in " + name + " property descriptor");1501            assert_equals(Object.getPrototypeOf(value), Function.prototype, "wrong value for " + name + "'s prototype");1502        }.bind(this), this.name + " interface: named constructor object");1503        subsetTestByKey(this.name, test, function()1504        {1505            // This function tests WebIDL as of 2019-01-14.1506            // "7. Let proto be the interface prototype object of interface I1507            //     in realm.1508            // "8. Perform ! DefinePropertyOrThrow(F, "prototype",1509            //     PropertyDescriptor{1510            //         [[Value]]: proto, [[Writable]]: false,1511            //         [[Enumerable]]: false, [[Configurable]]: false1512            //     })."1513            var name = constructor.rhs.value;1514            var expected = this.get_interface_object().prototype;1515            var desc = Object.getOwnPropertyDescriptor(self[name], "prototype");1516            assert_equals(desc.value, expected, "wrong value for " + name + ".prototype");1517            assert_false(desc.writable, "prototype should not be writable");1518            assert_false(desc.enumerable, "prototype should not be enumerable");1519            assert_false(desc.configurable, "prototype should not be configurable");1520            assert_false("get" in desc, "prototype should not have a getter");1521            assert_false("set" in desc, "prototype should not have a setter");1522        }.bind(this), this.name + " interface: named constructor prototype property");1523        subsetTestByKey(this.name, test, function()1524        {1525            // This function tests WebIDL as of 2019-01-14.1526            // "3. Perform ! SetFunctionName(F, id)."1527            var name = constructor.rhs.value;1528            var desc = Object.getOwnPropertyDescriptor(self[name], "name");1529            assert_equals(desc.value, name, "wrong value for " + name + ".name");1530            assert_false(desc.writable, "name should not be writable");1531            assert_false(desc.enumerable, "name should not be enumerable");1532            assert_true(desc.configurable, "name should be configurable");1533            assert_false("get" in desc, "name should not have a getter");1534            assert_false("set" in desc, "name should not have a setter");1535        }.bind(this), this.name + " interface: named constructor name");1536        subsetTestByKey(this.name, test, function()1537        {1538            // This function tests WebIDL as of 2019-01-14.1539            // "4. Initialize S to the effective overload set for constructors1540            //     with identifier id on interface I and with argument count 0.1541            // "5. Let length be the length of the shortest argument list of1542            //     the entries in S.1543            // "6. Perform ! SetFunctionLength(F, length)."1544            var name = constructor.rhs.value;1545            var desc = Object.getOwnPropertyDescriptor(self[name], "length");1546            assert_equals(desc.value, min_length, "wrong value for " + name + ".length");1547            assert_false(desc.writable, "length should not be writable");1548            assert_false(desc.enumerable, "length should not be enumerable");1549            assert_true(desc.configurable, "length should be configurable");1550            assert_false("get" in desc, "length should not have a getter");1551            assert_false("set" in desc, "length should not have a setter");1552        }.bind(this), this.name + " interface: named constructor length");1553        subsetTestByKey(this.name, test, function()1554        {1555            // This function tests WebIDL as of 2019-01-14.1556            // "1. Let steps be the following steps:1557            // "    1. If NewTarget is undefined, then throw a TypeError."1558            var name = constructor.rhs.value;1559            var args = constructor.arguments.map(function(arg) {1560                return create_suitable_object(arg.idlType);1561            });1562            assert_throws_js(globalOf(self[name]).TypeError, function() {1563                self[name](...args);1564            }.bind(this));1565        }.bind(this), this.name + " interface: named constructor without 'new'");1566    }1567    subsetTestByKey(this.name, test, function()1568    {1569        // This function tests WebIDL as of 2015-01-21.1570        // https://webidl.spec.whatwg.org/#interface-object1571        if (!this.should_have_interface_object()) {1572            return;1573        }1574        this.assert_interface_object_exists();1575        if (this.is_callback()) {1576            assert_false("prototype" in this.get_interface_object(),1577                         this.name + ' should not have a "prototype" property');1578            return;1579        }1580        // "An interface object for a non-callback interface must have a1581        // property named âprototypeâ with attributes { [[Writable]]: false,1582        // [[Enumerable]]: false, [[Configurable]]: false } whose value is an1583        // object called the interface prototype object. This object has1584        // properties that correspond to the regular attributes and regular1585        // operations defined on the interface, and is described in more detail1586        // in section 4.5.4 below."1587        assert_own_property(this.get_interface_object(), "prototype",1588                            'interface "' + this.name + '" does not have own property "prototype"');1589        var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "prototype");1590        assert_false("get" in desc, this.name + ".prototype should not have a getter");1591        assert_false("set" in desc, this.name + ".prototype should not have a setter");1592        assert_false(desc.writable, this.name + ".prototype should not be writable");1593        assert_false(desc.enumerable, this.name + ".prototype should not be enumerable");1594        assert_false(desc.configurable, this.name + ".prototype should not be configurable");1595        // Next, test that the [[Prototype]] of the interface prototype object1596        // is correct. (This is made somewhat difficult by the existence of1597        // [LegacyNoInterfaceObject].)1598        // TODO: Aryeh thinks there's at least other place in this file where1599        //       we try to figure out if an interface prototype object is1600        //       correct. Consolidate that code.1601        // "The interface prototype object for a given interface A must have an1602        // internal [[Prototype]] property whose value is returned from the1603        // following steps:1604        // "If A is declared with the [Global] extended1605        // attribute, and A supports named properties, then return the named1606        // properties object for A, as defined in §3.6.4 Named properties1607        // object.1608        // "Otherwise, if A is declared to inherit from another interface, then1609        // return the interface prototype object for the inherited interface.1610        // "Otherwise, return %ObjectPrototype%.1611        //1612        // "In the ECMAScript binding, the DOMException type has some additional1613        // requirements:1614        //1615        //     "Unlike normal interface types, the interface prototype object1616        //     for DOMException must have as its [[Prototype]] the intrinsic1617        //     object %ErrorPrototype%."1618        //1619        if (this.name === "Window") {1620            assert_class_string(Object.getPrototypeOf(this.get_interface_object().prototype),1621                                'WindowProperties',1622                                'Class name for prototype of Window' +1623                                '.prototype is not "WindowProperties"');1624        } else {1625            var inherit_interface, inherit_interface_interface_object;1626            if (this.base) {1627                inherit_interface = this.base;1628                var parent = this.array.members[inherit_interface];1629                if (!parent.has_extended_attribute("LegacyNoInterfaceObject")) {1630                    parent.assert_interface_object_exists();1631                    inherit_interface_interface_object = parent.get_interface_object();1632                }1633            } else if (this.name === "DOMException") {1634                inherit_interface = 'Error';1635                inherit_interface_interface_object = self.Error;1636            } else {1637                inherit_interface = 'Object';1638                inherit_interface_interface_object = self.Object;1639            }1640            if (inherit_interface_interface_object) {1641                assert_not_equals(inherit_interface_interface_object, undefined,1642                                  'should inherit from ' + inherit_interface + ', but there is no such property');1643                assert_own_property(inherit_interface_interface_object, 'prototype',1644                                    'should inherit from ' + inherit_interface + ', but that object has no "prototype" property');1645                assert_equals(Object.getPrototypeOf(this.get_interface_object().prototype),1646                              inherit_interface_interface_object.prototype,1647                              'prototype of ' + this.name + '.prototype is not ' + inherit_interface + '.prototype');1648            } else {1649                // We can't test that we get the correct object, because this is the1650                // only way to get our hands on it. We only test that its class1651                // string, at least, is correct.1652                assert_class_string(Object.getPrototypeOf(this.get_interface_object().prototype),1653                                    inherit_interface + 'Prototype',1654                                    'Class name for prototype of ' + this.name +1655                                    '.prototype is not "' + inherit_interface + 'Prototype"');1656            }1657        }1658        // "The class string of an interface prototype object is the1659        // concatenation of the interfaceâs qualified identifier and the string1660        // âPrototypeâ."1661        // Skip these tests for now due to a specification issue about1662        // prototype name.1663        // https://www.w3.org/Bugs/Public/show_bug.cgi?id=282441664        // assert_class_string(this.get_interface_object().prototype, this.get_qualified_name() + "Prototype",1665        //                     "class string of " + this.name + ".prototype");1666        // String() should end up calling {}.toString if nothing defines a1667        // stringifier.1668        if (!this.has_stringifier()) {1669            // assert_equals(String(this.get_interface_object().prototype), "[object " + this.get_qualified_name() + "Prototype]",1670            //         "String(" + this.name + ".prototype)");1671        }1672    }.bind(this), this.name + " interface: existence and properties of interface prototype object");1673    // "If the interface is declared with the [Global]1674    // extended attribute, or the interface is in the set of inherited1675    // interfaces for any other interface that is declared with one of these1676    // attributes, then the interface prototype object must be an immutable1677    // prototype exotic object."1678    // https://webidl.spec.whatwg.org/#interface-prototype-object1679    if (this.is_global()) {1680        this.test_immutable_prototype("interface prototype object", this.get_interface_object().prototype);1681    }1682    subsetTestByKey(this.name, test, function()1683    {1684        if (!this.should_have_interface_object()) {1685            return;1686        }1687        this.assert_interface_object_exists();1688        if (this.is_callback()) {1689            assert_false("prototype" in this.get_interface_object(),1690                         this.name + ' should not have a "prototype" property');1691            return;1692        }1693        assert_own_property(this.get_interface_object(), "prototype",1694                            'interface "' + this.name + '" does not have own property "prototype"');1695        // "If the [LegacyNoInterfaceObject] extended attribute was not specified1696        // on the interface, then the interface prototype object must also have a1697        // property named âconstructorâ with attributes { [[Writable]]: true,1698        // [[Enumerable]]: false, [[Configurable]]: true } whose value is a1699        // reference to the interface object for the interface."1700        assert_own_property(this.get_interface_object().prototype, "constructor",1701                            this.name + '.prototype does not have own property "constructor"');1702        var desc = Object.getOwnPropertyDescriptor(this.get_interface_object().prototype, "constructor");1703        assert_false("get" in desc, this.name + ".prototype.constructor should not have a getter");1704        assert_false("set" in desc, this.name + ".prototype.constructor should not have a setter");1705        assert_true(desc.writable, this.name + ".prototype.constructor should be writable");1706        assert_false(desc.enumerable, this.name + ".prototype.constructor should not be enumerable");1707        assert_true(desc.configurable, this.name + ".prototype.constructor should be configurable");1708        assert_equals(this.get_interface_object().prototype.constructor, this.get_interface_object(),1709                      this.name + '.prototype.constructor is not the same object as ' + this.name);1710    }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property');1711    subsetTestByKey(this.name, test, function()1712    {1713        if (!this.should_have_interface_object()) {1714            return;1715        }1716        this.assert_interface_object_exists();1717        if (this.is_callback()) {1718            assert_false("prototype" in this.get_interface_object(),1719                         this.name + ' should not have a "prototype" property');1720            return;1721        }1722        assert_own_property(this.get_interface_object(), "prototype",1723                            'interface "' + this.name + '" does not have own property "prototype"');1724        // If the interface has any member declared with the [Unscopable] extended1725        // attribute, then there must be a property on the interface prototype object1726        // whose name is the @@unscopables symbol, which has the attributes1727        // { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true },1728        // and whose value is an object created as follows...1729        var unscopables = this.get_unscopables().map(m => m.name);1730        var proto = this.get_interface_object().prototype;1731        if (unscopables.length != 0) {1732            assert_own_property(1733                proto, Symbol.unscopables,1734                this.name + '.prototype should have an @@unscopables property');1735            var desc = Object.getOwnPropertyDescriptor(proto, Symbol.unscopables);1736            assert_false("get" in desc,1737                         this.name + ".prototype[Symbol.unscopables] should not have a getter");1738            assert_false("set" in desc, this.name + ".prototype[Symbol.unscopables] should not have a setter");1739            assert_false(desc.writable, this.name + ".prototype[Symbol.unscopables] should not be writable");1740            assert_false(desc.enumerable, this.name + ".prototype[Symbol.unscopables] should not be enumerable");1741            assert_true(desc.configurable, this.name + ".prototype[Symbol.unscopables] should be configurable");1742            assert_equals(desc.value, proto[Symbol.unscopables],1743                          this.name + '.prototype[Symbol.unscopables] should be in the descriptor');1744            assert_equals(typeof desc.value, "object",1745                          this.name + '.prototype[Symbol.unscopables] should be an object');1746            assert_equals(Object.getPrototypeOf(desc.value), null,1747                          this.name + '.prototype[Symbol.unscopables] should have a null prototype');1748            assert_equals(Object.getOwnPropertySymbols(desc.value).length,1749                          0,1750                          this.name + '.prototype[Symbol.unscopables] should have the right number of symbol-named properties');1751            // Check that we do not have _extra_ unscopables.  Checking that we1752            // have all the ones we should will happen in the per-member tests.1753            var observed = Object.getOwnPropertyNames(desc.value);1754            for (var prop of observed) {1755                assert_not_equals(unscopables.indexOf(prop),1756                                  -1,1757                                  this.name + '.prototype[Symbol.unscopables] has unexpected property "' + prop + '"');1758            }1759        } else {1760            assert_equals(Object.getOwnPropertyDescriptor(this.get_interface_object().prototype, Symbol.unscopables),1761                          undefined,1762                          this.name + '.prototype should not have @@unscopables');1763        }1764    }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s @@unscopables property');1765};1766IdlInterface.prototype.test_immutable_prototype = function(type, obj)1767{1768    if (typeof Object.setPrototypeOf !== "function") {1769        return;1770    }1771    subsetTestByKey(this.name, test, function(t) {1772        var originalValue = Object.getPrototypeOf(obj);1773        var newValue = Object.create(null);1774        t.add_cleanup(function() {1775            try {1776                Object.setPrototypeOf(obj, originalValue);1777            } catch (err) {}1778        });1779        assert_throws_js(TypeError, function() {1780            Object.setPrototypeOf(obj, newValue);1781        });1782        assert_equals(1783                Object.getPrototypeOf(obj),1784                originalValue,1785                "original value not modified"1786            );1787    }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1788        "of " + type + " - setting to a new value via Object.setPrototypeOf " +1789        "should throw a TypeError");1790    subsetTestByKey(this.name, test, function(t) {1791        var originalValue = Object.getPrototypeOf(obj);1792        var newValue = Object.create(null);1793        t.add_cleanup(function() {1794            let setter = Object.getOwnPropertyDescriptor(1795                Object.prototype, '__proto__'1796            ).set;1797            try {1798                setter.call(obj, originalValue);1799            } catch (err) {}1800        });1801        // We need to find the actual setter for the '__proto__' property, so we1802        // can determine the right global for it.  Walk up the prototype chain1803        // looking for that property until we find it.1804        let setter;1805        {1806            let cur = obj;1807            while (cur) {1808                const desc = Object.getOwnPropertyDescriptor(cur, "__proto__");1809                if (desc) {1810                    setter = desc.set;1811                    break;1812                }1813                cur = Object.getPrototypeOf(cur);1814            }1815        }1816        assert_throws_js(globalOf(setter).TypeError, function() {1817            obj.__proto__ = newValue;1818        });1819        assert_equals(1820                Object.getPrototypeOf(obj),1821                originalValue,1822                "original value not modified"1823            );1824    }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1825        "of " + type + " - setting to a new value via __proto__ " +1826        "should throw a TypeError");1827    subsetTestByKey(this.name, test, function(t) {1828        var originalValue = Object.getPrototypeOf(obj);1829        var newValue = Object.create(null);1830        t.add_cleanup(function() {1831            try {1832                Reflect.setPrototypeOf(obj, originalValue);1833            } catch (err) {}1834        });1835        assert_false(Reflect.setPrototypeOf(obj, newValue));1836        assert_equals(1837                Object.getPrototypeOf(obj),1838                originalValue,1839                "original value not modified"1840            );1841    }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1842        "of " + type + " - setting to a new value via Reflect.setPrototypeOf " +1843        "should return false");1844    subsetTestByKey(this.name, test, function() {1845        var originalValue = Object.getPrototypeOf(obj);1846        Object.setPrototypeOf(obj, originalValue);1847    }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1848        "of " + type + " - setting to its original value via Object.setPrototypeOf " +1849        "should not throw");1850    subsetTestByKey(this.name, test, function() {1851        var originalValue = Object.getPrototypeOf(obj);1852        obj.__proto__ = originalValue;1853    }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1854        "of " + type + " - setting to its original value via __proto__ " +1855        "should not throw");1856    subsetTestByKey(this.name, test, function() {1857        var originalValue = Object.getPrototypeOf(obj);1858        assert_true(Reflect.setPrototypeOf(obj, originalValue));1859    }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1860        "of " + type + " - setting to its original value via Reflect.setPrototypeOf " +1861        "should return true");1862};1863IdlInterface.prototype.test_member_const = function(member)1864{1865    if (!this.has_constants()) {1866        throw new IdlHarnessError("Internal error: test_member_const called without any constants");1867    }1868    subsetTestByKey(this.name, test, function()1869    {1870        this.assert_interface_object_exists();1871        // "For each constant defined on an interface A, there must be1872        // a corresponding property on the interface object, if it1873        // exists."1874        assert_own_property(this.get_interface_object(), member.name);1875        // "The value of the property is that which is obtained by1876        // converting the constantâs IDL value to an ECMAScript1877        // value."1878        assert_equals(this.get_interface_object()[member.name], constValue(member.value),1879                      "property has wrong value");1880        // "The property has attributes { [[Writable]]: false,1881        // [[Enumerable]]: true, [[Configurable]]: false }."1882        var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), member.name);1883        assert_false("get" in desc, "property should not have a getter");1884        assert_false("set" in desc, "property should not have a setter");1885        assert_false(desc.writable, "property should not be writable");1886        assert_true(desc.enumerable, "property should be enumerable");1887        assert_false(desc.configurable, "property should not be configurable");1888    }.bind(this), this.name + " interface: constant " + member.name + " on interface object");1889    // "In addition, a property with the same characteristics must1890    // exist on the interface prototype object."1891    subsetTestByKey(this.name, test, function()1892    {1893        this.assert_interface_object_exists();1894        if (this.is_callback()) {1895            assert_false("prototype" in this.get_interface_object(),1896                         this.name + ' should not have a "prototype" property');1897            return;1898        }1899        assert_own_property(this.get_interface_object(), "prototype",1900                            'interface "' + this.name + '" does not have own property "prototype"');1901        assert_own_property(this.get_interface_object().prototype, member.name);1902        assert_equals(this.get_interface_object().prototype[member.name], constValue(member.value),1903                      "property has wrong value");1904        var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), member.name);1905        assert_false("get" in desc, "property should not have a getter");1906        assert_false("set" in desc, "property should not have a setter");1907        assert_false(desc.writable, "property should not be writable");1908        assert_true(desc.enumerable, "property should be enumerable");1909        assert_false(desc.configurable, "property should not be configurable");1910    }.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object");1911};1912IdlInterface.prototype.test_member_attribute = function(member)1913  {1914    if (!shouldRunSubTest(this.name)) {1915        return;1916    }1917    var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: attribute " + member.name);1918    a_test.step(function()1919    {1920        if (!this.should_have_interface_object()) {1921            a_test.done();1922            return;1923        }1924        this.assert_interface_object_exists();1925        assert_own_property(this.get_interface_object(), "prototype",1926                            'interface "' + this.name + '" does not have own property "prototype"');1927        if (member.special === "static") {1928            assert_own_property(this.get_interface_object(), member.name,1929                "The interface object must have a property " +1930                format_value(member.name));1931            a_test.done();1932            return;1933        }1934        this.do_member_unscopable_asserts(member);1935        if (this.is_global()) {1936            assert_own_property(self, member.name,1937                "The global object must have a property " +1938                format_value(member.name));1939            assert_false(member.name in this.get_interface_object().prototype,1940                "The prototype object should not have a property " +1941                format_value(member.name));1942            var getter = Object.getOwnPropertyDescriptor(self, member.name).get;1943            assert_equals(typeof(getter), "function",1944                          format_value(member.name) + " must have a getter");1945            // Try/catch around the get here, since it can legitimately throw.1946            // If it does, we obviously can't check for equality with direct1947            // invocation of the getter.1948            var gotValue;1949            var propVal;1950            try {1951                propVal = self[member.name];1952                gotValue = true;1953            } catch (e) {1954                gotValue = false;1955            }1956            if (gotValue) {1957                assert_equals(propVal, getter.call(undefined),1958                              "Gets on a global should not require an explicit this");1959            }1960            // do_interface_attribute_asserts must be the last thing we do,1961            // since it will call done() on a_test.1962            this.do_interface_attribute_asserts(self, member, a_test);1963        } else {1964            assert_true(member.name in this.get_interface_object().prototype,1965                "The prototype object must have a property " +1966                format_value(member.name));1967            if (!member.has_extended_attribute("LegacyLenientThis")) {1968                if (member.idlType.generic !== "Promise") {1969                    // this.get_interface_object() returns a thing in our global1970                    assert_throws_js(TypeError, function() {1971                        this.get_interface_object().prototype[member.name];1972                    }.bind(this), "getting property on prototype object must throw TypeError");1973                    // do_interface_attribute_asserts must be the last thing we1974                    // do, since it will call done() on a_test.1975                    this.do_interface_attribute_asserts(this.get_interface_object().prototype, member, a_test);1976                } else {1977                    promise_rejects_js(a_test, TypeError,1978                                    this.get_interface_object().prototype[member.name])1979                        .then(a_test.step_func(function() {1980                            // do_interface_attribute_asserts must be the last1981                            // thing we do, since it will call done() on a_test.1982                            this.do_interface_attribute_asserts(this.get_interface_object().prototype,1983                                                                member, a_test);1984                        }.bind(this)));1985                }1986            } else {1987                assert_equals(this.get_interface_object().prototype[member.name], undefined,1988                              "getting property on prototype object must return undefined");1989              // do_interface_attribute_asserts must be the last thing we do,1990              // since it will call done() on a_test.1991              this.do_interface_attribute_asserts(this.get_interface_object().prototype, member, a_test);1992            }1993        }1994    }.bind(this));1995};1996IdlInterface.prototype.test_member_operation = function(member)1997{1998    if (!shouldRunSubTest(this.name)) {1999        return;2000    }2001    var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: operation " + member);2002    a_test.step(function()2003    {2004        // This function tests WebIDL as of 2015-12-29.2005        // https://webidl.spec.whatwg.org/#es-operations2006        if (!this.should_have_interface_object()) {2007            a_test.done();2008            return;2009        }2010        this.assert_interface_object_exists();2011        if (this.is_callback()) {2012            assert_false("prototype" in this.get_interface_object(),2013                         this.name + ' should not have a "prototype" property');2014            a_test.done();2015            return;2016        }2017        assert_own_property(this.get_interface_object(), "prototype",2018                            'interface "' + this.name + '" does not have own property "prototype"');2019        // "For each unique identifier of an exposed operation defined on the2020        // interface, there must exist a corresponding property, unless the2021        // effective overload set for that identifier and operation and with an2022        // argument count of 0 has no entries."2023        // TODO: Consider [Exposed].2024        // "The location of the property is determined as follows:"2025        var memberHolderObject;2026        // "* If the operation is static, then the property exists on the2027        //    interface object."2028        if (member.special === "static") {2029            assert_own_property(this.get_interface_object(), member.name,2030                    "interface object missing static operation");2031            memberHolderObject = this.get_interface_object();2032        // "* Otherwise, [...] if the interface was declared with the [Global]2033        //    extended attribute, then the property exists2034        //    on every object that implements the interface."2035        } else if (this.is_global()) {2036            assert_own_property(self, member.name,2037                    "global object missing non-static operation");2038            memberHolderObject = self;2039        // "* Otherwise, the property exists solely on the interfaceâs2040        //    interface prototype object."2041        } else {2042            assert_own_property(this.get_interface_object().prototype, member.name,2043                    "interface prototype object missing non-static operation");2044            memberHolderObject = this.get_interface_object().prototype;2045        }2046        this.do_member_unscopable_asserts(member);2047        this.do_member_operation_asserts(memberHolderObject, member, a_test);2048    }.bind(this));2049};2050IdlInterface.prototype.do_member_unscopable_asserts = function(member)2051{2052    // Check that if the member is unscopable then it's in the2053    // @@unscopables object properly.2054    if (!member.isUnscopable) {2055        return;2056    }2057    var unscopables = this.get_interface_object().prototype[Symbol.unscopables];2058    var prop = member.name;2059    var propDesc = Object.getOwnPropertyDescriptor(unscopables, prop);2060    assert_equals(typeof propDesc, "object",2061                  this.name + '.prototype[Symbol.unscopables].' + prop + ' must exist')2062    assert_false("get" in propDesc,2063                 this.name + '.prototype[Symbol.unscopables].' + prop + ' must have no getter');2064    assert_false("set" in propDesc,2065                 this.name + '.prototype[Symbol.unscopables].' + prop + ' must have no setter');2066    assert_true(propDesc.writable,2067                this.name + '.prototype[Symbol.unscopables].' + prop + ' must be writable');2068    assert_true(propDesc.enumerable,2069                this.name + '.prototype[Symbol.unscopables].' + prop + ' must be enumerable');2070    assert_true(propDesc.configurable,2071                this.name + '.prototype[Symbol.unscopables].' + prop + ' must be configurable');2072    assert_equals(propDesc.value, true,2073                  this.name + '.prototype[Symbol.unscopables].' + prop + ' must have the value `true`');2074};2075IdlInterface.prototype.do_member_operation_asserts = function(memberHolderObject, member, a_test)2076{2077    var done = a_test.done.bind(a_test);2078    var operationUnforgeable = member.isUnforgeable;2079    var desc = Object.getOwnPropertyDescriptor(memberHolderObject, member.name);2080    // "The property has attributes { [[Writable]]: B,2081    // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the2082    // operation is unforgeable on the interface, and true otherwise".2083    assert_false("get" in desc, "property should not have a getter");2084    assert_false("set" in desc, "property should not have a setter");2085    assert_equals(desc.writable, !operationUnforgeable,2086                  "property should be writable if and only if not unforgeable");2087    assert_true(desc.enumerable, "property should be enumerable");2088    assert_equals(desc.configurable, !operationUnforgeable,2089                  "property should be configurable if and only if not unforgeable");2090    // "The value of the property is a Function object whose2091    // behavior is as follows . . ."2092    assert_equals(typeof memberHolderObject[member.name], "function",2093                  "property must be a function");2094    const ctors = this.members.filter(function(m) {2095        return m.type == "operation" && m.name == member.name;2096    });2097    assert_equals(2098        memberHolderObject[member.name].length,2099        minOverloadLength(ctors),2100        "property has wrong .length");2101    assert_equals(2102        memberHolderObject[member.name].name,2103        member.name,2104        "property has wrong .name");2105    // Make some suitable arguments2106    var args = member.arguments.map(function(arg) {2107        return create_suitable_object(arg.idlType);2108    });2109    // "Let O be a value determined as follows:2110    // ". . .2111    // "Otherwise, throw a TypeError."2112    // This should be hit if the operation is not static, there is2113    // no [ImplicitThis] attribute, and the this value is null.2114    //2115    // TODO: We currently ignore the [ImplicitThis] case.  Except we manually2116    // check for globals, since otherwise we'll invoke window.close().  And we2117    // have to skip this test for anything that on the proto chain of "self",2118    // since that does in fact have implicit-this behavior.2119    if (member.special !== "static") {2120        var cb;2121        if (!this.is_global() &&2122            memberHolderObject[member.name] != self[member.name])2123        {2124            cb = awaitNCallbacks(2, done);2125            throwOrReject(a_test, member, memberHolderObject[member.name], null, args,2126                          "calling operation with this = null didn't throw TypeError", cb);2127        } else {2128            cb = awaitNCallbacks(1, done);2129        }2130        // ". . . If O is not null and is also not a platform object2131        // that implements interface I, throw a TypeError."2132        //2133        // TODO: Test a platform object that implements some other2134        // interface.  (Have to be sure to get inheritance right.)2135        throwOrReject(a_test, member, memberHolderObject[member.name], {}, args,2136                      "calling operation with this = {} didn't throw TypeError", cb);2137    } else {2138        done();2139    }2140}2141IdlInterface.prototype.test_to_json_operation = function(desc, memberHolderObject, member) {2142    var instanceName = memberHolderObject && memberHolderObject.constructor.name2143        || member.name + " object";2144    if (member.has_extended_attribute("Default")) {2145        subsetTestByKey(this.name, test, function() {2146            var map = this.default_to_json_operation();2147            var json = memberHolderObject.toJSON();2148            map.forEach(function(type, k) {2149                assert_true(k in json, "property " + JSON.stringify(k) + " should be present in the output of " + this.name + ".prototype.toJSON()");2150                var descriptor = Object.getOwnPropertyDescriptor(json, k);2151                assert_true(descriptor.writable, "property " + k + " should be writable");2152                assert_true(descriptor.configurable, "property " + k + " should be configurable");2153                assert_true(descriptor.enumerable, "property " + k + " should be enumerable");2154                this.array.assert_type_is(json[k], type);2155                delete json[k];2156            }, this);2157        }.bind(this), this.name + " interface: default toJSON operation on " + desc);2158    } else {2159        subsetTestByKey(this.name, test, function() {2160            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);2161            this.array.assert_type_is(memberHolderObject.toJSON(), member.idlType);2162        }.bind(this), this.name + " interface: toJSON operation on " + desc);2163    }2164};2165IdlInterface.prototype.test_member_iterable = function(member)2166{2167    subsetTestByKey(this.name, test, function()2168    {2169        var isPairIterator = member.idlType.length === 2;2170        var proto = this.get_interface_object().prototype;2171        var iteratorDesc = Object.getOwnPropertyDescriptor(proto, Symbol.iterator);2172        assert_true(iteratorDesc.writable, "@@iterator property should be writable");2173        assert_true(iteratorDesc.configurable, "@@iterator property should be configurable");2174        assert_false(iteratorDesc.enumerable, "@@iterator property should not be enumerable");2175        assert_equals(typeof iteratorDesc.value, "function", "@@iterator property should be a function");2176        assert_equals(iteratorDesc.value.length, 0, "@@iterator function object length should be 0");2177        assert_equals(iteratorDesc.value.name, isPairIterator ? "entries" : "values", "@@iterator function object should have the right name");2178        if (isPairIterator) {2179            assert_equals(proto["entries"], proto[Symbol.iterator], "entries method should be the same as @@iterator method");2180            [2181                ["entries", 0],2182                ["keys", 0],2183                ["values", 0],2184                ["forEach", 1]2185            ].forEach(([property, length]) => {2186                var desc = Object.getOwnPropertyDescriptor(proto, property);2187                assert_equals(typeof desc.value, "function", property + " property should be a function");2188                assert_equals(desc.value.length, length, property + " function object should have the right length");2189                assert_equals(desc.value.name, property, property + " function object should have the right name");2190            });2191        } else {2192            assert_equals(proto[Symbol.iterator], Array.prototype[Symbol.iterator], "@@iterator method should be the same as Array prototype's");2193            ["entries", "keys", "values", "forEach", Symbol.iterator].forEach(property => {2194                var propertyName = property === Symbol.iterator ? "@@iterator" : property;2195                assert_equals(proto[property], Array.prototype[property], propertyName + " method should be the same as Array prototype's");2196            });2197        }2198    }.bind(this), this.name + " interface: iterable<" + member.idlType.map(function(t) { return t.idlType; }).join(", ") + ">");2199};2200IdlInterface.prototype.test_member_maplike = function(member) {2201    subsetTestByKey(this.name, test, () => {2202        const proto = this.get_interface_object().prototype;2203        const methods = [2204            ["entries", 0],2205            ["keys", 0],2206            ["values", 0],2207            ["forEach", 1],2208            ["get", 1],2209            ["has", 1]2210        ];2211        if (!member.readonly) {2212            methods.push(2213                ["set", 2],2214                ["delete", 1],2215                ["clear", 1]2216            );2217        }2218        for (const [name, length] of methods) {2219            const desc = Object.getOwnPropertyDescriptor(proto, name);2220            assert_equals(typeof desc.value, "function", `${name} should be a function`);2221            assert_equals(desc.enumerable, false, `${name} enumerable`);2222            assert_equals(desc.configurable, true, `${name} configurable`);2223            assert_equals(desc.writable, true, `${name} writable`);2224            assert_equals(desc.value.length, length, `${name} function object length should be ${length}`);2225            assert_equals(desc.value.name, name, `${name} function object should have the right name`);2226        }2227        const iteratorDesc = Object.getOwnPropertyDescriptor(proto, Symbol.iterator);2228        assert_equals(iteratorDesc.value, proto.entries, `@@iterator should equal entries`);2229        assert_equals(iteratorDesc.enumerable, false, `@@iterator enumerable`);2230        assert_equals(iteratorDesc.configurable, true, `@@iterator configurable`);2231        assert_equals(iteratorDesc.writable, true, `@@iterator writable`);2232        const sizeDesc = Object.getOwnPropertyDescriptor(proto, "size");2233        assert_equals(typeof sizeDesc.get, "function", `size getter should be a function`);2234        assert_equals(sizeDesc.set, undefined, `size should not have a setter`);2235        assert_equals(sizeDesc.enumerable, false, `size enumerable`);2236        assert_equals(sizeDesc.configurable, true, `size configurable`);2237        assert_equals(sizeDesc.get.length, 0, `size getter length should have the right length`);2238        assert_equals(sizeDesc.get.name, "get size", `size getter have the right name`);2239    }, `${this.name} interface: maplike<${member.idlType.map(t => t.idlType).join(", ")}>`);2240};2241IdlInterface.prototype.test_member_setlike = function(member) {2242    subsetTestByKey(this.name, test, () => {2243        const proto = this.get_interface_object().prototype;2244        const methods = [2245            ["entries", 0],2246            ["keys", 0],2247            ["values", 0],2248            ["forEach", 1],2249            ["has", 1]2250        ];2251        if (!member.readonly) {2252            methods.push(2253                ["add", 1],2254                ["delete", 1],2255                ["clear", 1]2256            );2257        }2258        for (const [name, length] of methods) {2259            const desc = Object.getOwnPropertyDescriptor(proto, name);2260            assert_equals(typeof desc.value, "function", `${name} should be a function`);2261            assert_equals(desc.enumerable, false, `${name} enumerable`);2262            assert_equals(desc.configurable, true, `${name} configurable`);2263            assert_equals(desc.writable, true, `${name} writable`);2264            assert_equals(desc.value.length, length, `${name} function object length should be ${length}`);2265            assert_equals(desc.value.name, name, `${name} function object should have the right name`);2266        }2267        const iteratorDesc = Object.getOwnPropertyDescriptor(proto, Symbol.iterator);2268        assert_equals(iteratorDesc.value, proto.values, `@@iterator should equal values`);2269        assert_equals(iteratorDesc.enumerable, false, `@@iterator enumerable`);2270        assert_equals(iteratorDesc.configurable, true, `@@iterator configurable`);2271        assert_equals(iteratorDesc.writable, true, `@@iterator writable`);2272        const sizeDesc = Object.getOwnPropertyDescriptor(proto, "size");2273        assert_equals(typeof sizeDesc.get, "function", `size getter should be a function`);2274        assert_equals(sizeDesc.set, undefined, `size should not have a setter`);2275        assert_equals(sizeDesc.enumerable, false, `size enumerable`);2276        assert_equals(sizeDesc.configurable, true, `size configurable`);2277        assert_equals(sizeDesc.get.length, 0, `size getter length should have the right length`);2278        assert_equals(sizeDesc.get.name, "size", `size getter have the right name`);2279    }, `${this.name} interface: setlike<${member.idlType.map(t => t.idlType).join(", ")}>`);2280};2281IdlInterface.prototype.test_member_async_iterable = function(member)2282{2283    subsetTestByKey(this.name, test, function()2284    {2285        var isPairIterator = member.idlType.length === 2;2286        var proto = this.get_interface_object().prototype;2287        var iteratorDesc = Object.getOwnPropertyDescriptor(proto, Symbol.asyncIterator);2288        assert_true(iteratorDesc.writable, "@@asyncIterator property should be writable");2289        assert_true(iteratorDesc.configurable, "@@asyncIterator property should be configurable");2290        assert_false(iteratorDesc.enumerable, "@@asyncIterator property should not be enumerable");2291        assert_equals(typeof iteratorDesc.value, "function", "@@asyncIterator property should be a function");2292        assert_equals(iteratorDesc.value.length, 0, "@@asyncIterator function object length should be 0");2293        assert_equals(iteratorDesc.value.name, isPairIterator ? "entries" : "values", "@@asyncIterator function object should have the right name");2294        if (isPairIterator) {2295            assert_equals(proto["entries"], proto[Symbol.asyncIterator], "entries method should be the same as @@asyncIterator method");2296            ["entries", "keys", "values"].forEach(property => {2297                var desc = Object.getOwnPropertyDescriptor(proto, property);2298                assert_equals(typeof desc.value, "function", property + " property should be a function");2299                assert_equals(desc.value.length, 0, property + " function object length should be 0");2300                assert_equals(desc.value.name, property, property + " function object should have the right name");2301            });2302        } else {2303            assert_equals(proto["values"], proto[Symbol.asyncIterator], "values method should be the same as @@asyncIterator method");2304            assert_false("entries" in proto, "should not have an entries method");2305            assert_false("keys" in proto, "should not have a keys method");2306        }2307    }.bind(this), this.name + " interface: async iterable<" + member.idlType.map(function(t) { return t.idlType; }).join(", ") + ">");2308};2309IdlInterface.prototype.test_member_stringifier = function(member)2310{2311    subsetTestByKey(this.name, test, function()2312    {2313        if (!this.should_have_interface_object()) {2314            return;2315        }2316        this.assert_interface_object_exists();2317        if (this.is_callback()) {2318            assert_false("prototype" in this.get_interface_object(),2319                         this.name + ' should not have a "prototype" property');2320            return;2321        }2322        assert_own_property(this.get_interface_object(), "prototype",2323                            'interface "' + this.name + '" does not have own property "prototype"');2324        // ". . . the property exists on the interface prototype object."2325        var interfacePrototypeObject = this.get_interface_object().prototype;2326        assert_own_property(interfacePrototypeObject, "toString",2327                "interface prototype object missing non-static operation");2328        var stringifierUnforgeable = member.isUnforgeable;2329        var desc = Object.getOwnPropertyDescriptor(interfacePrototypeObject, "toString");2330        // "The property has attributes { [[Writable]]: B,2331        // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the2332        // stringifier is unforgeable on the interface, and true otherwise."2333        assert_false("get" in desc, "property should not have a getter");2334        assert_false("set" in desc, "property should not have a setter");2335        assert_equals(desc.writable, !stringifierUnforgeable,2336                      "property should be writable if and only if not unforgeable");2337        assert_true(desc.enumerable, "property should be enumerable");2338        assert_equals(desc.configurable, !stringifierUnforgeable,2339                      "property should be configurable if and only if not unforgeable");2340        // "The value of the property is a Function object, which behaves as2341        // follows . . ."2342        assert_equals(typeof interfacePrototypeObject.toString, "function",2343                      "property must be a function");2344        // "The value of the Function objectâs âlengthâ property is the Number2345        // value 0."2346        assert_equals(interfacePrototypeObject.toString.length, 0,2347            "property has wrong .length");2348        // "Let O be the result of calling ToObject on the this value."2349        assert_throws_js(globalOf(interfacePrototypeObject.toString).TypeError, function() {2350            interfacePrototypeObject.toString.apply(null, []);2351        }, "calling stringifier with this = null didn't throw TypeError");2352        // "If O is not an object that implements the interface on which the2353        // stringifier was declared, then throw a TypeError."2354        //2355        // TODO: Test a platform object that implements some other2356        // interface.  (Have to be sure to get inheritance right.)2357        assert_throws_js(globalOf(interfacePrototypeObject.toString).TypeError, function() {2358            interfacePrototypeObject.toString.apply({}, []);2359        }, "calling stringifier with this = {} didn't throw TypeError");2360    }.bind(this), this.name + " interface: stringifier");2361};2362IdlInterface.prototype.test_members = function()2363{2364    for (var i = 0; i < this.members.length; i++)2365    {2366        var member = this.members[i];2367        if (member.untested) {2368            continue;2369        }2370        if (!exposed_in(exposure_set(member, this.exposureSet))) {2371            subsetTestByKey(this.name, test, function() {2372                // It's not exposed, so we shouldn't find it anywhere.2373                assert_false(member.name in this.get_interface_object(),2374                             "The interface object must not have a property " +2375                             format_value(member.name));2376                assert_false(member.name in this.get_interface_object().prototype,2377                             "The prototype object must not have a property " +2378                             format_value(member.name));2379            }.bind(this), this.name + " interface: member " + member.name);2380            continue;2381        }2382        switch (member.type) {2383        case "const":2384            this.test_member_const(member);2385            break;2386        case "attribute":2387            // For unforgeable attributes, we do the checks in2388            // test_interface_of instead.2389            if (!member.isUnforgeable)2390            {2391                this.test_member_attribute(member);2392            }2393            if (member.special === "stringifier") {2394                this.test_member_stringifier(member);2395            }2396            break;2397        case "operation":2398            // TODO: Need to correctly handle multiple operations with the same2399            // identifier.2400            // For unforgeable operations, we do the checks in2401            // test_interface_of instead.2402            if (member.name) {2403                if (!member.isUnforgeable)2404                {2405                    this.test_member_operation(member);2406                }2407            } else if (member.special === "stringifier") {2408                this.test_member_stringifier(member);2409            }2410            break;2411        case "iterable":2412            if (member.async) {2413                this.test_member_async_iterable(member);2414            } else {2415                this.test_member_iterable(member);2416            }2417            break;2418        case "maplike":2419            this.test_member_maplike(member);2420            break;2421        case "setlike":2422            this.test_member_setlike(member);2423            break;2424        default:2425            // TODO: check more member types.2426            break;2427        }2428    }2429};2430IdlInterface.prototype.test_object = function(desc)2431{2432    var obj, exception = null;2433    try2434    {2435        obj = eval(desc);2436    }2437    catch(e)2438    {2439        exception = e;2440    }2441    var expected_typeof;2442    if (this.name == "HTMLAllCollection")2443    {2444        // Result of [[IsHTMLDDA]] slot2445        expected_typeof = "undefined";2446    }2447    else2448    {2449        expected_typeof = "object";2450    }2451    this.test_primary_interface_of(desc, obj, exception, expected_typeof);2452    var current_interface = this;2453    while (current_interface)2454    {2455        if (!(current_interface.name in this.array.members))2456        {2457            throw new IdlHarnessError("Interface " + current_interface.name + " not found (inherited by " + this.name + ")");2458        }2459        if (current_interface.prevent_multiple_testing && current_interface.already_tested)2460        {2461            return;2462        }2463        current_interface.test_interface_of(desc, obj, exception, expected_typeof);2464        current_interface = this.array.members[current_interface.base];2465    }2466};2467IdlInterface.prototype.test_primary_interface_of = function(desc, obj, exception, expected_typeof)2468{2469    // Only the object itself, not its members, are tested here, so if the2470    // interface is untested, there is nothing to do.2471    if (this.untested)2472    {2473        return;2474    }2475    // "The internal [[SetPrototypeOf]] method of every platform object that2476    // implements an interface with the [Global] extended2477    // attribute must execute the same algorithm as is defined for the2478    // [[SetPrototypeOf]] internal method of an immutable prototype exotic2479    // object."2480    // https://webidl.spec.whatwg.org/#platform-object-setprototypeof2481    if (this.is_global())2482    {2483        this.test_immutable_prototype("global platform object", obj);2484    }2485    // We can't easily test that its prototype is correct if there's no2486    // interface object, or the object is from a different global environment2487    // (not instanceof Object).  TODO: test in this case that its prototype at2488    // least looks correct, even if we can't test that it's actually correct.2489    if (this.should_have_interface_object()2490    && (typeof obj != expected_typeof || obj instanceof Object))2491    {2492        subsetTestByKey(this.name, test, function()2493        {2494            assert_equals(exception, null, "Unexpected exception when evaluating object");2495            assert_equals(typeof obj, expected_typeof, "wrong typeof object");2496            this.assert_interface_object_exists();2497            assert_own_property(this.get_interface_object(), "prototype",2498                                'interface "' + this.name + '" does not have own property "prototype"');2499            // "The value of the internal [[Prototype]] property of the2500            // platform object is the interface prototype object of the primary2501            // interface from the platform objectâs associated global2502            // environment."2503            assert_equals(Object.getPrototypeOf(obj),2504                          this.get_interface_object().prototype,2505                          desc + "'s prototype is not " + this.name + ".prototype");2506        }.bind(this), this.name + " must be primary interface of " + desc);2507    }2508    // "The class string of a platform object that implements one or more2509    // interfaces must be the qualified name of the primary interface of the2510    // platform object."2511    subsetTestByKey(this.name, test, function()2512    {2513        assert_equals(exception, null, "Unexpected exception when evaluating object");2514        assert_equals(typeof obj, expected_typeof, "wrong typeof object");2515        assert_class_string(obj, this.get_qualified_name(), "class string of " + desc);2516        if (!this.has_stringifier())2517        {2518            assert_equals(String(obj), "[object " + this.get_qualified_name() + "]", "String(" + desc + ")");2519        }2520    }.bind(this), "Stringification of " + desc);2521};2522IdlInterface.prototype.test_interface_of = function(desc, obj, exception, expected_typeof)2523{2524    // TODO: Indexed and named properties, more checks on interface members2525    this.already_tested = true;2526    if (!shouldRunSubTest(this.name)) {2527        return;2528    }2529    for (var i = 0; i < this.members.length; i++)2530    {2531        var member = this.members[i];2532        if (member.untested) {2533            continue;2534        }2535        if (!exposed_in(exposure_set(member, this.exposureSet))) {2536            subsetTestByKey(this.name, test, function() {2537                assert_equals(exception, null, "Unexpected exception when evaluating object");2538                assert_false(member.name in obj);2539            }.bind(this), this.name + " interface: " + desc + ' must not have property "' + member.name + '"');2540            continue;2541        }2542        if (member.type == "attribute" && member.isUnforgeable)2543        {2544            var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: " + desc + ' must have own property "' + member.name + '"');2545            a_test.step(function() {2546                assert_equals(exception, null, "Unexpected exception when evaluating object");2547                assert_equals(typeof obj, expected_typeof, "wrong typeof object");2548                // Call do_interface_attribute_asserts last, since it will call a_test.done()2549                this.do_interface_attribute_asserts(obj, member, a_test);2550            }.bind(this));2551        }2552        else if (member.type == "operation" &&2553                 member.name &&2554                 member.isUnforgeable)2555        {2556            var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: " + desc + ' must have own property "' + member.name + '"');2557            a_test.step(function()2558            {2559                assert_equals(exception, null, "Unexpected exception when evaluating object");2560                assert_equals(typeof obj, expected_typeof, "wrong typeof object");2561                assert_own_property(obj, member.name,2562                                    "Doesn't have the unforgeable operation property");2563                this.do_member_operation_asserts(obj, member, a_test);2564            }.bind(this));2565        }2566        else if ((member.type == "const"2567        || member.type == "attribute"2568        || member.type == "operation")2569        && member.name)2570        {2571            subsetTestByKey(this.name, test, function()2572            {2573                assert_equals(exception, null, "Unexpected exception when evaluating object");2574                assert_equals(typeof obj, expected_typeof, "wrong typeof object");2575                if (member.special !== "static") {2576                    if (!this.is_global()) {2577                        assert_inherits(obj, member.name);2578                    } else {2579                        assert_own_property(obj, member.name);2580                    }2581                    if (member.type == "const")2582                    {2583                        assert_equals(obj[member.name], constValue(member.value));2584                    }2585                    if (member.type == "attribute")2586                    {2587                        // Attributes are accessor properties, so they might2588                        // legitimately throw an exception rather than returning2589                        // anything.2590                        var property, thrown = false;2591                        try2592                        {2593                            property = obj[member.name];2594                        }2595                        catch (e)2596                        {2597                            thrown = true;2598                        }2599                        if (!thrown)2600                        {2601                            if (this.name == "Document" && member.name == "all")2602                            {2603                                // Result of [[IsHTMLDDA]] slot2604                                assert_equals(typeof property, "undefined");2605                            }2606                            else2607                            {2608                                this.array.assert_type_is(property, member.idlType);2609                            }2610                        }2611                    }2612                    if (member.type == "operation")2613                    {2614                        assert_equals(typeof obj[member.name], "function");2615                    }2616                }2617            }.bind(this), this.name + " interface: " + desc + ' must inherit property "' + member + '" with the proper type');2618        }2619        // TODO: This is wrong if there are multiple operations with the same2620        // identifier.2621        // TODO: Test passing arguments of the wrong type.2622        if (member.type == "operation" && member.name && member.arguments.length)2623        {2624            var description =2625                this.name + " interface: calling " + member + " on " + desc +2626                " with too few arguments must throw TypeError";2627            var a_test = subsetTestByKey(this.name, async_test, description);2628            a_test.step(function()2629            {2630                assert_equals(exception, null, "Unexpected exception when evaluating object");2631                assert_equals(typeof obj, expected_typeof, "wrong typeof object");2632                var fn;2633                if (member.special !== "static") {2634                    if (!this.is_global() && !member.isUnforgeable) {2635                        assert_inherits(obj, member.name);2636                    } else {2637                        assert_own_property(obj, member.name);2638                    }2639                    fn = obj[member.name];2640                }2641                else2642                {2643                    assert_own_property(obj.constructor, member.name, "interface object must have static operation as own property");2644                    fn = obj.constructor[member.name];2645                }2646                var minLength = minOverloadLength(this.members.filter(function(m) {2647                    return m.type == "operation" && m.name == member.name;2648                }));2649                var args = [];2650                var cb = awaitNCallbacks(minLength, a_test.done.bind(a_test));2651                for (var i = 0; i < minLength; i++) {2652                    throwOrReject(a_test, member, fn, obj, args, "Called with " + i + " arguments", cb);2653                    args.push(create_suitable_object(member.arguments[i].idlType));2654                }2655                if (minLength === 0) {2656                    cb();2657                }2658            }.bind(this));2659        }2660        if (member.is_to_json_regular_operation()) {2661            this.test_to_json_operation(desc, obj, member);2662        }2663    }2664};2665IdlInterface.prototype.has_stringifier = function()2666{2667    if (this.name === "DOMException") {2668        // toString is inherited from Error, so don't assume we have the2669        // default stringifer2670        return true;2671    }2672    if (this.members.some(function(member) { return member.special === "stringifier"; })) {2673        return true;2674    }2675    if (this.base &&2676        this.array.members[this.base].has_stringifier()) {2677        return true;2678    }2679    return false;2680};2681IdlInterface.prototype.do_interface_attribute_asserts = function(obj, member, a_test)2682{2683    // This function tests WebIDL as of 2015-01-27.2684    // TODO: Consider [Exposed].2685    // This is called by test_member_attribute() with the prototype as obj if2686    // it is not a global, and the global otherwise, and by test_interface_of()2687    // with the object as obj.2688    var pendingPromises = [];2689    // "The name of the property is the identifier of the attribute."2690    assert_own_property(obj, member.name);2691    // "The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:2692    // true, [[Configurable]]: configurable }, where:2693    // "configurable is false if the attribute was declared with the2694    // [LegacyUnforgeable] extended attribute and true otherwise;2695    // "G is the attribute getter, defined below; and2696    // "S is the attribute setter, also defined below."2697    var desc = Object.getOwnPropertyDescriptor(obj, member.name);2698    assert_false("value" in desc, 'property descriptor should not have a "value" field');2699    assert_false("writable" in desc, 'property descriptor should not have a "writable" field');2700    assert_true(desc.enumerable, "property should be enumerable");2701    if (member.isUnforgeable)2702    {2703        assert_false(desc.configurable, "[LegacyUnforgeable] property must not be configurable");2704    }2705    else2706    {2707        assert_true(desc.configurable, "property must be configurable");2708    }2709    // "The attribute getter is a Function object whose behavior when invoked2710    // is as follows:"2711    assert_equals(typeof desc.get, "function", "getter must be Function");2712    // "If the attribute is a regular attribute, then:"2713    if (member.special !== "static") {2714        // "If O is not a platform object that implements I, then:2715        // "If the attribute was specified with the [LegacyLenientThis] extended2716        // attribute, then return undefined.2717        // "Otherwise, throw a TypeError."2718        if (!member.has_extended_attribute("LegacyLenientThis")) {2719            if (member.idlType.generic !== "Promise") {2720                assert_throws_js(globalOf(desc.get).TypeError, function() {2721                    desc.get.call({});2722                }.bind(this), "calling getter on wrong object type must throw TypeError");2723            } else {2724                pendingPromises.push(2725                    promise_rejects_js(a_test, TypeError, desc.get.call({}),2726                                    "calling getter on wrong object type must reject the return promise with TypeError"));2727            }2728        } else {2729            assert_equals(desc.get.call({}), undefined,2730                          "calling getter on wrong object type must return undefined");2731        }2732    }2733    // "The value of the Function objectâs âlengthâ property is the Number2734    // value 0."2735    assert_equals(desc.get.length, 0, "getter length must be 0");2736    // "Let name be the string "get " prepended to attributeâs identifier."2737    // "Perform ! SetFunctionName(F, name)."2738    assert_equals(desc.get.name, "get " + member.name,2739        "getter must have the name 'get " + member.name + "'");2740    // TODO: Test calling setter on the interface prototype (should throw2741    // TypeError in most cases).2742    if (member.readonly2743    && !member.has_extended_attribute("LegacyLenientSetter")2744    && !member.has_extended_attribute("PutForwards")2745    && !member.has_extended_attribute("Replaceable"))2746    {2747        // "The attribute setter is undefined if the attribute is declared2748        // readonly and has neither a [PutForwards] nor a [Replaceable]2749        // extended attribute declared on it."2750        assert_equals(desc.set, undefined, "setter must be undefined for readonly attributes");2751    }2752    else2753    {2754        // "Otherwise, it is a Function object whose behavior when2755        // invoked is as follows:"2756        assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes");2757        // "If the attribute is a regular attribute, then:"2758        if (member.special !== "static") {2759            // "If /validThis/ is false and the attribute was not specified2760            // with the [LegacyLenientThis] extended attribute, then throw a2761            // TypeError."2762            // "If the attribute is declared with a [Replaceable] extended2763            // attribute, then: ..."2764            // "If validThis is false, then return."2765            if (!member.has_extended_attribute("LegacyLenientThis")) {2766                assert_throws_js(globalOf(desc.set).TypeError, function() {2767                    desc.set.call({});2768                }.bind(this), "calling setter on wrong object type must throw TypeError");2769            } else {2770                assert_equals(desc.set.call({}), undefined,2771                              "calling setter on wrong object type must return undefined");2772            }2773        }2774        // "The value of the Function objectâs âlengthâ property is the Number2775        // value 1."2776        assert_equals(desc.set.length, 1, "setter length must be 1");2777        // "Let name be the string "set " prepended to id."2778        // "Perform ! SetFunctionName(F, name)."2779        assert_equals(desc.set.name, "set " + member.name,2780            "The attribute setter must have the name 'set " + member.name + "'");2781    }2782    Promise.all(pendingPromises).then(a_test.done.bind(a_test));2783}2784/// IdlInterfaceMember ///2785function IdlInterfaceMember(obj)2786{2787    /**2788     * obj is an object produced by the WebIDLParser.js "ifMember" production.2789     * We just forward all properties to this object without modification,2790     * except for special extAttrs handling.2791     */2792    for (var k in obj.toJSON())2793    {2794        this[k] = obj[k];2795    }2796    if (!("extAttrs" in this))2797    {2798        this.extAttrs = [];2799    }2800    this.isUnforgeable = this.has_extended_attribute("LegacyUnforgeable");2801    this.isUnscopable = this.has_extended_attribute("Unscopable");2802}2803IdlInterfaceMember.prototype = Object.create(IdlObject.prototype);2804IdlInterfaceMember.prototype.toJSON = function() {2805    return this;2806};2807IdlInterfaceMember.prototype.is_to_json_regular_operation = function() {2808    return this.type == "operation" && this.special !== "static" && this.name == "toJSON";2809};2810IdlInterfaceMember.prototype.toString = function() {2811    function formatType(type) {2812        var result;2813        if (type.generic) {2814            result = type.generic + "<" + type.idlType.map(formatType).join(", ") + ">";2815        } else if (type.union) {2816            result = "(" + type.subtype.map(formatType).join(" or ") + ")";2817        } else {2818            result = type.idlType;2819        }2820        if (type.nullable) {2821            result += "?"2822        }2823        return result;2824    }2825    if (this.type === "operation") {2826        var args = this.arguments.map(function(m) {2827            return [2828                m.optional ? "optional " : "",2829                formatType(m.idlType),2830                m.variadic ? "..." : "",2831            ].join("");2832        }).join(", ");2833        return this.name + "(" + args + ")";2834    }2835    return this.name;2836}2837/// Internal helper functions ///2838function create_suitable_object(type)2839{2840    /**2841     * type is an object produced by the WebIDLParser.js "type" production.  We2842     * return a JavaScript value that matches the type, if we can figure out2843     * how.2844     */2845    if (type.nullable)2846    {2847        return null;2848    }2849    switch (type.idlType)2850    {2851        case "any":2852        case "boolean":2853            return true;2854        case "byte": case "octet": case "short": case "unsigned short":2855        case "long": case "unsigned long": case "long long":2856        case "unsigned long long": case "float": case "double":2857        case "unrestricted float": case "unrestricted double":2858            return 7;2859        case "DOMString":2860        case "ByteString":2861        case "USVString":2862            return "foo";2863        case "object":2864            return {a: "b"};2865        case "Node":2866            return document.createTextNode("abc");2867    }2868    return null;2869}2870/// IdlEnum ///2871// Used for IdlArray.prototype.assert_type_is2872function IdlEnum(obj)2873{2874    /**2875     * obj is an object produced by the WebIDLParser.js "dictionary"2876     * production.2877     */2878    /** Self-explanatory. */2879    this.name = obj.name;2880    /** An array of values produced by the "enum" production. */2881    this.values = obj.values;2882}2883IdlEnum.prototype = Object.create(IdlObject.prototype);2884/// IdlCallback ///2885// Used for IdlArray.prototype.assert_type_is2886function IdlCallback(obj)2887{2888    /**2889     * obj is an object produced by the WebIDLParser.js "callback"2890     * production.2891     */2892    /** Self-explanatory. */2893    this.name = obj.name;2894    /** Arguments for the callback. */2895    this.arguments = obj.arguments;2896}2897IdlCallback.prototype = Object.create(IdlObject.prototype);2898/// IdlTypedef ///2899// Used for IdlArray.prototype.assert_type_is2900function IdlTypedef(obj)2901{2902    /**2903     * obj is an object produced by the WebIDLParser.js "typedef"2904     * production.2905     */2906    /** Self-explanatory. */2907    this.name = obj.name;2908    /** The idlType that we are supposed to be typedeffing to. */2909    this.idlType = obj.idlType;2910}2911IdlTypedef.prototype = Object.create(IdlObject.prototype);2912/// IdlNamespace ///2913function IdlNamespace(obj)2914{2915    this.name = obj.name;2916    this.extAttrs = obj.extAttrs;2917    this.untested = obj.untested;2918    /** A back-reference to our IdlArray. */2919    this.array = obj.array;2920    /** An array of IdlInterfaceMembers. */2921    this.members = obj.members.map(m => new IdlInterfaceMember(m));2922}2923IdlNamespace.prototype = Object.create(IdlObject.prototype);2924IdlNamespace.prototype.do_member_operation_asserts = function (memberHolderObject, member, a_test)2925{2926    var desc = Object.getOwnPropertyDescriptor(memberHolderObject, member.name);2927    assert_false("get" in desc, "property should not have a getter");2928    assert_false("set" in desc, "property should not have a setter");2929    assert_equals(2930        desc.writable,2931        !member.isUnforgeable,2932        "property should be writable if and only if not unforgeable");2933    assert_true(desc.enumerable, "property should be enumerable");2934    assert_equals(2935        desc.configurable,2936        !member.isUnforgeable,2937        "property should be configurable if and only if not unforgeable");2938    assert_equals(2939        typeof memberHolderObject[member.name],2940        "function",2941         "property must be a function");2942    assert_equals(2943        memberHolderObject[member.name].length,2944        minOverloadLength(this.members.filter(function(m) {2945            return m.type == "operation" && m.name == member.name;2946        })),2947        "operation has wrong .length");2948    a_test.done();2949}2950IdlNamespace.prototype.test_member_operation = function(member)2951{2952    if (!shouldRunSubTest(this.name)) {2953        return;2954    }2955    var a_test = subsetTestByKey(2956        this.name,2957        async_test,2958        this.name + ' namespace: operation ' + member);2959    a_test.step(function() {2960        assert_own_property(2961            self[this.name],2962            member.name,2963            'namespace object missing operation ' + format_value(member.name));2964        this.do_member_operation_asserts(self[this.name], member, a_test);2965    }.bind(this));2966};2967IdlNamespace.prototype.test_member_attribute = function (member)2968{2969    if (!shouldRunSubTest(this.name)) {2970        return;2971    }2972    var a_test = subsetTestByKey(2973        this.name,2974        async_test,2975        this.name + ' namespace: attribute ' + member.name);2976    a_test.step(function()2977    {2978        assert_own_property(2979            self[this.name],2980            member.name,2981            this.name + ' does not have property ' + format_value(member.name));2982        var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name);2983        assert_equals(desc.set, undefined, "setter must be undefined for namespace members");2984        a_test.done();2985    }.bind(this));2986};2987IdlNamespace.prototype.test_self = function ()2988{2989    /**2990     * TODO(lukebjerring): Assert:2991     * - "Note that unlike interfaces or dictionaries, namespaces do not create types."2992     */2993    subsetTestByKey(this.name, test, () => {2994        assert_true(this.extAttrs.every(o => o.name === "Exposed" || o.name === "SecureContext"),2995            "Only the [Exposed] and [SecureContext] extended attributes are applicable to namespaces");2996        assert_true(this.has_extended_attribute("Exposed"),2997            "Namespaces must be annotated with the [Exposed] extended attribute");2998    }, `${this.name} namespace: extended attributes`);2999    const namespaceObject = self[this.name];3000    subsetTestByKey(this.name, test, () => {3001        const desc = Object.getOwnPropertyDescriptor(self, this.name);3002        assert_equals(desc.value, namespaceObject, `wrong value for ${this.name} namespace object`);3003        assert_true(desc.writable, "namespace object should be writable");3004        assert_false(desc.enumerable, "namespace object should not be enumerable");3005        assert_true(desc.configurable, "namespace object should be configurable");3006        assert_false("get" in desc, "namespace object should not have a getter");3007        assert_false("set" in desc, "namespace object should not have a setter");3008    }, `${this.name} namespace: property descriptor`);3009    subsetTestByKey(this.name, test, () => {3010        assert_true(Object.isExtensible(namespaceObject));3011    }, `${this.name} namespace: [[Extensible]] is true`);3012    subsetTestByKey(this.name, test, () => {3013        assert_true(namespaceObject instanceof Object);3014        if (this.name === "console") {3015            // https://console.spec.whatwg.org/#console-namespace3016            const namespacePrototype = Object.getPrototypeOf(namespaceObject);3017            assert_equals(Reflect.ownKeys(namespacePrototype).length, 0);3018            assert_equals(Object.getPrototypeOf(namespacePrototype), Object.prototype);3019        } else {3020            assert_equals(Object.getPrototypeOf(namespaceObject), Object.prototype);3021        }3022    }, `${this.name} namespace: [[Prototype]] is Object.prototype`);3023    subsetTestByKey(this.name, test, () => {3024        assert_equals(typeof namespaceObject, "object");3025    }, `${this.name} namespace: typeof is "object"`);3026    subsetTestByKey(this.name, test, () => {3027        assert_equals(3028            Object.getOwnPropertyDescriptor(namespaceObject, "length"),3029            undefined,3030            "length property must be undefined"3031        );3032    }, `${this.name} namespace: has no length property`);3033    subsetTestByKey(this.name, test, () => {3034        assert_equals(3035            Object.getOwnPropertyDescriptor(namespaceObject, "name"),3036            undefined,3037            "name property must be undefined"3038        );3039    }, `${this.name} namespace: has no name property`);3040};3041IdlNamespace.prototype.test = function ()3042{3043    if (!this.untested) {3044        this.test_self();3045    }3046    for (const v of Object.values(this.members)) {3047        switch (v.type) {3048        case 'operation':3049            this.test_member_operation(v);3050            break;3051        case 'attribute':3052            this.test_member_attribute(v);3053            break;3054        default:3055            throw 'Invalid namespace member ' + v.name + ': ' + v.type + ' not supported';3056        }3057    };3058};3059}());3060/**3061 * idl_test is a promise_test wrapper that handles the fetching of the IDL,3062 * avoiding repetitive boilerplate.3063 *3064 * @param {String[]} srcs Spec name(s) for source idl files (fetched from3065 *      /interfaces/{name}.idl).3066 * @param {String[]} deps Spec name(s) for dependency idl files (fetched3067 *      from /interfaces/{name}.idl). Order is important - dependencies from3068 *      each source will only be included if they're already know to be a3069 *      dependency (i.e. have already been seen).3070 * @param {Function} setup_func Function for extra setup of the idl_array, such3071 *      as adding objects. Do not call idl_array.test() in the setup; it is3072 *      called by this function (idl_test).3073 */3074function idl_test(srcs, deps, idl_setup_func) {3075    return promise_test(function (t) {3076        var idl_array = new IdlArray();3077        var setup_error = null;3078        const validationIgnored = [3079            "constructor-member",3080            "dict-arg-default",3081            "require-exposed"3082        ];3083        return Promise.all(3084            srcs.concat(deps).map(fetch_spec))3085            .then(function(results) {3086                const astArray = results.map(result =>3087                    WebIDL2.parse(result.idl, { sourceName: result.spec })3088                );3089                test(() => {3090                    const validations = WebIDL2.validate(astArray)3091                        .filter(v => !validationIgnored.includes(v.ruleName));3092                    if (validations.length) {3093                        const message = validations.map(v => v.message).join("\n\n");3094                        throw new Error(message);3095                    }3096                }, "idl_test validation");3097                for (var i = 0; i < srcs.length; i++) {3098                    idl_array.internal_add_idls(astArray[i]);3099                }3100                for (var i = srcs.length; i < srcs.length + deps.length; i++) {3101                    idl_array.internal_add_dependency_idls(astArray[i]);3102                }3103            })3104            .then(function() {3105                if (idl_setup_func) {3106                    return idl_setup_func(idl_array, t);3107                }3108            })3109            .catch(function(e) { setup_error = e || 'IDL setup failed.'; })3110            .then(function () {3111                var error = setup_error;3112                try {3113                    idl_array.test(); // Test what we can.3114                } catch (e) {3115                    // If testing fails hard here, the original setup error3116                    // is more likely to be the real cause.3117                    error = error || e;3118                }3119                if (error) {3120                    throw error;3121                }3122            });3123    }, 'idl_test setup');3124}3125/**3126 * fetch_spec is a shorthand for a Promise that fetches the spec's content.3127 */3128function fetch_spec(spec) {3129    var url = '/interfaces/' + spec + '.idl';3130    return fetch(url).then(function (r) {3131        if (!r.ok) {3132            throw new IdlHarnessError("Error fetching " + url + ".");3133        }3134        return r.text();3135    }).then(idl => ({ spec, idl }));3136}...main.component.ts
Source:main.component.ts  
1import { Component, OnInit } from '@angular/core';2import { FormBuilder, FormGroup, Validators } from '@angular/forms';3import { ToastrService } from 'ngx-toastr';4import { IMain } from 'src/app/interface/main.interface';5import { MainServiceService } from 'src/app/service/main-service.service';6@Component({7  selector: 'app-main',8  templateUrl: './main.component.html',9  styleUrls: ['./main.component.scss']10})11export class MainComponent implements OnInit {12  AdvertisementForm: FormGroup;13  EditForm: FormGroup;14  getTitle:string;15  titleDetails:string;16  descriptionDetails:string;17  dateDetails:string;18  showModalAdd: boolean = false;19  showModalEdit: boolean = false;20  showDetails:boolean = false;21  showSearch:boolean = true22  checkAds:boolean = true23  myData:any;24  searchArray:object[];25  setSimilarAnnouncement:string[]26  date: Date = new Date();27  id: number;28  constructor(29    private FormBuilder: FormBuilder,30    private mainService: MainServiceService,31    private toastr: ToastrService32  ) { }33  ngOnInit(): void {34    this.getData()35    this.initFormAdd()36    this.initEditForm()37  }38  getData(): void {39    this.mainService.get().subscribe(data => {40      this.myData = data;41      console.log(data);42      43      if(this.myData.length <=0){44        this.checkAds = true45      }46      else{47        this.checkAds = false48      }49    })50  }51  initFormAdd(): void {52    this.AdvertisementForm = this.FormBuilder.group({53      title: [null, Validators.required],54      description: [null, Validators.required]55    })56  }57  initEditForm(): void {58    this.EditForm = this.FormBuilder.group({59      title: [null, Validators.required],60      description: [null, Validators.required]61    })62  }63  addAnnouncement(): void {64    this.showModalAdd = true;65    document.body.style.background = 'rgba(0, 0, 0, 0.05)';66  }67  saveAnnouncment(): void {68    if (this.AdvertisementForm.value.title && this.AdvertisementForm.value.description) {69      let getDate:any  = this.date.getDate();70      let getMonth:any  = this.date.getMonth();71      let getMinutes:any = this.date.getMinutes();72      if(getDate < 10){73        getDate  = '0'+ getDate;74      }75      if(getMonth < 10){76        getMonth  = '0'+ getMonth;77      }78      if(getMinutes < 10){79        getMinutes  = '0'+ getMinutes;80      }81      else if(getDate >=10){82        getDate;83      }84      else if(getMonth >=10){85        getDate;86      }87      else if(getMinutes >=10){88        getMinutes;89      }90      const item = {91        ...this.AdvertisementForm.value,92        date: `${getDate}.${getMonth}.${this.date.getFullYear()} | ${this.date.getHours()}:${getMinutes}`93      }94      this.mainService.post(item).subscribe(() => {95        this.getData()96      })97      this.AdvertisementForm.reset();98      this.showModalAdd = false;99      this.success('Announcement created');100      document.body.style.background = 'none';101    }102    else {103      this.error('Please , fill all of the fields ');104    }105  }106  editAnnouncement(item): void {107    this.EditForm.patchValue({108      title: item.title,109      description: item.description110    });111    this.showModalEdit = true;112    this.id = item.id;113    document.body.style.background = 'rgba(0, 0, 0, 0.05)';114  }115  saveEdit(): void {116    if (this.EditForm.value.title && this.EditForm.value.description) {117      let getDate:any  = this.date.getDate();118      let getMonth:any  = this.date.getMonth();119      let getMinutes:any = this.date.getMinutes();120      if(getDate < 10){121        getDate  = '0'+ getDate;122      }123      if(getMonth < 10){124        getMonth  = '0'+ getMonth;125      }126      if(getMinutes < 10){127        getMinutes  = '0'+ getMinutes;128      }129      else if(getDate >=10){130        getDate;131      }132      else if(getMonth >=10){133        getDate;134      }135      else if(getMinutes >=10){136        getMinutes;137      }138      const editItem = {139        ...this.EditForm.value,140        date: `${getDate}.${getMonth}.${this.date.getFullYear()} | ${this.date.getHours()}:${getMinutes}`141      }142      this.mainService.update(editItem, this.id).subscribe(() => {143        this.getData();144      })145      this.showModalEdit = false;146      document.body.style.background = 'none';147      this.success('Successfully edited');148      this.showSearch = true149      this.searchArray = []150    }151    else {152      this.error('Please , fill all of the fields ');153    }154  }155  deleteAnnouncement(id): void {156    this.mainService.delete(id).subscribe(() => {157      this.getData();158      this.success('Successfully deleted');159    })160    this.showSearch = true161    this.searchArray = []162  }163  infoAnnouncement(item):void{164    this.titleDetails= item.title165    this.descriptionDetails=item.description166    this.dateDetails=item.date167    this.showDetails = true;168    document.body.style.background = 'rgba(0, 0, 0, 0.05)';169    const splitTitle = item.title.split(' ')170    const splitDescription = item.description.split(' ')171    let similarAnnouncement = []172    for (const iteratorTitle of splitTitle) {173      for (const iteratorDesc of splitDescription) {174          this.myData.filter(function(announcement){175            if(announcement.title.includes(iteratorTitle) && announcement.description.includes(iteratorDesc)){176              if(similarAnnouncement.length <3){177                if(!similarAnnouncement.includes(announcement)){178                  similarAnnouncement.push(announcement)179                }180              }181            }182          })183      }184    }185    this.setSimilarAnnouncement = similarAnnouncement186  }187  search():void{188    let getValue = document.querySelector('.search') as HTMLInputElement    189    if(this.getTitle){190      const filterArray = this.myData.filter(function(item){191        if(item.title == getValue.value.replace(/\s{2,}/g,"")){192          return {item}193        }194      })195      this.searchArray = filterArray196      if(this.searchArray.length > 0){197        this.showSearch = false198  199      }200      else{201        this.error('There is no such title')202        this.showSearch = true203      }204    }205    else{206      this.warning('Please , enter the title')207    }208    209    210    211    this.getTitle = ''    212  }213  backToAll():void{214    this.showSearch = true215    this.searchArray = []216  }217  closeAdd(): void {218    this.showModalAdd = false;219    this.showModalEdit = false;220    this.showDetails = false;221    document.body.style.background = 'none';222  }223  224  success(messege): void {225    this.toastr.success(messege);226  }227  error(messege): void {228    this.toastr.error(messege);229  }230  warning(messege): void {231    this.toastr.warning(messege);232  }...getEntries.js
Source:getEntries.js  
1// @flow2import type { EntriesDescriptor, RemoteObjectId } from './types';3import { getObject } from './remoteObject';4import serialize from './serialize';5export type GetEntriesConfig = {6    limit?: number;7}8type ObjectIteratorDescriptor = {9    iterator: Iterator<*>;10    consumedCount: number;11}12type ObjectIterators = Map<number, ObjectIteratorDescriptor>;13const iteratorsByObject:WeakMap<Object, ObjectIterators> = new WeakMap();14function acquireIterator(object: Object, iteratorId) : [boolean, Iterator<any>] {15    if (iteratorId != null && iteratorsByObject.has(object)) {16        const iteratorDescriptor = iteratorsByObject.get(object).get(iteratorId);17        if (iteratorDescriptor) {18            return [false, iteratorDescriptor.iterator];19        }20    }21    return [true, object[Symbol.iterator]()];22}23export default function getEntries(24    objectId: RemoteObjectId,25    iteratorId: ?number = null,26    config:GetEntriesConfig = {}27) : EntriesDescriptor {28    const object = getObject(objectId);29    if (!object) {30        throw new Error('Object does not exist');31    }32    if (!object[Symbol.iterator]) {33        throw new Error('Object is not iterable');34    }35    const objectDescriptor = serialize(object);36    if (objectDescriptor.type !== 'object') {37        throw new Error('Invalid object found; was not serialized to expected type');38    }39    let totalEntries = null;40    const limit = config.limit;41    let size;42    // This it to appease flow - doing it in one condition below didn't work43    if (objectDescriptor.size != null) {44        size = objectDescriptor.size;45    }46    if (size && size !== 'Infinity') {47        totalEntries = objectDescriptor.size;48    } else if (!limit) {49        // TODO: Alternatively just iterate to fixed 'safe' limit and throw?50        throw new Error('Potentially infinite collection; you must specify a limit');51    }52    const [isNewIterator, it] = acquireIterator(object, iteratorId);53    let n = it.next();54    const values = [];55    while (!n.done) {56        let subType = null;57        // This is to appease flow - doing it in one condition below didn't58        // work for some reason59        if (objectDescriptor.subType != null) {60            subType = objectDescriptor.subType;61        }62        if (subType === 'map') {63            values.push([serialize(n.value[0]), serialize(n.value[1])]);64        } else {65            values.push(serialize(n.value));66        }67        if (limit && values.length >= limit) {68            break;69        }70        n = it.next();71    }72    let iteratorData;73    let done = n.done;74    if (!done) {75        if (!iteratorsByObject.has(object)) {76            iteratorsByObject.set(object, new Map());77        }78        const iterators = iteratorsByObject.get(object);79        let id = iteratorId;80        let iteratorDesc = id != null && iterators.get(id);81        if (!iteratorDesc) {82            id = iterators.size;83            iteratorDesc = {84                iterator: it,85                consumedCount: 0,86            };87            iterators.set(id, iteratorDesc);88        }89        iteratorDesc.consumedCount += values.length;90        iteratorData = { iteratorId: id };91        // If we know the size of the iterator check if we have actually consumed it all92        if (totalEntries && totalEntries === iteratorDesc.consumedCount) {93            done = true;94            iteratorData = null;95        }96    }97    return {98        objectId,99        isNewIterator,100        result: values,101        done,102        ...iteratorData,103    };...Using AI Code Generation
1var WPTree = require('./wptree.js');2var fs = require('fs');3var path = require('path');4var dir = process.argv[2];5var ext = process.argv[3];6var tree = new WPTree(dir);7tree.iteratorDesc(function (file) {8  if (path.extname(file) === '.' + ext) {9    console.log(file);10  }11});Using AI Code Generation
1var wptree = require('wptree');2var tree = wptree.createTree();3var data = [1,2,3,4,5,6,7,8,9,10];4tree.init(data);5var iter = tree.iteratorDesc();6while(iter.hasNext()) {7	console.log(iter.next());8}9var wptree = require('wptree');10var tree = wptree.createTree();11var data = [1,2,3,4,5,6,7,8,9,10];12tree.init(data);13var iter = tree.iteratorAsc();14while(iter.hasNext()) {15	console.log(iter.next());16}17var wptree = require('wptree');18var tree = wptree.createTree();19var data = [1,2,3,4,5,6,7,8,9,10];20tree.init(data);21var iter = tree.iteratorAscFrom(3);22while(iter.hasNext()) {23	console.log(iter.next());24}25var wptree = require('wptree');26var tree = wptree.createTree();27var data = [1,2,3,4,5,6,7,8,9,10];28tree.init(data);29var iter = tree.iteratorDescFrom(7);30while(iter.hasNext()) {31	console.log(iter.next());32}Using AI Code Generation
1var wptree = require('./wptree');2var fs = require('fs');3var path = require('path');4var dirPath = process.argv[2];5var depth = process.argv[3];6var tree = new wptree(dirPath, depth);7var iterator = tree.iteratorDesc();8var next = iterator.next();9while(!next.done) {10	console.log(next.value);11	next = iterator.next();12}Using AI Code Generation
1var wptree = require('wptree');2var path = require('path');3var tree = wptree.create();4tree.add('a');5tree.add('a/b');6tree.add('a/c');7tree.add('a/c/d');8tree.add('a/c/e');9tree.add('a/c/f');10tree.add('a/c/g');11tree.add('a/c/h');12tree.add('a/c/i');13tree.add('a/c/j');14tree.add('a/c/k');15tree.add('a/c/l');16tree.add('a/c/m');17tree.add('a/c/n');18tree.add('a/c/o');19tree.add('a/c/p');20tree.add('a/c/q');21tree.add('a/c/r');22tree.add('a/c/s');23tree.add('a/c/t');24tree.add('a/c/u');25tree.add('a/c/v');26tree.add('a/c/w');27tree.add('a/c/x');28tree.add('a/c/y');29tree.add('a/c/z');30tree.add('a/c/aa');31tree.add('a/c/ab');32tree.add('a/c/ac');33tree.add('a/c/ad');34tree.add('a/c/ae');35tree.add('a/c/af');36tree.add('a/c/ag');37tree.add('a/c/ah');38tree.add('a/c/ai');39tree.add('a/c/aj');40tree.add('a/c/ak');41tree.add('a/c/al');42tree.add('a/c/am');43tree.add('a/c/an');44tree.add('a/c/ao');45tree.add('a/c/ap');46tree.add('a/c/aq');47tree.add('a/c/ar');48tree.add('a/c/as');49tree.add('a/c/at');50tree.add('a/c/au');51tree.add('a/c/av');52tree.add('a/c/aw');53tree.add('a/c/ax');54tree.add('a/c/ay');55tree.add('a/c/az');56tree.add('a/c/ba');57tree.add('a/c/bb');58tree.add('a/c/bc');59tree.add('a/c/bd');60tree.add('a/c/be');61tree.add('a/c/bf');62tree.add('a/c/bg');63tree.add('a/c/bh');64tree.add('a/c/bi');65tree.add('a/c/bj');66tree.add('a/c/bk');67tree.add('a/c/bl');68tree.add('Using AI Code Generation
1var wptree = require('./wptree.js');2var tree = new wptree({value: 1, left: {value: 2, left: {value:4}, right: {value:5}}, right: {value: 3, left: {value:6}, right: {value:7}}});3var it = tree.iteratorDesc();4var node = it.next();5while(!node.done){6	console.log(node.value);7	node = it.next();8}Using AI Code Generation
1var wptree = require('wptree');2var tree = new wptree();3var iter = tree.iteratorDesc();4var node = iter.next();5while(node != null){6    console.log(node);7    node = iter.next();8}Using AI Code Generation
1var wptree = require('./wptree');2var fs = require('fs');3var data = fs.readFileSync("data.json");4var obj = JSON.parse(data);5var tree = new wptree(obj);6var iterator = tree.iteratorDesc("A");7var next = iterator.next();8while(!next.done) {9    console.log(next.value);10    next = iterator.next();11}Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
