Best JavaScript code snippet using wpt
idlharness.js
Source:idlharness.js
1/*2Distributed under both the W3C Test Suite License [1] and the W3C33-clause BSD License [2]. To contribute to a W3C Test Suite, see the4policies and contribution forms [3].5[1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license6[2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license7[3] http://www.w3.org/2004/10/27-testcases8*/9/* For user documentation see docs/_writing-tests/idlharness.md */10/**11 * Notes for people who want to edit this file (not just use it as a library):12 *13 * Most of the interesting stuff happens in the derived classes of IdlObject,14 * especially IdlInterface. The entry point for all IdlObjects is .test(),15 * which is called by IdlArray.test(). An IdlObject is conceptually just16 * "thing we want to run tests on", and an IdlArray is an array of IdlObjects17 * with some additional data thrown in.18 *19 * The object model is based on what WebIDLParser.js produces, which is in turn20 * based on its pegjs grammar. If you want to figure out what properties an21 * object will have from WebIDLParser.js, the best way is to look at the22 * grammar:23 *24 * https://github.com/darobin/webidl.js/blob/master/lib/grammar.peg25 *26 * So for instance:27 *28 * // interface definition29 * interface30 * = extAttrs:extendedAttributeList? S? "interface" S name:identifier w herit:ifInheritance? w "{" w mem:ifMember* w "}" w ";" w31 * { return { type: "interface", name: name, inheritance: herit, members: mem, extAttrs: extAttrs }; }32 *33 * This means that an "interface" object will have a .type property equal to34 * the string "interface", a .name property equal to the identifier that the35 * parser found, an .inheritance property equal to either null or the result of36 * the "ifInheritance" production found elsewhere in the grammar, and so on.37 * After each grammatical production is a JavaScript function in curly braces38 * that gets called with suitable arguments and returns some JavaScript value.39 *40 * (Note that the version of WebIDLParser.js we use might sometimes be41 * out-of-date or forked.)42 *43 * The members and methods of the classes defined by this file are all at least44 * briefly documented, hopefully.45 */46(function(){47"use strict";48// Support subsetTestByKey from /common/subset-tests-by-key.js, but make it optional49if (!('subsetTestByKey' in self)) {50 self.subsetTestByKey = function(key, callback, ...args) {51 return callback(...args);52 }53 self.shouldRunSubTest = () => true;54}55/// Helpers ///56function constValue (cnt)57{58 if (cnt.type === "null") return null;59 if (cnt.type === "NaN") return NaN;60 if (cnt.type === "Infinity") return cnt.negative ? -Infinity : Infinity;61 if (cnt.type === "number") return +cnt.value;62 return cnt.value;63}64function minOverloadLength(overloads)65{66 // "The value of the Function objectâs âlengthâ property is67 // a Number determined as follows:68 // ". . .69 // "Return the length of the shortest argument list of the70 // entries in S."71 if (!overloads.length) {72 return 0;73 }74 return overloads.map(function(attr) {75 return attr.arguments ? attr.arguments.filter(function(arg) {76 return !arg.optional && !arg.variadic;77 }).length : 0;78 })79 .reduce(function(m, n) { return Math.min(m, n); });80}81function throwOrReject(a_test, operation, fn, obj, args, message, cb)82{83 if (operation.idlType.generic !== "Promise") {84 assert_throws(new TypeError(), function() {85 fn.apply(obj, args);86 }, message);87 cb();88 } else {89 try {90 promise_rejects(a_test, new TypeError(), fn.apply(obj, args), message).then(cb, cb);91 } catch (e){92 a_test.step(function() {93 assert_unreached("Throws \"" + e + "\" instead of rejecting promise");94 cb();95 });96 }97 }98}99function awaitNCallbacks(n, cb, ctx)100{101 var counter = 0;102 return function() {103 counter++;104 if (counter >= n) {105 cb();106 }107 };108}109var fround =110(function(){111 if (Math.fround) return Math.fround;112 var arr = new Float32Array(1);113 return function fround(n) {114 arr[0] = n;115 return arr[0];116 };117})();118/// IdlHarnessError ///119// Entry point120self.IdlHarnessError = function(message)121{122 /**123 * Message to be printed as the error's toString invocation.124 */125 this.message = message;126};127IdlHarnessError.prototype = Object.create(Error.prototype);128IdlHarnessError.prototype.toString = function()129{130 return this.message;131};132/// IdlArray ///133// Entry point134self.IdlArray = function()135{136 /**137 * A map from strings to the corresponding named IdlObject, such as138 * IdlInterface or IdlException. These are the things that test() will run139 * tests on.140 */141 this.members = {};142 /**143 * A map from strings to arrays of strings. The keys are interface or144 * exception names, and are expected to also exist as keys in this.members145 * (otherwise they'll be ignored). This is populated by add_objects() --146 * see documentation at the start of the file. The actual tests will be147 * run by calling this.members[name].test_object(obj) for each obj in148 * this.objects[name]. obj is a string that will be eval'd to produce a149 * JavaScript value, which is supposed to be an object implementing the150 * given IdlObject (interface, exception, etc.).151 */152 this.objects = {};153 /**154 * When adding multiple collections of IDLs one at a time, an earlier one155 * might contain a partial interface or implements statement that depends156 * on a later one. Save these up and handle them right before we run157 * tests.158 *159 * .partials is simply an array of objects from WebIDLParser.js'160 * "partialinterface" production. .implements maps strings to arrays of161 * strings, such that162 *163 * A implements B;164 * A implements C;165 * D implements E;166 *167 * results in this["implements"] = { A: ["B", "C"], D: ["E"] }.168 *169 * Similarly,170 *171 * interface A : B {};172 * interface B : C {};173 *174 * results in this["inheritance"] = { A: "B", B: "C" }175 */176 this.partials = [];177 this["implements"] = {};178 this["includes"] = {};179 this["inheritance"] = {};180 /**181 * Record of skipped IDL items, in case we later realize that they are a182 * dependency (to retroactively process them).183 */184 this.skipped = new Map();185};186IdlArray.prototype.add_idls = function(raw_idls, options)187{188 /** Entry point. See documentation at beginning of file. */189 this.internal_add_idls(WebIDL2.parse(raw_idls), options);190};191IdlArray.prototype.add_untested_idls = function(raw_idls, options)192{193 /** Entry point. See documentation at beginning of file. */194 var parsed_idls = WebIDL2.parse(raw_idls);195 this.mark_as_untested(parsed_idls);196 this.internal_add_idls(parsed_idls, options);197};198IdlArray.prototype.mark_as_untested = function (parsed_idls)199{200 for (var i = 0; i < parsed_idls.length; i++) {201 parsed_idls[i].untested = true;202 if ("members" in parsed_idls[i]) {203 for (var j = 0; j < parsed_idls[i].members.length; j++) {204 parsed_idls[i].members[j].untested = true;205 }206 }207 }208};209IdlArray.prototype.is_excluded_by_options = function (name, options)210{211 return options &&212 (options.except && options.except.includes(name)213 || options.only && !options.only.includes(name));214};215IdlArray.prototype.add_dependency_idls = function(raw_idls, options)216{217 const parsed_idls = WebIDL2.parse(raw_idls);218 const new_options = { only: [] }219 const all_deps = new Set();220 Object.values(this.inheritance).forEach(v => all_deps.add(v));221 Object.entries(this.implements).forEach(([k, v]) => {222 all_deps.add(k);223 all_deps.add(v);224 });225 // NOTE: If 'A includes B' for B that we care about, then A is also a dep.226 Object.keys(this.includes).forEach(k => {227 all_deps.add(k);228 this.includes[k].forEach(v => all_deps.add(v));229 });230 this.partials.forEach(p => all_deps.add(p.name));231 // Add the attribute idlTypes of all the nested members of idls.232 const attrDeps = parsedIdls => {233 return parsedIdls.reduce((deps, parsed) => {234 if (parsed.members) {235 for (const attr of Object.values(parsed.members).filter(m => m.type === 'attribute')) {236 let attrType = attr.idlType;237 // Check for generic members (e.g. FrozenArray<MyType>)238 if (attrType.generic) {239 deps.add(attrType.generic);240 attrType = attrType.idlType;241 }242 deps.add(attrType.idlType);243 }244 }245 if (parsed.base in this.members) {246 attrDeps([this.members[parsed.base]]).forEach(dep => deps.add(dep));247 }248 return deps;249 }, new Set());250 };251 const testedMembers = Object.values(this.members).filter(m => !m.untested && m.members);252 attrDeps(testedMembers).forEach(dep => all_deps.add(dep));253 const testedPartials = this.partials.filter(m => !m.untested && m.members);254 attrDeps(testedPartials).forEach(dep => all_deps.add(dep));255 if (options && options.except && options.only) {256 throw new IdlHarnessError("The only and except options can't be used together.");257 }258 const defined_or_untested = name => {259 // NOTE: Deps are untested, so we're lenient, and skip re-encountered definitions.260 // e.g. for 'idl' containing A:B, B:C, C:D261 // array.add_idls(idl, {only: ['A','B']}).262 // array.add_dependency_idls(idl);263 // B would be encountered as tested, and encountered as a dep, so we ignore.264 return name in this.members265 || this.is_excluded_by_options(name, options);266 }267 // Maps name -> [parsed_idl, ...]268 const process = function(parsed) {269 var deps = [];270 if (parsed.name) {271 deps.push(parsed.name);272 } else if (parsed.type === "implements") {273 deps.push(parsed.target);274 deps.push(parsed.implements);275 } else if (parsed.type === "includes") {276 deps.push(parsed.target);277 deps.push(parsed.includes);278 }279 deps = deps.filter(function(name) {280 if (!name281 || name === parsed.name && defined_or_untested(name)282 || !all_deps.has(name)) {283 // Flag as skipped, if it's not already processed, so we can284 // come back to it later if we retrospectively call it a dep.285 if (name && !(name in this.members)) {286 this.skipped.has(name)287 ? this.skipped.get(name).push(parsed)288 : this.skipped.set(name, [parsed]);289 }290 return false;291 }292 return true;293 }.bind(this));294 deps.forEach(function(name) {295 if (!new_options.only.includes(name)) {296 new_options.only.push(name);297 }298 const follow_up = new Set();299 for (const dep_type of ["inheritance", "implements", "includes"]) {300 if (parsed[dep_type]) {301 const inheriting = parsed[dep_type];302 const inheritor = parsed.name || parsed.target;303 const deps = [inheriting];304 // For A includes B, we can ignore A, unless B (or some of its305 // members) is being tested.306 if (dep_type !== "includes"307 || inheriting in this.members && !this.members[inheriting].untested308 || this.partials.some(function(p) {309 return p.name === inheriting;310 })) {311 deps.push(inheritor);312 }313 for (const dep of deps) {314 if (!new_options.only.includes(dep)) {315 new_options.only.push(dep);316 }317 all_deps.add(dep);318 follow_up.add(dep);319 }320 }321 }322 for (const deferred of follow_up) {323 if (this.skipped.has(deferred)) {324 const next = this.skipped.get(deferred);325 this.skipped.delete(deferred);326 next.forEach(process);327 }328 }329 }.bind(this));330 }.bind(this);331 for (let parsed of parsed_idls) {332 process(parsed);333 }334 this.mark_as_untested(parsed_idls);335 if (new_options.only.length) {336 this.internal_add_idls(parsed_idls, new_options);337 }338}339IdlArray.prototype.internal_add_idls = function(parsed_idls, options)340{341 /**342 * Internal helper called by add_idls() and add_untested_idls().343 *344 * parsed_idls is an array of objects that come from WebIDLParser.js's345 * "definitions" production. The add_untested_idls() entry point346 * additionally sets an .untested property on each object (and its347 * .members) so that they'll be skipped by test() -- they'll only be348 * used for base interfaces of tested interfaces, return types, etc.349 *350 * options is a dictionary that can have an only or except member which are351 * arrays. If only is given then only members, partials and interface352 * targets listed will be added, and if except is given only those that353 * aren't listed will be added. Only one of only and except can be used.354 */355 if (options && options.only && options.except)356 {357 throw new IdlHarnessError("The only and except options can't be used together.");358 }359 var should_skip = name => {360 return this.is_excluded_by_options(name, options);361 }362 parsed_idls.forEach(function(parsed_idl)363 {364 var partial_types = [365 "interface",366 "interface mixin",367 "dictionary",368 "namespace",369 ];370 if (parsed_idl.partial && partial_types.includes(parsed_idl.type))371 {372 if (should_skip(parsed_idl.name))373 {374 return;375 }376 this.partials.push(parsed_idl);377 return;378 }379 if (parsed_idl.type == "implements")380 {381 if (should_skip(parsed_idl.target))382 {383 return;384 }385 if (!(parsed_idl.target in this["implements"]))386 {387 this["implements"][parsed_idl.target] = [];388 }389 this["implements"][parsed_idl.target].push(parsed_idl["implements"]);390 return;391 }392 if (parsed_idl.type == "includes")393 {394 if (should_skip(parsed_idl.target))395 {396 return;397 }398 if (!(parsed_idl.target in this["includes"]))399 {400 this["includes"][parsed_idl.target] = [];401 }402 this["includes"][parsed_idl.target].push(parsed_idl["includes"]);403 return;404 }405 parsed_idl.array = this;406 if (should_skip(parsed_idl.name))407 {408 return;409 }410 if (parsed_idl.name in this.members)411 {412 throw new IdlHarnessError("Duplicate identifier " + parsed_idl.name);413 }414 if (parsed_idl["inheritance"]) {415 // NOTE: Clash should be impossible (would require redefinition of parsed_idl.name).416 if (parsed_idl.name in this["inheritance"]417 && parsed_idl["inheritance"] != this["inheritance"][parsed_idl.name]) {418 throw new IdlHarnessError(419 `Inheritance for ${parsed_idl.name} was already defined`);420 }421 this["inheritance"][parsed_idl.name] = parsed_idl["inheritance"];422 }423 switch(parsed_idl.type)424 {425 case "interface":426 this.members[parsed_idl.name] =427 new IdlInterface(parsed_idl, /* is_callback = */ false, /* is_mixin = */ false);428 break;429 case "interface mixin":430 this.members[parsed_idl.name] =431 new IdlInterface(parsed_idl, /* is_callback = */ false, /* is_mixin = */ true);432 break;433 case "dictionary":434 // Nothing to test, but we need the dictionary info around for type435 // checks436 this.members[parsed_idl.name] = new IdlDictionary(parsed_idl);437 break;438 case "typedef":439 this.members[parsed_idl.name] = new IdlTypedef(parsed_idl);440 break;441 case "callback":442 // TODO443 console.log("callback not yet supported");444 break;445 case "enum":446 this.members[parsed_idl.name] = new IdlEnum(parsed_idl);447 break;448 case "callback interface":449 this.members[parsed_idl.name] =450 new IdlInterface(parsed_idl, /* is_callback = */ true, /* is_mixin = */ false);451 break;452 case "namespace":453 this.members[parsed_idl.name] = new IdlNamespace(parsed_idl);454 break;455 default:456 throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported";457 }458 }.bind(this));459};460IdlArray.prototype.add_objects = function(dict)461{462 /** Entry point. See documentation at beginning of file. */463 for (var k in dict)464 {465 if (k in this.objects)466 {467 this.objects[k] = this.objects[k].concat(dict[k]);468 }469 else470 {471 this.objects[k] = dict[k];472 }473 }474};475IdlArray.prototype.prevent_multiple_testing = function(name)476{477 /** Entry point. See documentation at beginning of file. */478 this.members[name].prevent_multiple_testing = true;479};480IdlArray.prototype.recursively_get_implements = function(interface_name)481{482 /**483 * Helper function for test(). Returns an array of things that implement484 * interface_name, so if the IDL contains485 *486 * A implements B;487 * B implements C;488 * B implements D;489 *490 * then recursively_get_implements("A") should return ["B", "C", "D"].491 */492 var ret = this["implements"][interface_name];493 if (ret === undefined)494 {495 return [];496 }497 for (var i = 0; i < this["implements"][interface_name].length; i++)498 {499 ret = ret.concat(this.recursively_get_implements(ret[i]));500 if (ret.indexOf(ret[i]) != ret.lastIndexOf(ret[i]))501 {502 throw new IdlHarnessError("Circular implements statements involving " + ret[i]);503 }504 }505 return ret;506};507IdlArray.prototype.recursively_get_includes = function(interface_name)508{509 /**510 * Helper function for test(). Returns an array of things that implement511 * interface_name, so if the IDL contains512 *513 * A includes B;514 * B includes C;515 * B includes D;516 *517 * then recursively_get_includes("A") should return ["B", "C", "D"].518 */519 var ret = this["includes"][interface_name];520 if (ret === undefined)521 {522 return [];523 }524 for (var i = 0; i < this["includes"][interface_name].length; i++)525 {526 ret = ret.concat(this.recursively_get_includes(ret[i]));527 if (ret.indexOf(ret[i]) != ret.lastIndexOf(ret[i]))528 {529 throw new IdlHarnessError("Circular includes statements involving " + ret[i]);530 }531 }532 return ret;533};534IdlArray.prototype.is_json_type = function(type)535{536 /**537 * Checks whether type is a JSON type as per538 * https://heycam.github.io/webidl/#dfn-json-types539 */540 var idlType = type.idlType;541 if (type.generic == "Promise") { return false; }542 // nullable and annotated types don't need to be handled separately,543 // as webidl2 doesn't represent them wrapped-up (as they're described544 // in WebIDL).545 // union and record types546 if (type.union || type.generic == "record") {547 return idlType.every(this.is_json_type, this);548 }549 // sequence types550 if (type.generic == "sequence" || type.generic == "FrozenArray") {551 return this.is_json_type(idlType[0]);552 }553 if (typeof idlType != "string") { throw new Error("Unexpected type " + JSON.stringify(idlType)); }554 switch (idlType)555 {556 // Numeric types557 case "byte":558 case "octet":559 case "short":560 case "unsigned short":561 case "long":562 case "unsigned long":563 case "long long":564 case "unsigned long long":565 case "float":566 case "double":567 case "unrestricted float":568 case "unrestricted double":569 // boolean570 case "boolean":571 // string types572 case "DOMString":573 case "ByteString":574 case "USVString":575 // object type576 case "object":577 return true;578 case "Error":579 case "DOMException":580 case "Int8Array":581 case "Int16Array":582 case "Int32Array":583 case "Uint8Array":584 case "Uint16Array":585 case "Uint32Array":586 case "Uint8ClampedArray":587 case "Float32Array":588 case "ArrayBuffer":589 case "DataView":590 case "any":591 return false;592 default:593 var thing = this.members[idlType];594 if (!thing) { throw new Error("Type " + idlType + " not found"); }595 if (thing instanceof IdlEnum) { return true; }596 if (thing instanceof IdlTypedef) {597 return this.is_json_type(thing.idlType);598 }599 // dictionaries where all of their members are JSON types600 if (thing instanceof IdlDictionary) {601 var stack = thing.get_inheritance_stack();602 var map = new Map();603 while (stack.length)604 {605 stack.pop().members.forEach(function(m) {606 map.set(m.name, m.idlType)607 });608 }609 return Array.from(map.values()).every(this.is_json_type, this);610 }611 // interface types that have a toJSON operation declared on themselves or612 // one of their inherited or consequential interfaces.613 if (thing instanceof IdlInterface) {614 var base;615 while (thing)616 {617 if (thing.has_to_json_regular_operation()) { return true; }618 var mixins = this.implements[thing.name] || this.includes[thing.name];619 if (mixins) {620 mixins = mixins.map(function(id) {621 var mixin = this.members[id];622 if (!mixin) {623 throw new Error("Interface " + id + " not found (implemented by " + thing.name + ")");624 }625 return mixin;626 }, this);627 if (mixins.some(function(m) { return m.has_to_json_regular_operation() } )) { return true; }628 }629 if (!thing.base) { return false; }630 base = this.members[thing.base];631 if (!base) {632 throw new Error("Interface " + thing.base + " not found (inherited by " + thing.name + ")");633 }634 thing = base;635 }636 return false;637 }638 return false;639 }640};641function exposure_set(object, default_set) {642 var exposed = object.extAttrs && object.extAttrs.filter(a => a.name === "Exposed");643 if (exposed && exposed.length > 1) {644 throw new IdlHarnessError(645 `Multiple 'Exposed' extended attributes on ${object.name}`);646 }647 let result = default_set || ["Window"];648 if (result && !(result instanceof Set)) {649 result = new Set(result);650 }651 if (exposed && exposed.length) {652 const { rhs } = exposed[0];653 // Could be a list or a string.654 const set = rhs.type === "identifier-list" ?655 rhs.value.map(id => id.value) :656 [ rhs.value ];657 result = new Set(set);658 }659 if (result && result.has("Worker")) {660 result.delete("Worker");661 result.add("DedicatedWorker");662 result.add("ServiceWorker");663 result.add("SharedWorker");664 }665 return result;666}667function exposed_in(globals) {668 if ('document' in self) {669 return globals.has("Window");670 }671 if ('DedicatedWorkerGlobalScope' in self &&672 self instanceof DedicatedWorkerGlobalScope) {673 return globals.has("DedicatedWorker");674 }675 if ('SharedWorkerGlobalScope' in self &&676 self instanceof SharedWorkerGlobalScope) {677 return globals.has("SharedWorker");678 }679 if ('ServiceWorkerGlobalScope' in self &&680 self instanceof ServiceWorkerGlobalScope) {681 return globals.has("ServiceWorker");682 }683 throw new IdlHarnessError("Unexpected global object");684}685/**686 * Asserts that the given error message is thrown for the given function.687 * @param {string|IdlHarnessError} error Expected Error message.688 * @param {Function} idlArrayFunc Function operating on an IdlArray that should throw.689 */690IdlArray.prototype.assert_throws = function(error, idlArrayFunc)691{692 try {693 idlArrayFunc.call(this, this);694 } catch (e) {695 if (e instanceof AssertionError) {696 throw e;697 }698 // Assertions for behaviour of the idlharness.js engine.699 if (error instanceof IdlHarnessError) {700 error = error.message;701 }702 if (e.message !== error) {703 throw new IdlHarnessError(`${idlArrayFunc} threw "${e}", not the expected IdlHarnessError "${error}"`);704 }705 return;706 }707 throw new IdlHarnessError(`${idlArrayFunc} did not throw the expected IdlHarnessError`);708}709IdlArray.prototype.test = function()710{711 /** Entry point. See documentation at beginning of file. */712 // First merge in all the partial interfaces and implements statements we713 // encountered.714 this.collapse_partials();715 for (var lhs in this["implements"])716 {717 this.recursively_get_implements(lhs).forEach(function(rhs)718 {719 var errStr = lhs + " implements " + rhs + ", but ";720 if (!(lhs in this.members)) throw errStr + lhs + " is undefined.";721 if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface.";722 if (!(rhs in this.members)) throw errStr + rhs + " is undefined.";723 if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface.";724 this.members[rhs].members.forEach(function(member)725 {726 this.members[lhs].members.push(new IdlInterfaceMember(member));727 }.bind(this));728 }.bind(this));729 }730 this["implements"] = {};731 for (var lhs in this["includes"])732 {733 this.recursively_get_includes(lhs).forEach(function(rhs)734 {735 var errStr = lhs + " includes " + rhs + ", but ";736 if (!(lhs in this.members)) throw errStr + lhs + " is undefined.";737 if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface.";738 if (!(rhs in this.members)) throw errStr + rhs + " is undefined.";739 if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface.";740 this.members[rhs].members.forEach(function(member)741 {742 this.members[lhs].members.push(new IdlInterfaceMember(member));743 }.bind(this));744 }.bind(this));745 }746 this["includes"] = {};747 // Assert B defined for A : B748 for (const member of Object.values(this.members).filter(m => m.base)) {749 const lhs = member.name;750 const rhs = member.base;751 if (!(rhs in this.members)) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${rhs} is undefined.`);752 const lhs_is_interface = this.members[lhs] instanceof IdlInterface;753 const rhs_is_interface = this.members[rhs] instanceof IdlInterface;754 if (rhs_is_interface != lhs_is_interface) {755 if (!lhs_is_interface) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${lhs} is not an interface.`);756 if (!rhs_is_interface) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${rhs} is not an interface.`);757 }758 // Check for circular dependencies.759 member.get_inheritance_stack();760 }761 Object.getOwnPropertyNames(this.members).forEach(function(memberName) {762 var member = this.members[memberName];763 if (!(member instanceof IdlInterface)) {764 return;765 }766 var globals = exposure_set(member);767 member.exposed = exposed_in(globals);768 member.exposureSet = globals;769 }.bind(this));770 // Now run test() on every member, and test_object() for every object.771 for (var name in this.members)772 {773 this.members[name].test();774 if (name in this.objects)775 {776 const objects = this.objects[name];777 if (!objects || !Array.isArray(objects)) {778 throw new IdlHarnessError(`Invalid or empty objects for member ${name}`);779 }780 objects.forEach(function(str)781 {782 if (!this.members[name] || !(this.members[name] instanceof IdlInterface)) {783 throw new IdlHarnessError(`Invalid object member name ${name}`);784 }785 this.members[name].test_object(str);786 }.bind(this));787 }788 }789};790IdlArray.prototype.collapse_partials = function()791{792 const testedPartials = new Map();793 this.partials.forEach(function(parsed_idl)794 {795 const originalExists = parsed_idl.name in this.members796 && (this.members[parsed_idl.name] instanceof IdlInterface797 || this.members[parsed_idl.name] instanceof IdlDictionary798 || this.members[parsed_idl.name] instanceof IdlNamespace);799 let partialTestName = parsed_idl.name;800 if (!parsed_idl.untested) {801 // Ensure unique test name in case of multiple partials.802 let partialTestCount = 1;803 if (testedPartials.has(parsed_idl.name)) {804 partialTestCount += testedPartials.get(parsed_idl.name);805 partialTestName = `${partialTestName}[${partialTestCount}]`;806 }807 testedPartials.set(parsed_idl.name, partialTestCount);808 test(function () {809 assert_true(originalExists, `Original ${parsed_idl.type} should be defined`);810 var expected = IdlInterface;811 switch (parsed_idl.type) {812 case 'interface': expected = IdlInterface; break;813 case 'dictionary': expected = IdlDictionary; break;814 case 'namespace': expected = IdlNamespace; break;815 }816 assert_true(817 expected.prototype.isPrototypeOf(this.members[parsed_idl.name]),818 `Original ${parsed_idl.name} definition should have type ${parsed_idl.type}`);819 }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: original ${parsed_idl.type} defined`);820 }821 if (!originalExists) {822 // Not good.. but keep calm and carry on.823 return;824 }825 if (parsed_idl.extAttrs)826 {827 // Special-case "Exposed". Must be a subset of original interface's exposure.828 // Exposed on a partial is the equivalent of having the same Exposed on all nested members.829 // See https://github.com/heycam/webidl/issues/154 for discrepency between Exposed and830 // other extended attributes on partial interfaces.831 const exposureAttr = parsed_idl.extAttrs.find(a => a.name === "Exposed");832 if (exposureAttr) {833 if (!parsed_idl.untested) {834 test(function () {835 const partialExposure = exposure_set(parsed_idl);836 const memberExposure = exposure_set(this.members[parsed_idl.name]);837 partialExposure.forEach(name => {838 if (!memberExposure || !memberExposure.has(name)) {839 throw new IdlHarnessError(840 `Partial ${parsed_idl.name} ${parsed_idl.type} is exposed to '${name}', the original ${parsed_idl.type} is not.`);841 }842 });843 }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: valid exposure set`);844 }845 parsed_idl.members.forEach(function (member) {846 member.extAttrs.push(exposureAttr);847 }.bind(this));848 }849 parsed_idl.extAttrs.forEach(function(extAttr)850 {851 // "Exposed" already handled above.852 if (extAttr.name === "Exposed") {853 return;854 }855 this.members[parsed_idl.name].extAttrs.push(extAttr);856 }.bind(this));857 }858 parsed_idl.members.forEach(function(member)859 {860 this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member));861 }.bind(this));862 }.bind(this));863 this.partials = [];864}865IdlArray.prototype.assert_type_is = function(value, type)866{867 if (type.idlType in this.members868 && this.members[type.idlType] instanceof IdlTypedef) {869 this.assert_type_is(value, this.members[type.idlType].idlType);870 return;871 }872 if (type.union) {873 for (var i = 0; i < type.idlType.length; i++) {874 try {875 this.assert_type_is(value, type.idlType[i]);876 // No AssertionError, so we match one type in the union877 return;878 } catch(e) {879 if (e instanceof AssertionError) {880 // We didn't match this type, let's try some others881 continue;882 }883 throw e;884 }885 }886 // TODO: Is there a nice way to list the union's types in the message?887 assert_true(false, "Attribute has value " + format_value(value)888 + " which doesn't match any of the types in the union");889 }890 /**891 * Helper function that tests that value is an instance of type according892 * to the rules of WebIDL. value is any JavaScript value, and type is an893 * object produced by WebIDLParser.js' "type" production. That production894 * is fairly elaborate due to the complexity of WebIDL's types, so it's895 * best to look at the grammar to figure out what properties it might have.896 */897 if (type.idlType == "any")898 {899 // No assertions to make900 return;901 }902 if (type.nullable && value === null)903 {904 // This is fine905 return;906 }907 if (type.array)908 {909 // TODO: not supported yet910 return;911 }912 if (type.generic === "sequence")913 {914 assert_true(Array.isArray(value), "should be an Array");915 if (!value.length)916 {917 // Nothing we can do.918 return;919 }920 this.assert_type_is(value[0], type.idlType[0]);921 return;922 }923 if (type.generic === "Promise") {924 assert_true("then" in value, "Attribute with a Promise type should have a then property");925 // TODO: Ideally, we would check on project fulfillment926 // that we get the right type927 // but that would require making the type check async928 return;929 }930 if (type.generic === "FrozenArray") {931 assert_true(Array.isArray(value), "Value should be array");932 assert_true(Object.isFrozen(value), "Value should be frozen");933 if (!value.length)934 {935 // Nothing we can do.936 return;937 }938 this.assert_type_is(value[0], type.idlType[0]);939 return;940 }941 type = Array.isArray(type.idlType) ? type.idlType[0] : type.idlType;942 switch(type)943 {944 case "void":945 assert_equals(value, undefined);946 return;947 case "boolean":948 assert_equals(typeof value, "boolean");949 return;950 case "byte":951 assert_equals(typeof value, "number");952 assert_equals(value, Math.floor(value), "should be an integer");953 assert_true(-128 <= value && value <= 127, "byte " + value + " should be in range [-128, 127]");954 return;955 case "octet":956 assert_equals(typeof value, "number");957 assert_equals(value, Math.floor(value), "should be an integer");958 assert_true(0 <= value && value <= 255, "octet " + value + " should be in range [0, 255]");959 return;960 case "short":961 assert_equals(typeof value, "number");962 assert_equals(value, Math.floor(value), "should be an integer");963 assert_true(-32768 <= value && value <= 32767, "short " + value + " should be in range [-32768, 32767]");964 return;965 case "unsigned short":966 assert_equals(typeof value, "number");967 assert_equals(value, Math.floor(value), "should be an integer");968 assert_true(0 <= value && value <= 65535, "unsigned short " + value + " should be in range [0, 65535]");969 return;970 case "long":971 assert_equals(typeof value, "number");972 assert_equals(value, Math.floor(value), "should be an integer");973 assert_true(-2147483648 <= value && value <= 2147483647, "long " + value + " should be in range [-2147483648, 2147483647]");974 return;975 case "unsigned long":976 assert_equals(typeof value, "number");977 assert_equals(value, Math.floor(value), "should be an integer");978 assert_true(0 <= value && value <= 4294967295, "unsigned long " + value + " should be in range [0, 4294967295]");979 return;980 case "long long":981 assert_equals(typeof value, "number");982 return;983 case "unsigned long long":984 case "DOMTimeStamp":985 assert_equals(typeof value, "number");986 assert_true(0 <= value, "unsigned long long should be positive");987 return;988 case "float":989 assert_equals(typeof value, "number");990 assert_equals(value, fround(value), "float rounded to 32-bit float should be itself");991 assert_not_equals(value, Infinity);992 assert_not_equals(value, -Infinity);993 assert_not_equals(value, NaN);994 return;995 case "DOMHighResTimeStamp":996 case "double":997 assert_equals(typeof value, "number");998 assert_not_equals(value, Infinity);999 assert_not_equals(value, -Infinity);1000 assert_not_equals(value, NaN);1001 return;1002 case "unrestricted float":1003 assert_equals(typeof value, "number");1004 assert_equals(value, fround(value), "unrestricted float rounded to 32-bit float should be itself");1005 return;1006 case "unrestricted double":1007 assert_equals(typeof value, "number");1008 return;1009 case "DOMString":1010 assert_equals(typeof value, "string");1011 return;1012 case "ByteString":1013 assert_equals(typeof value, "string");1014 assert_regexp_match(value, /^[\x00-\x7F]*$/);1015 return;1016 case "USVString":1017 assert_equals(typeof value, "string");1018 assert_regexp_match(value, /^([\x00-\ud7ff\ue000-\uffff]|[\ud800-\udbff][\udc00-\udfff])*$/);1019 return;1020 case "object":1021 assert_in_array(typeof value, ["object", "function"], "wrong type: not object or function");1022 return;1023 }1024 if (!(type in this.members))1025 {1026 throw new IdlHarnessError("Unrecognized type " + type);1027 }1028 if (this.members[type] instanceof IdlInterface)1029 {1030 // We don't want to run the full1031 // IdlInterface.prototype.test_instance_of, because that could result1032 // in an infinite loop. TODO: This means we don't have tests for1033 // NoInterfaceObject interfaces, and we also can't test objects that1034 // come from another self.1035 assert_in_array(typeof value, ["object", "function"], "wrong type: not object or function");1036 if (value instanceof Object1037 && !this.members[type].has_extended_attribute("NoInterfaceObject")1038 && type in self)1039 {1040 assert_true(value instanceof self[type], "instanceof " + type);1041 }1042 }1043 else if (this.members[type] instanceof IdlEnum)1044 {1045 assert_equals(typeof value, "string");1046 }1047 else if (this.members[type] instanceof IdlDictionary)1048 {1049 // TODO: Test when we actually have something to test this on1050 }1051 else1052 {1053 throw new IdlHarnessError("Type " + type + " isn't an interface or dictionary");1054 }1055};1056/// IdlObject ///1057function IdlObject() {}1058IdlObject.prototype.test = function()1059{1060 /**1061 * By default, this does nothing, so no actual tests are run for IdlObjects1062 * that don't define any (e.g., IdlDictionary at the time of this writing).1063 */1064};1065IdlObject.prototype.has_extended_attribute = function(name)1066{1067 /**1068 * This is only meaningful for things that support extended attributes,1069 * such as interfaces, exceptions, and members.1070 */1071 return this.extAttrs.some(function(o)1072 {1073 return o.name == name;1074 });1075};1076/// IdlDictionary ///1077// Used for IdlArray.prototype.assert_type_is1078function IdlDictionary(obj)1079{1080 /**1081 * obj is an object produced by the WebIDLParser.js "dictionary"1082 * production.1083 */1084 /** Self-explanatory. */1085 this.name = obj.name;1086 /** A back-reference to our IdlArray. */1087 this.array = obj.array;1088 /** An array of objects produced by the "dictionaryMember" production. */1089 this.members = obj.members;1090 /**1091 * The name (as a string) of the dictionary type we inherit from, or null1092 * if there is none.1093 */1094 this.base = obj.inheritance;1095}1096IdlDictionary.prototype = Object.create(IdlObject.prototype);1097IdlDictionary.prototype.get_inheritance_stack = function() {1098 return IdlInterface.prototype.get_inheritance_stack.call(this);1099};1100/// IdlInterface ///1101function IdlInterface(obj, is_callback, is_mixin)1102{1103 /**1104 * obj is an object produced by the WebIDLParser.js "interface" production.1105 */1106 /** Self-explanatory. */1107 this.name = obj.name;1108 /** A back-reference to our IdlArray. */1109 this.array = obj.array;1110 /**1111 * An indicator of whether we should run tests on the interface object and1112 * interface prototype object. Tests on members are controlled by .untested1113 * on each member, not this.1114 */1115 this.untested = obj.untested;1116 /** An array of objects produced by the "ExtAttr" production. */1117 this.extAttrs = obj.extAttrs;1118 /** An array of IdlInterfaceMembers. */1119 this.members = obj.members.map(function(m){return new IdlInterfaceMember(m); });1120 if (this.has_extended_attribute("Unforgeable")) {1121 this.members1122 .filter(function(m) { return m.special !== "static" && (m.type == "attribute" || m.type == "operation"); })1123 .forEach(function(m) { return m.isUnforgeable = true; });1124 }1125 /**1126 * The name (as a string) of the type we inherit from, or null if there is1127 * none.1128 */1129 this.base = obj.inheritance;1130 this._is_callback = is_callback;1131 this._is_mixin = is_mixin;1132}1133IdlInterface.prototype = Object.create(IdlObject.prototype);1134IdlInterface.prototype.is_callback = function()1135{1136 return this._is_callback;1137};1138IdlInterface.prototype.is_mixin = function()1139{1140 return this._is_mixin;1141};1142IdlInterface.prototype.has_constants = function()1143{1144 return this.members.some(function(member) {1145 return member.type === "const";1146 });1147};1148IdlInterface.prototype.get_unscopables = function()1149{1150 return this.members.filter(function(member) {1151 return member.isUnscopable;1152 });1153};1154IdlInterface.prototype.is_global = function()1155{1156 return this.extAttrs.some(function(attribute) {1157 return attribute.name === "Global";1158 });1159};1160/**1161 * Value of the LegacyNamespace extended attribute, if any.1162 *1163 * https://heycam.github.io/webidl/#LegacyNamespace1164 */1165IdlInterface.prototype.get_legacy_namespace = function()1166{1167 var legacyNamespace = this.extAttrs.find(function(attribute) {1168 return attribute.name === "LegacyNamespace";1169 });1170 return legacyNamespace ? legacyNamespace.rhs.value : undefined;1171};1172IdlInterface.prototype.get_interface_object_owner = function()1173{1174 var legacyNamespace = this.get_legacy_namespace();1175 return legacyNamespace ? self[legacyNamespace] : self;1176};1177IdlInterface.prototype.assert_interface_object_exists = function()1178{1179 var owner = this.get_legacy_namespace() || "self";1180 assert_own_property(self[owner], this.name, owner + " does not have own property " + format_value(this.name));1181};1182IdlInterface.prototype.get_interface_object = function() {1183 if (this.has_extended_attribute("NoInterfaceObject")) {1184 throw new IdlHarnessError(this.name + " has no interface object due to NoInterfaceObject");1185 }1186 return this.get_interface_object_owner()[this.name];1187};1188IdlInterface.prototype.get_qualified_name = function() {1189 // https://heycam.github.io/webidl/#qualified-name1190 var legacyNamespace = this.get_legacy_namespace();1191 if (legacyNamespace) {1192 return legacyNamespace + "." + this.name;1193 }1194 return this.name;1195};1196IdlInterface.prototype.has_to_json_regular_operation = function() {1197 return this.members.some(function(m) {1198 return m.is_to_json_regular_operation();1199 });1200};1201IdlInterface.prototype.has_default_to_json_regular_operation = function() {1202 return this.members.some(function(m) {1203 return m.is_to_json_regular_operation() && m.has_extended_attribute("Default");1204 });1205};1206IdlInterface.prototype.get_inheritance_stack = function() {1207 /**1208 * See https://heycam.github.io/webidl/#create-an-inheritance-stack1209 *1210 * Returns an array of IdlInterface objects which contains itself1211 * and all of its inherited interfaces.1212 *1213 * So given:1214 *1215 * A : B {};1216 * B : C {};1217 * C {};1218 *1219 * then A.get_inheritance_stack() should return [A, B, C],1220 * and B.get_inheritance_stack() should return [B, C].1221 *1222 * Note: as dictionary inheritance is expressed identically by the AST,1223 * this works just as well for getting a stack of inherited dictionaries.1224 */1225 var stack = [this];1226 var idl_interface = this;1227 while (idl_interface.base) {1228 var base = this.array.members[idl_interface.base];1229 if (!base) {1230 throw new Error(idl_interface.type + " " + idl_interface.base + " not found (inherited by " + idl_interface.name + ")");1231 } else if (stack.indexOf(base) > -1) {1232 stack.push(base);1233 let dep_chain = stack.map(i => i.name).join(',');1234 throw new IdlHarnessError(`${this.name} has a circular dependency: ${dep_chain}`);1235 }1236 idl_interface = base;1237 stack.push(idl_interface);1238 }1239 return stack;1240};1241/**1242 * Implementation of1243 * https://heycam.github.io/webidl/#default-tojson-operation1244 * for testing purposes.1245 *1246 * Collects the IDL types of the attributes that meet the criteria1247 * for inclusion in the default toJSON operation for easy1248 * comparison with actual value1249 */1250IdlInterface.prototype.default_to_json_operation = function(callback) {1251 var map = new Map(), isDefault = false;1252 this.traverse_inherited_and_consequential_interfaces(function(I) {1253 if (I.has_default_to_json_regular_operation()) {1254 isDefault = true;1255 I.members.forEach(function(m) {1256 if (m.special !== "static" && m.type == "attribute" && I.array.is_json_type(m.idlType)) {1257 map.set(m.name, m.idlType);1258 }1259 });1260 } else if (I.has_to_json_regular_operation()) {1261 isDefault = false;1262 }1263 });1264 return isDefault ? map : null;1265};1266/**1267 * Traverses inherited interfaces from the top down1268 * and imeplemented interfaces inside out.1269 * Invokes |callback| on each interface.1270 *1271 * This is an abstract implementation of the traversal1272 * algorithm specified in:1273 * https://heycam.github.io/webidl/#collect-attribute-values1274 * Given the following inheritance tree:1275 *1276 * F1277 * |1278 * C E - I1279 * | |1280 * B - D1281 * |1282 * G - A - H - J1283 *1284 * Invoking traverse_inherited_and_consequential_interfaces() on A1285 * would traverse the tree in the following order:1286 * C -> B -> F -> E -> I -> D -> A -> G -> H -> J1287 */1288IdlInterface.prototype.traverse_inherited_and_consequential_interfaces = function(callback) {1289 if (typeof callback != "function") {1290 throw new TypeError();1291 }1292 var stack = this.get_inheritance_stack();1293 _traverse_inherited_and_consequential_interfaces(stack, callback);1294};1295function _traverse_inherited_and_consequential_interfaces(stack, callback) {1296 var I = stack.pop();1297 callback(I);1298 var mixins = I.array["implements"][I.name] || I.array["includes"][I.name];1299 if (mixins) {1300 mixins.forEach(function(id) {1301 var mixin = I.array.members[id];1302 if (!mixin) {1303 throw new Error("Interface " + id + " not found (implemented by " + I.name + ")");1304 }1305 var interfaces = mixin.get_inheritance_stack();1306 _traverse_inherited_and_consequential_interfaces(interfaces, callback);1307 });1308 }1309 if (stack.length > 0) {1310 _traverse_inherited_and_consequential_interfaces(stack, callback);1311 }1312}1313IdlInterface.prototype.test = function()1314{1315 if (this.has_extended_attribute("NoInterfaceObject") || this.is_mixin())1316 {1317 // No tests to do without an instance. TODO: We should still be able1318 // to run tests on the prototype object, if we obtain one through some1319 // other means.1320 return;1321 }1322 if (!this.exposed) {1323 subsetTestByKey(this.name, test, function() {1324 assert_false(this.name in self);1325 }.bind(this), this.name + " interface: existence and properties of interface object");1326 return;1327 }1328 if (!this.untested)1329 {1330 // First test things to do with the exception/interface object and1331 // exception/interface prototype object.1332 this.test_self();1333 }1334 // Then test things to do with its members (constants, fields, attributes,1335 // operations, . . .). These are run even if .untested is true, because1336 // members might themselves be marked as .untested. This might happen to1337 // interfaces if the interface itself is untested but a partial interface1338 // that extends it is tested -- then the interface itself and its initial1339 // members will be marked as untested, but the members added by the partial1340 // interface are still tested.1341 this.test_members();1342};1343IdlInterface.prototype.test_self = function()1344{1345 subsetTestByKey(this.name, test, function()1346 {1347 // This function tests WebIDL as of 2015-01-13.1348 // "For every interface that is exposed in a given ECMAScript global1349 // environment and:1350 // * is a callback interface that has constants declared on it, or1351 // * is a non-callback interface that is not declared with the1352 // [NoInterfaceObject] extended attribute,1353 // a corresponding property MUST exist on the ECMAScript global object.1354 // The name of the property is the identifier of the interface, and its1355 // value is an object called the interface object.1356 // The property has the attributes { [[Writable]]: true,1357 // [[Enumerable]]: false, [[Configurable]]: true }."1358 if (this.is_callback() && !this.has_constants()) {1359 return;1360 }1361 // TODO: Should we test here that the property is actually writable1362 // etc., or trust getOwnPropertyDescriptor?1363 this.assert_interface_object_exists();1364 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object_owner(), this.name);1365 assert_false("get" in desc, "self's property " + format_value(this.name) + " should not have a getter");1366 assert_false("set" in desc, "self's property " + format_value(this.name) + " should not have a setter");1367 assert_true(desc.writable, "self's property " + format_value(this.name) + " should be writable");1368 assert_false(desc.enumerable, "self's property " + format_value(this.name) + " should not be enumerable");1369 assert_true(desc.configurable, "self's property " + format_value(this.name) + " should be configurable");1370 if (this.is_callback()) {1371 // "The internal [[Prototype]] property of an interface object for1372 // a callback interface must be the Function.prototype object."1373 assert_equals(Object.getPrototypeOf(this.get_interface_object()), Function.prototype,1374 "prototype of self's property " + format_value(this.name) + " is not Object.prototype");1375 return;1376 }1377 // "The interface object for a given non-callback interface is a1378 // function object."1379 // "If an object is defined to be a function object, then it has1380 // characteristics as follows:"1381 // Its [[Prototype]] internal property is otherwise specified (see1382 // below).1383 // "* Its [[Get]] internal property is set as described in ECMA-2621384 // section 9.1.8."1385 // Not much to test for this.1386 // "* Its [[Construct]] internal property is set as described in1387 // ECMA-262 section 19.2.2.3."1388 // Tested below if no constructor is defined. TODO: test constructors1389 // if defined.1390 // "* Its @@hasInstance property is set as described in ECMA-2621391 // section 19.2.3.8, unless otherwise specified."1392 // TODO1393 // ES6 (rev 30) 19.1.3.6:1394 // "Else, if O has a [[Call]] internal method, then let builtinTag be1395 // "Function"."1396 assert_class_string(this.get_interface_object(), "Function", "class string of " + this.name);1397 // "The [[Prototype]] internal property of an interface object for a1398 // non-callback interface is determined as follows:"1399 var prototype = Object.getPrototypeOf(this.get_interface_object());1400 if (this.base) {1401 // "* If the interface inherits from some other interface, the1402 // value of [[Prototype]] is the interface object for that other1403 // interface."1404 var inherited_interface = this.array.members[this.base];1405 if (!inherited_interface.has_extended_attribute("NoInterfaceObject")) {1406 inherited_interface.assert_interface_object_exists();1407 assert_equals(prototype, inherited_interface.get_interface_object(),1408 'prototype of ' + this.name + ' is not ' +1409 this.base);1410 }1411 } else {1412 // "If the interface doesn't inherit from any other interface, the1413 // value of [[Prototype]] is %FunctionPrototype% ([ECMA-262],1414 // section 6.1.7.4)."1415 assert_equals(prototype, Function.prototype,1416 "prototype of self's property " + format_value(this.name) + " is not Function.prototype");1417 }1418 if (!this.has_extended_attribute("Constructor")) {1419 // "The internal [[Call]] method of the interface object behaves as1420 // follows . . .1421 //1422 // "If I was not declared with a [Constructor] extended attribute,1423 // then throw a TypeError."1424 var interface_object = this.get_interface_object();1425 assert_throws(new TypeError(), function() {1426 interface_object();1427 }, "interface object didn't throw TypeError when called as a function");1428 assert_throws(new TypeError(), function() {1429 new interface_object();1430 }, "interface object didn't throw TypeError when called as a constructor");1431 }1432 }.bind(this), this.name + " interface: existence and properties of interface object");1433 if (!this.is_callback()) {1434 subsetTestByKey(this.name, test, function() {1435 // This function tests WebIDL as of 2014-10-25.1436 // https://heycam.github.io/webidl/#es-interface-call1437 this.assert_interface_object_exists();1438 // "Interface objects for non-callback interfaces MUST have a1439 // property named âlengthâ with attributes { [[Writable]]: false,1440 // [[Enumerable]]: false, [[Configurable]]: true } whose value is1441 // a Number."1442 assert_own_property(this.get_interface_object(), "length");1443 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "length");1444 assert_false("get" in desc, this.name + ".length should not have a getter");1445 assert_false("set" in desc, this.name + ".length should not have a setter");1446 assert_false(desc.writable, this.name + ".length should not be writable");1447 assert_false(desc.enumerable, this.name + ".length should not be enumerable");1448 assert_true(desc.configurable, this.name + ".length should be configurable");1449 var constructors = this.extAttrs1450 .filter(function(attr) { return attr.name == "Constructor"; });1451 var expected_length = minOverloadLength(constructors);1452 assert_equals(this.get_interface_object().length, expected_length, "wrong value for " + this.name + ".length");1453 }.bind(this), this.name + " interface object length");1454 }1455 if (!this.is_callback() || this.has_constants()) {1456 subsetTestByKey(this.name, test, function() {1457 // This function tests WebIDL as of 2015-11-17.1458 // https://heycam.github.io/webidl/#interface-object1459 this.assert_interface_object_exists();1460 // "All interface objects must have a property named ânameâ with1461 // attributes { [[Writable]]: false, [[Enumerable]]: false,1462 // [[Configurable]]: true } whose value is the identifier of the1463 // corresponding interface."1464 assert_own_property(this.get_interface_object(), "name");1465 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "name");1466 assert_false("get" in desc, this.name + ".name should not have a getter");1467 assert_false("set" in desc, this.name + ".name should not have a setter");1468 assert_false(desc.writable, this.name + ".name should not be writable");1469 assert_false(desc.enumerable, this.name + ".name should not be enumerable");1470 assert_true(desc.configurable, this.name + ".name should be configurable");1471 assert_equals(this.get_interface_object().name, this.name, "wrong value for " + this.name + ".name");1472 }.bind(this), this.name + " interface object name");1473 }1474 if (this.has_extended_attribute("LegacyWindowAlias")) {1475 subsetTestByKey(this.name, test, function()1476 {1477 var aliasAttrs = this.extAttrs.filter(function(o) { return o.name === "LegacyWindowAlias"; });1478 if (aliasAttrs.length > 1) {1479 throw new IdlHarnessError("Invalid IDL: multiple LegacyWindowAlias extended attributes on " + this.name);1480 }1481 if (this.is_callback()) {1482 throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on non-interface " + this.name);1483 }1484 if (!this.exposureSet.has("Window")) {1485 throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " which is not exposed in Window");1486 }1487 // TODO: when testing of [NoInterfaceObject] interfaces is supported,1488 // check that it's not specified together with LegacyWindowAlias.1489 // TODO: maybe check that [LegacyWindowAlias] is not specified on a partial interface.1490 var rhs = aliasAttrs[0].rhs;1491 if (!rhs) {1492 throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " without identifier");1493 }1494 var aliases;1495 if (rhs.type === "identifier-list") {1496 aliases = rhs.value.map(id => id.value);1497 } else { // rhs.type === identifier1498 aliases = [ rhs.value ];1499 }1500 // OK now actually check the aliases...1501 var alias;1502 if (exposed_in(exposure_set(this, this.exposureSet)) && 'document' in self) {1503 for (alias of aliases) {1504 assert_true(alias in self, alias + " should exist");1505 assert_equals(self[alias], this.get_interface_object(), "self." + alias + " should be the same value as self." + this.get_qualified_name());1506 var desc = Object.getOwnPropertyDescriptor(self, alias);1507 assert_equals(desc.value, this.get_interface_object(), "wrong value in " + alias + " property descriptor");1508 assert_true(desc.writable, alias + " should be writable");1509 assert_false(desc.enumerable, alias + " should not be enumerable");1510 assert_true(desc.configurable, alias + " should be configurable");1511 assert_false('get' in desc, alias + " should not have a getter");1512 assert_false('set' in desc, alias + " should not have a setter");1513 }1514 } else {1515 for (alias of aliases) {1516 assert_false(alias in self, alias + " should not exist");1517 }1518 }1519 }.bind(this), this.name + " interface: legacy window alias");1520 }1521 if (this.has_extended_attribute("NamedConstructor")) {1522 var constructors = this.extAttrs1523 .filter(function(attr) { return attr.name == "NamedConstructor"; });1524 if (constructors.length !== 1) {1525 throw new IdlHarnessError("Internal error: missing support for multiple NamedConstructor extended attributes");1526 }1527 var constructor = constructors[0];1528 var min_length = minOverloadLength([constructor]);1529 subsetTestByKey(this.name, test, function()1530 {1531 // This function tests WebIDL as of 2019-01-14.1532 // "for every [NamedConstructor] extended attribute on an exposed1533 // interface, a corresponding property must exist on the ECMAScript1534 // global object. The name of the property is the1535 // [NamedConstructor]'s identifier, and its value is an object1536 // called a named constructor, ... . The property has the attributes1537 // { [[Writable]]: true, [[Enumerable]]: false,1538 // [[Configurable]]: true }."1539 var name = constructor.rhs.value;1540 assert_own_property(self, name);1541 var desc = Object.getOwnPropertyDescriptor(self, name);1542 assert_equals(desc.value, self[name], "wrong value in " + name + " property descriptor");1543 assert_true(desc.writable, name + " should be writable");1544 assert_false(desc.enumerable, name + " should not be enumerable");1545 assert_true(desc.configurable, name + " should be configurable");1546 assert_false("get" in desc, name + " should not have a getter");1547 assert_false("set" in desc, name + " should not have a setter");1548 }.bind(this), this.name + " interface: named constructor");1549 subsetTestByKey(this.name, test, function()1550 {1551 // This function tests WebIDL as of 2019-01-14.1552 // "2. Let F be ! CreateBuiltinFunction(realm, steps,1553 // realm.[[Intrinsics]].[[%FunctionPrototype%]])."1554 var name = constructor.rhs.value;1555 var value = self[name];1556 assert_equals(typeof value, "function", "type of value in " + name + " property descriptor");1557 assert_not_equals(value, this.get_interface_object(), "wrong value in " + name + " property descriptor");1558 assert_equals(Object.getPrototypeOf(value), Function.prototype, "wrong value for " + name + "'s prototype");1559 }.bind(this), this.name + " interface: named constructor object");1560 subsetTestByKey(this.name, test, function()1561 {1562 // This function tests WebIDL as of 2019-01-14.1563 // "7. Let proto be the interface prototype object of interface I1564 // in realm.1565 // "8. Perform ! DefinePropertyOrThrow(F, "prototype",1566 // PropertyDescriptor{1567 // [[Value]]: proto, [[Writable]]: false,1568 // [[Enumerable]]: false, [[Configurable]]: false1569 // })."1570 var name = constructor.rhs.value;1571 var expected = this.get_interface_object().prototype;1572 var desc = Object.getOwnPropertyDescriptor(self[name], "prototype");1573 assert_equals(desc.value, expected, "wrong value for " + name + ".prototype");1574 assert_false(desc.writable, "prototype should not be writable");1575 assert_false(desc.enumerable, "prototype should not be enumerable");1576 assert_false(desc.configurable, "prototype should not be configurable");1577 assert_false("get" in desc, "prototype should not have a getter");1578 assert_false("set" in desc, "prototype should not have a setter");1579 }.bind(this), this.name + " interface: named constructor prototype property");1580 subsetTestByKey(this.name, test, function()1581 {1582 // This function tests WebIDL as of 2019-01-14.1583 // "3. Perform ! SetFunctionName(F, id)."1584 var name = constructor.rhs.value;1585 var desc = Object.getOwnPropertyDescriptor(self[name], "name");1586 assert_equals(desc.value, name, "wrong value for " + name + ".name");1587 assert_false(desc.writable, "name should not be writable");1588 assert_false(desc.enumerable, "name should not be enumerable");1589 assert_true(desc.configurable, "name should be configurable");1590 assert_false("get" in desc, "name should not have a getter");1591 assert_false("set" in desc, "name should not have a setter");1592 }.bind(this), this.name + " interface: named constructor name");1593 subsetTestByKey(this.name, test, function()1594 {1595 // This function tests WebIDL as of 2019-01-14.1596 // "4. Initialize S to the effective overload set for constructors1597 // with identifier id on interface I and with argument count 0.1598 // "5. Let length be the length of the shortest argument list of1599 // the entries in S.1600 // "6. Perform ! SetFunctionLength(F, length)."1601 var name = constructor.rhs.value;1602 var desc = Object.getOwnPropertyDescriptor(self[name], "length");1603 assert_equals(desc.value, min_length, "wrong value for " + name + ".length");1604 assert_false(desc.writable, "length should not be writable");1605 assert_false(desc.enumerable, "length should not be enumerable");1606 assert_true(desc.configurable, "length should be configurable");1607 assert_false("get" in desc, "length should not have a getter");1608 assert_false("set" in desc, "length should not have a setter");1609 }.bind(this), this.name + " interface: named constructor length");1610 subsetTestByKey(this.name, test, function()1611 {1612 // This function tests WebIDL as of 2019-01-14.1613 // "1. Let steps be the following steps:1614 // " 1. If NewTarget is undefined, then throw a TypeError."1615 var name = constructor.rhs.value;1616 var args = constructor.arguments.map(function(arg) {1617 return create_suitable_object(arg.idlType);1618 });1619 assert_throws(new TypeError(), function() {1620 self[name](...args);1621 }.bind(this));1622 }.bind(this), this.name + " interface: named constructor without 'new'");1623 }1624 subsetTestByKey(this.name, test, function()1625 {1626 // This function tests WebIDL as of 2015-01-21.1627 // https://heycam.github.io/webidl/#interface-object1628 if (this.is_callback() && !this.has_constants()) {1629 return;1630 }1631 this.assert_interface_object_exists();1632 if (this.is_callback()) {1633 assert_false("prototype" in this.get_interface_object(),1634 this.name + ' should not have a "prototype" property');1635 return;1636 }1637 // "An interface object for a non-callback interface must have a1638 // property named âprototypeâ with attributes { [[Writable]]: false,1639 // [[Enumerable]]: false, [[Configurable]]: false } whose value is an1640 // object called the interface prototype object. This object has1641 // properties that correspond to the regular attributes and regular1642 // operations defined on the interface, and is described in more detail1643 // in section 4.5.4 below."1644 assert_own_property(this.get_interface_object(), "prototype",1645 'interface "' + this.name + '" does not have own property "prototype"');1646 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "prototype");1647 assert_false("get" in desc, this.name + ".prototype should not have a getter");1648 assert_false("set" in desc, this.name + ".prototype should not have a setter");1649 assert_false(desc.writable, this.name + ".prototype should not be writable");1650 assert_false(desc.enumerable, this.name + ".prototype should not be enumerable");1651 assert_false(desc.configurable, this.name + ".prototype should not be configurable");1652 // Next, test that the [[Prototype]] of the interface prototype object1653 // is correct. (This is made somewhat difficult by the existence of1654 // [NoInterfaceObject].)1655 // TODO: Aryeh thinks there's at least other place in this file where1656 // we try to figure out if an interface prototype object is1657 // correct. Consolidate that code.1658 // "The interface prototype object for a given interface A must have an1659 // internal [[Prototype]] property whose value is returned from the1660 // following steps:1661 // "If A is declared with the [Global] extended1662 // attribute, and A supports named properties, then return the named1663 // properties object for A, as defined in §3.6.4 Named properties1664 // object.1665 // "Otherwise, if A is declared to inherit from another interface, then1666 // return the interface prototype object for the inherited interface.1667 // "Otherwise, return %ObjectPrototype%.1668 //1669 // "In the ECMAScript binding, the DOMException type has some additional1670 // requirements:1671 //1672 // "Unlike normal interface types, the interface prototype object1673 // for DOMException must have as its [[Prototype]] the intrinsic1674 // object %ErrorPrototype%."1675 //1676 if (this.name === "Window") {1677 assert_class_string(Object.getPrototypeOf(this.get_interface_object().prototype),1678 'WindowProperties',1679 'Class name for prototype of Window' +1680 '.prototype is not "WindowProperties"');1681 } else {1682 var inherit_interface, inherit_interface_interface_object;1683 if (this.base) {1684 inherit_interface = this.base;1685 var parent = this.array.members[inherit_interface];1686 if (!parent.has_extended_attribute("NoInterfaceObject")) {1687 parent.assert_interface_object_exists();1688 inherit_interface_interface_object = parent.get_interface_object();1689 }1690 } else if (this.name === "DOMException") {1691 inherit_interface = 'Error';1692 inherit_interface_interface_object = self.Error;1693 } else {1694 inherit_interface = 'Object';1695 inherit_interface_interface_object = self.Object;1696 }1697 if (inherit_interface_interface_object) {1698 assert_not_equals(inherit_interface_interface_object, undefined,1699 'should inherit from ' + inherit_interface + ', but there is no such property');1700 assert_own_property(inherit_interface_interface_object, 'prototype',1701 'should inherit from ' + inherit_interface + ', but that object has no "prototype" property');1702 assert_equals(Object.getPrototypeOf(this.get_interface_object().prototype),1703 inherit_interface_interface_object.prototype,1704 'prototype of ' + this.name + '.prototype is not ' + inherit_interface + '.prototype');1705 } else {1706 // We can't test that we get the correct object, because this is the1707 // only way to get our hands on it. We only test that its class1708 // string, at least, is correct.1709 assert_class_string(Object.getPrototypeOf(this.get_interface_object().prototype),1710 inherit_interface + 'Prototype',1711 'Class name for prototype of ' + this.name +1712 '.prototype is not "' + inherit_interface + 'Prototype"');1713 }1714 }1715 // "The class string of an interface prototype object is the1716 // concatenation of the interfaceâs qualified identifier and the string1717 // âPrototypeâ."1718 // Skip these tests for now due to a specification issue about1719 // prototype name.1720 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=282441721 // assert_class_string(this.get_interface_object().prototype, this.get_qualified_name() + "Prototype",1722 // "class string of " + this.name + ".prototype");1723 // String() should end up calling {}.toString if nothing defines a1724 // stringifier.1725 if (!this.has_stringifier()) {1726 // assert_equals(String(this.get_interface_object().prototype), "[object " + this.get_qualified_name() + "Prototype]",1727 // "String(" + this.name + ".prototype)");1728 }1729 }.bind(this), this.name + " interface: existence and properties of interface prototype object");1730 // "If the interface is declared with the [Global]1731 // extended attribute, or the interface is in the set of inherited1732 // interfaces for any other interface that is declared with one of these1733 // attributes, then the interface prototype object must be an immutable1734 // prototype exotic object."1735 // https://heycam.github.io/webidl/#interface-prototype-object1736 if (this.is_global()) {1737 this.test_immutable_prototype("interface prototype object", this.get_interface_object().prototype);1738 }1739 subsetTestByKey(this.name, test, function()1740 {1741 if (this.is_callback() && !this.has_constants()) {1742 return;1743 }1744 this.assert_interface_object_exists();1745 if (this.is_callback()) {1746 assert_false("prototype" in this.get_interface_object(),1747 this.name + ' should not have a "prototype" property');1748 return;1749 }1750 assert_own_property(this.get_interface_object(), "prototype",1751 'interface "' + this.name + '" does not have own property "prototype"');1752 // "If the [NoInterfaceObject] extended attribute was not specified on1753 // the interface, then the interface prototype object must also have a1754 // property named âconstructorâ with attributes { [[Writable]]: true,1755 // [[Enumerable]]: false, [[Configurable]]: true } whose value is a1756 // reference to the interface object for the interface."1757 assert_own_property(this.get_interface_object().prototype, "constructor",1758 this.name + '.prototype does not have own property "constructor"');1759 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object().prototype, "constructor");1760 assert_false("get" in desc, this.name + ".prototype.constructor should not have a getter");1761 assert_false("set" in desc, this.name + ".prototype.constructor should not have a setter");1762 assert_true(desc.writable, this.name + ".prototype.constructor should be writable");1763 assert_false(desc.enumerable, this.name + ".prototype.constructor should not be enumerable");1764 assert_true(desc.configurable, this.name + ".prototype.constructor should be configurable");1765 assert_equals(this.get_interface_object().prototype.constructor, this.get_interface_object(),1766 this.name + '.prototype.constructor is not the same object as ' + this.name);1767 }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property');1768 subsetTestByKey(this.name, test, function()1769 {1770 if (this.is_callback() && !this.has_constants()) {1771 return;1772 }1773 this.assert_interface_object_exists();1774 if (this.is_callback()) {1775 assert_false("prototype" in this.get_interface_object(),1776 this.name + ' should not have a "prototype" property');1777 return;1778 }1779 assert_own_property(this.get_interface_object(), "prototype",1780 'interface "' + this.name + '" does not have own property "prototype"');1781 // If the interface has any member declared with the [Unscopable] extended1782 // attribute, then there must be a property on the interface prototype object1783 // whose name is the @@unscopables symbol, which has the attributes1784 // { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true },1785 // and whose value is an object created as follows...1786 var unscopables = this.get_unscopables().map(m => m.name);1787 var proto = this.get_interface_object().prototype;1788 if (unscopables.length != 0) {1789 assert_own_property(1790 proto, Symbol.unscopables,1791 this.name + '.prototype should have an @@unscopables property');1792 var desc = Object.getOwnPropertyDescriptor(proto, Symbol.unscopables);1793 assert_false("get" in desc,1794 this.name + ".prototype[Symbol.unscopables] should not have a getter");1795 assert_false("set" in desc, this.name + ".prototype[Symbol.unscopables] should not have a setter");1796 assert_false(desc.writable, this.name + ".prototype[Symbol.unscopables] should not be writable");1797 assert_false(desc.enumerable, this.name + ".prototype[Symbol.unscopables] should not be enumerable");1798 assert_true(desc.configurable, this.name + ".prototype[Symbol.unscopables] should be configurable");1799 assert_equals(desc.value, proto[Symbol.unscopables],1800 this.name + '.prototype[Symbol.unscopables] should be in the descriptor');1801 assert_equals(typeof desc.value, "object",1802 this.name + '.prototype[Symbol.unscopables] should be an object');1803 assert_equals(Object.getPrototypeOf(desc.value), null,1804 this.name + '.prototype[Symbol.unscopables] should have a null prototype');1805 assert_equals(Object.getOwnPropertySymbols(desc.value).length,1806 0,1807 this.name + '.prototype[Symbol.unscopables] should have the right number of symbol-named properties');1808 // Check that we do not have _extra_ unscopables. Checking that we1809 // have all the ones we should will happen in the per-member tests.1810 var observed = Object.getOwnPropertyNames(desc.value);1811 for (var prop of observed) {1812 assert_not_equals(unscopables.indexOf(prop),1813 -1,1814 this.name + '.prototype[Symbol.unscopables] has unexpected property "' + prop + '"');1815 }1816 } else {1817 assert_equals(Object.getOwnPropertyDescriptor(this.get_interface_object().prototype, Symbol.unscopables),1818 undefined,1819 this.name + '.prototype should not have @@unscopables');1820 }1821 }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s @@unscopables property');1822};1823IdlInterface.prototype.test_immutable_prototype = function(type, obj)1824{1825 if (typeof Object.setPrototypeOf !== "function") {1826 return;1827 }1828 subsetTestByKey(this.name, test, function(t) {1829 var originalValue = Object.getPrototypeOf(obj);1830 var newValue = Object.create(null);1831 t.add_cleanup(function() {1832 try {1833 Object.setPrototypeOf(obj, originalValue);1834 } catch (err) {}1835 });1836 assert_throws(new TypeError(), function() {1837 Object.setPrototypeOf(obj, newValue);1838 });1839 assert_equals(1840 Object.getPrototypeOf(obj),1841 originalValue,1842 "original value not modified"1843 );1844 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1845 "of " + type + " - setting to a new value via Object.setPrototypeOf " +1846 "should throw a TypeError");1847 subsetTestByKey(this.name, test, function(t) {1848 var originalValue = Object.getPrototypeOf(obj);1849 var newValue = Object.create(null);1850 t.add_cleanup(function() {1851 var setter = Object.getOwnPropertyDescriptor(1852 Object.prototype, '__proto__'1853 ).set;1854 try {1855 setter.call(obj, originalValue);1856 } catch (err) {}1857 });1858 assert_throws(new TypeError(), function() {1859 obj.__proto__ = newValue;1860 });1861 assert_equals(1862 Object.getPrototypeOf(obj),1863 originalValue,1864 "original value not modified"1865 );1866 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1867 "of " + type + " - setting to a new value via __proto__ " +1868 "should throw a TypeError");1869 subsetTestByKey(this.name, test, function(t) {1870 var originalValue = Object.getPrototypeOf(obj);1871 var newValue = Object.create(null);1872 t.add_cleanup(function() {1873 try {1874 Reflect.setPrototypeOf(obj, originalValue);1875 } catch (err) {}1876 });1877 assert_false(Reflect.setPrototypeOf(obj, newValue));1878 assert_equals(1879 Object.getPrototypeOf(obj),1880 originalValue,1881 "original value not modified"1882 );1883 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1884 "of " + type + " - setting to a new value via Reflect.setPrototypeOf " +1885 "should return false");1886 subsetTestByKey(this.name, test, function() {1887 var originalValue = Object.getPrototypeOf(obj);1888 Object.setPrototypeOf(obj, originalValue);1889 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1890 "of " + type + " - setting to its original value via Object.setPrototypeOf " +1891 "should not throw");1892 subsetTestByKey(this.name, test, function() {1893 var originalValue = Object.getPrototypeOf(obj);1894 obj.__proto__ = originalValue;1895 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1896 "of " + type + " - setting to its original value via __proto__ " +1897 "should not throw");1898 subsetTestByKey(this.name, test, function() {1899 var originalValue = Object.getPrototypeOf(obj);1900 assert_true(Reflect.setPrototypeOf(obj, originalValue));1901 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1902 "of " + type + " - setting to its original value via Reflect.setPrototypeOf " +1903 "should return true");1904};1905IdlInterface.prototype.test_member_const = function(member)1906{1907 if (!this.has_constants()) {1908 throw new IdlHarnessError("Internal error: test_member_const called without any constants");1909 }1910 subsetTestByKey(this.name, test, function()1911 {1912 this.assert_interface_object_exists();1913 // "For each constant defined on an interface A, there must be1914 // a corresponding property on the interface object, if it1915 // exists."1916 assert_own_property(this.get_interface_object(), member.name);1917 // "The value of the property is that which is obtained by1918 // converting the constantâs IDL value to an ECMAScript1919 // value."1920 assert_equals(this.get_interface_object()[member.name], constValue(member.value),1921 "property has wrong value");1922 // "The property has attributes { [[Writable]]: false,1923 // [[Enumerable]]: true, [[Configurable]]: false }."1924 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), member.name);1925 assert_false("get" in desc, "property should not have a getter");1926 assert_false("set" in desc, "property should not have a setter");1927 assert_false(desc.writable, "property should not be writable");1928 assert_true(desc.enumerable, "property should be enumerable");1929 assert_false(desc.configurable, "property should not be configurable");1930 }.bind(this), this.name + " interface: constant " + member.name + " on interface object");1931 // "In addition, a property with the same characteristics must1932 // exist on the interface prototype object."1933 subsetTestByKey(this.name, test, function()1934 {1935 this.assert_interface_object_exists();1936 if (this.is_callback()) {1937 assert_false("prototype" in this.get_interface_object(),1938 this.name + ' should not have a "prototype" property');1939 return;1940 }1941 assert_own_property(this.get_interface_object(), "prototype",1942 'interface "' + this.name + '" does not have own property "prototype"');1943 assert_own_property(this.get_interface_object().prototype, member.name);1944 assert_equals(this.get_interface_object().prototype[member.name], constValue(member.value),1945 "property has wrong value");1946 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), member.name);1947 assert_false("get" in desc, "property should not have a getter");1948 assert_false("set" in desc, "property should not have a setter");1949 assert_false(desc.writable, "property should not be writable");1950 assert_true(desc.enumerable, "property should be enumerable");1951 assert_false(desc.configurable, "property should not be configurable");1952 }.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object");1953};1954IdlInterface.prototype.test_member_attribute = function(member)1955 {1956 if (!shouldRunSubTest(this.name)) {1957 return;1958 }1959 var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: attribute " + member.name);1960 a_test.step(function()1961 {1962 if (this.is_callback() && !this.has_constants()) {1963 a_test.done()1964 return;1965 }1966 this.assert_interface_object_exists();1967 assert_own_property(this.get_interface_object(), "prototype",1968 'interface "' + this.name + '" does not have own property "prototype"');1969 if (member.special === "static") {1970 assert_own_property(this.get_interface_object(), member.name,1971 "The interface object must have a property " +1972 format_value(member.name));1973 a_test.done();1974 return;1975 }1976 this.do_member_unscopable_asserts(member);1977 if (this.is_global()) {1978 assert_own_property(self, member.name,1979 "The global object must have a property " +1980 format_value(member.name));1981 assert_false(member.name in this.get_interface_object().prototype,1982 "The prototype object should not have a property " +1983 format_value(member.name));1984 var getter = Object.getOwnPropertyDescriptor(self, member.name).get;1985 assert_equals(typeof(getter), "function",1986 format_value(member.name) + " must have a getter");1987 // Try/catch around the get here, since it can legitimately throw.1988 // If it does, we obviously can't check for equality with direct1989 // invocation of the getter.1990 var gotValue;1991 var propVal;1992 try {1993 propVal = self[member.name];1994 gotValue = true;1995 } catch (e) {1996 gotValue = false;1997 }1998 if (gotValue) {1999 assert_equals(propVal, getter.call(undefined),2000 "Gets on a global should not require an explicit this");2001 }2002 // do_interface_attribute_asserts must be the last thing we do,2003 // since it will call done() on a_test.2004 this.do_interface_attribute_asserts(self, member, a_test);2005 } else {2006 assert_true(member.name in this.get_interface_object().prototype,2007 "The prototype object must have a property " +2008 format_value(member.name));2009 if (!member.has_extended_attribute("LenientThis")) {2010 if (member.idlType.generic !== "Promise") {2011 assert_throws(new TypeError(), function() {2012 this.get_interface_object().prototype[member.name];2013 }.bind(this), "getting property on prototype object must throw TypeError");2014 // do_interface_attribute_asserts must be the last thing we2015 // do, since it will call done() on a_test.2016 this.do_interface_attribute_asserts(this.get_interface_object().prototype, member, a_test);2017 } else {2018 promise_rejects(a_test, new TypeError(),2019 this.get_interface_object().prototype[member.name])2020 .then(function() {2021 // do_interface_attribute_asserts must be the last2022 // thing we do, since it will call done() on a_test.2023 this.do_interface_attribute_asserts(this.get_interface_object().prototype,2024 member, a_test);2025 }.bind(this));2026 }2027 } else {2028 assert_equals(this.get_interface_object().prototype[member.name], undefined,2029 "getting property on prototype object must return undefined");2030 // do_interface_attribute_asserts must be the last thing we do,2031 // since it will call done() on a_test.2032 this.do_interface_attribute_asserts(this.get_interface_object().prototype, member, a_test);2033 }2034 }2035 }.bind(this));2036};2037IdlInterface.prototype.test_member_operation = function(member)2038{2039 if (!shouldRunSubTest(this.name)) {2040 return;2041 }2042 var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: operation " + member.name +2043 "(" + member.arguments.map(2044 function(m) {return m.idlType.idlType; } ).join(", ")2045 +")");2046 a_test.step(function()2047 {2048 // This function tests WebIDL as of 2015-12-29.2049 // https://heycam.github.io/webidl/#es-operations2050 if (this.is_callback() && !this.has_constants()) {2051 a_test.done();2052 return;2053 }2054 this.assert_interface_object_exists();2055 if (this.is_callback()) {2056 assert_false("prototype" in this.get_interface_object(),2057 this.name + ' should not have a "prototype" property');2058 a_test.done();2059 return;2060 }2061 assert_own_property(this.get_interface_object(), "prototype",2062 'interface "' + this.name + '" does not have own property "prototype"');2063 // "For each unique identifier of an exposed operation defined on the2064 // interface, there must exist a corresponding property, unless the2065 // effective overload set for that identifier and operation and with an2066 // argument count of 0 has no entries."2067 // TODO: Consider [Exposed].2068 // "The location of the property is determined as follows:"2069 var memberHolderObject;2070 // "* If the operation is static, then the property exists on the2071 // interface object."2072 if (member.special === "static") {2073 assert_own_property(this.get_interface_object(), member.name,2074 "interface object missing static operation");2075 memberHolderObject = this.get_interface_object();2076 // "* Otherwise, [...] if the interface was declared with the [Global]2077 // extended attribute, then the property exists2078 // on every object that implements the interface."2079 } else if (this.is_global()) {2080 assert_own_property(self, member.name,2081 "global object missing non-static operation");2082 memberHolderObject = self;2083 // "* Otherwise, the property exists solely on the interfaceâs2084 // interface prototype object."2085 } else {2086 assert_own_property(this.get_interface_object().prototype, member.name,2087 "interface prototype object missing non-static operation");2088 memberHolderObject = this.get_interface_object().prototype;2089 }2090 this.do_member_unscopable_asserts(member);2091 this.do_member_operation_asserts(memberHolderObject, member, a_test);2092 }.bind(this));2093};2094IdlInterface.prototype.do_member_unscopable_asserts = function(member)2095{2096 // Check that if the member is unscopable then it's in the2097 // @@unscopables object properly.2098 if (!member.isUnscopable) {2099 return;2100 }2101 var unscopables = this.get_interface_object().prototype[Symbol.unscopables];2102 var prop = member.name;2103 var propDesc = Object.getOwnPropertyDescriptor(unscopables, prop);2104 assert_equals(typeof propDesc, "object",2105 this.name + '.prototype[Symbol.unscopables].' + prop + ' must exist')2106 assert_false("get" in propDesc,2107 this.name + '.prototype[Symbol.unscopables].' + prop + ' must have no getter');2108 assert_false("set" in propDesc,2109 this.name + '.prototype[Symbol.unscopables].' + prop + ' must have no setter');2110 assert_true(propDesc.writable,2111 this.name + '.prototype[Symbol.unscopables].' + prop + ' must be writable');2112 assert_true(propDesc.enumerable,2113 this.name + '.prototype[Symbol.unscopables].' + prop + ' must be enumerable');2114 assert_true(propDesc.configurable,2115 this.name + '.prototype[Symbol.unscopables].' + prop + ' must be configurable');2116 assert_equals(propDesc.value, true,2117 this.name + '.prototype[Symbol.unscopables].' + prop + ' must have the value `true`');2118};2119IdlInterface.prototype.do_member_operation_asserts = function(memberHolderObject, member, a_test)2120{2121 var done = a_test.done.bind(a_test);2122 var operationUnforgeable = member.isUnforgeable;2123 var desc = Object.getOwnPropertyDescriptor(memberHolderObject, member.name);2124 // "The property has attributes { [[Writable]]: B,2125 // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the2126 // operation is unforgeable on the interface, and true otherwise".2127 assert_false("get" in desc, "property should not have a getter");2128 assert_false("set" in desc, "property should not have a setter");2129 assert_equals(desc.writable, !operationUnforgeable,2130 "property should be writable if and only if not unforgeable");2131 assert_true(desc.enumerable, "property should be enumerable");2132 assert_equals(desc.configurable, !operationUnforgeable,2133 "property should be configurable if and only if not unforgeable");2134 // "The value of the property is a Function object whose2135 // behavior is as follows . . ."2136 assert_equals(typeof memberHolderObject[member.name], "function",2137 "property must be a function");2138 const ctors = this.members.filter(function(m) {2139 return m.type == "operation" && m.name == member.name;2140 });2141 assert_equals(2142 memberHolderObject[member.name].length,2143 minOverloadLength(ctors),2144 "property has wrong .length");2145 // Make some suitable arguments2146 var args = member.arguments.map(function(arg) {2147 return create_suitable_object(arg.idlType);2148 });2149 // "Let O be a value determined as follows:2150 // ". . .2151 // "Otherwise, throw a TypeError."2152 // This should be hit if the operation is not static, there is2153 // no [ImplicitThis] attribute, and the this value is null.2154 //2155 // TODO: We currently ignore the [ImplicitThis] case. Except we manually2156 // check for globals, since otherwise we'll invoke window.close(). And we2157 // have to skip this test for anything that on the proto chain of "self",2158 // since that does in fact have implicit-this behavior.2159 if (member.special !== "static") {2160 var cb;2161 if (!this.is_global() &&2162 memberHolderObject[member.name] != self[member.name])2163 {2164 cb = awaitNCallbacks(2, done);2165 throwOrReject(a_test, member, memberHolderObject[member.name], null, args,2166 "calling operation with this = null didn't throw TypeError", cb);2167 } else {2168 cb = awaitNCallbacks(1, done);2169 }2170 // ". . . If O is not null and is also not a platform object2171 // that implements interface I, throw a TypeError."2172 //2173 // TODO: Test a platform object that implements some other2174 // interface. (Have to be sure to get inheritance right.)2175 throwOrReject(a_test, member, memberHolderObject[member.name], {}, args,2176 "calling operation with this = {} didn't throw TypeError", cb);2177 } else {2178 done();2179 }2180}2181IdlInterface.prototype.test_to_json_operation = function(desc, memberHolderObject, member) {2182 var instanceName = memberHolderObject && memberHolderObject.constructor.name2183 || member.name + " object";2184 if (member.has_extended_attribute("Default")) {2185 subsetTestByKey(this.name, test, function() {2186 var map = this.default_to_json_operation();2187 var json = memberHolderObject.toJSON();2188 map.forEach(function(type, k) {2189 assert_true(k in json, "property " + JSON.stringify(k) + " should be present in the output of " + this.name + ".prototype.toJSON()");2190 var descriptor = Object.getOwnPropertyDescriptor(json, k);2191 assert_true(descriptor.writable, "property " + k + " should be writable");2192 assert_true(descriptor.configurable, "property " + k + " should be configurable");2193 assert_true(descriptor.enumerable, "property " + k + " should be enumerable");2194 this.array.assert_type_is(json[k], type);2195 delete json[k];2196 }, this);2197 }.bind(this), this.name + " interface: default toJSON operation on " + desc);2198 } else {2199 subsetTestByKey(this.name, test, function() {2200 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);2201 this.array.assert_type_is(memberHolderObject.toJSON(), member.idlType);2202 }.bind(this), this.name + " interface: toJSON operation on " + desc);2203 }2204};2205IdlInterface.prototype.test_member_iterable = function(member)2206{2207 subsetTestByKey(this.name, test, function()2208 {2209 var isPairIterator = member.idlType.length === 2;2210 var proto = this.get_interface_object().prototype;2211 var descriptor = Object.getOwnPropertyDescriptor(proto, Symbol.iterator);2212 assert_true(descriptor.writable, "@@iterator property should be writable");2213 assert_true(descriptor.configurable, "@@iterator property should be configurable");2214 assert_false(descriptor.enumerable, "@@iterator property should not be enumerable");2215 assert_equals(typeof descriptor.value, "function", "@@iterator property should be a function");2216 assert_equals(descriptor.value.length, 0, "@@iterator function object length should be 0");2217 assert_equals(descriptor.value.name, isPairIterator ? "entries" : "values", "@@iterator function object should have the right name");2218 if (isPairIterator) {2219 assert_equals(proto["entries"], proto[Symbol.iterator], "entries method should be the same as @@iterator method");2220 } else {2221 assert_equals(proto[Symbol.iterator], Array.prototype[Symbol.iterator], "@@iterator method should be the same as Array prototype's");2222 ["entries", "keys", "values", "forEach", Symbol.iterator].forEach(function(property) {2223 var propertyName = property === Symbol.iterator ? "@@iterator" : property;2224 assert_equals(proto[property], Array.prototype[property], propertyName + " method should be the same as Array prototype's");2225 }.bind(this));2226 }2227 }.bind(this), this.name + " interface: iterable<" + member.idlType.map(function(t) { return t.idlType; }).join(", ") + ">");2228};2229IdlInterface.prototype.test_member_stringifier = function(member)2230{2231 subsetTestByKey(this.name, test, function()2232 {2233 if (this.is_callback() && !this.has_constants()) {2234 return;2235 }2236 this.assert_interface_object_exists();2237 if (this.is_callback()) {2238 assert_false("prototype" in this.get_interface_object(),2239 this.name + ' should not have a "prototype" property');2240 return;2241 }2242 assert_own_property(this.get_interface_object(), "prototype",2243 'interface "' + this.name + '" does not have own property "prototype"');2244 // ". . . the property exists on the interface prototype object."2245 var interfacePrototypeObject = this.get_interface_object().prototype;2246 assert_own_property(interfacePrototypeObject, "toString",2247 "interface prototype object missing non-static operation");2248 var stringifierUnforgeable = member.isUnforgeable;2249 var desc = Object.getOwnPropertyDescriptor(interfacePrototypeObject, "toString");2250 // "The property has attributes { [[Writable]]: B,2251 // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the2252 // stringifier is unforgeable on the interface, and true otherwise."2253 assert_false("get" in desc, "property should not have a getter");2254 assert_false("set" in desc, "property should not have a setter");2255 assert_equals(desc.writable, !stringifierUnforgeable,2256 "property should be writable if and only if not unforgeable");2257 assert_true(desc.enumerable, "property should be enumerable");2258 assert_equals(desc.configurable, !stringifierUnforgeable,2259 "property should be configurable if and only if not unforgeable");2260 // "The value of the property is a Function object, which behaves as2261 // follows . . ."2262 assert_equals(typeof interfacePrototypeObject.toString, "function",2263 "property must be a function");2264 // "The value of the Function objectâs âlengthâ property is the Number2265 // value 0."2266 assert_equals(interfacePrototypeObject.toString.length, 0,2267 "property has wrong .length");2268 // "Let O be the result of calling ToObject on the this value."2269 assert_throws(new TypeError(), function() {2270 interfacePrototypeObject.toString.apply(null, []);2271 }, "calling stringifier with this = null didn't throw TypeError");2272 // "If O is not an object that implements the interface on which the2273 // stringifier was declared, then throw a TypeError."2274 //2275 // TODO: Test a platform object that implements some other2276 // interface. (Have to be sure to get inheritance right.)2277 assert_throws(new TypeError(), function() {2278 interfacePrototypeObject.toString.apply({}, []);2279 }, "calling stringifier with this = {} didn't throw TypeError");2280 }.bind(this), this.name + " interface: stringifier");2281};2282IdlInterface.prototype.test_members = function()2283{2284 for (var i = 0; i < this.members.length; i++)2285 {2286 var member = this.members[i];2287 if (member.untested) {2288 continue;2289 }2290 if (!exposed_in(exposure_set(member, this.exposureSet))) {2291 subsetTestByKey(this.name, test, function() {2292 // It's not exposed, so we shouldn't find it anywhere.2293 assert_false(member.name in this.get_interface_object(),2294 "The interface object must not have a property " +2295 format_value(member.name));2296 assert_false(member.name in this.get_interface_object().prototype,2297 "The prototype object must not have a property " +2298 format_value(member.name));2299 }.bind(this), this.name + " interface: member " + member.name);2300 continue;2301 }2302 switch (member.type) {2303 case "const":2304 this.test_member_const(member);2305 break;2306 case "attribute":2307 // For unforgeable attributes, we do the checks in2308 // test_interface_of instead.2309 if (!member.isUnforgeable)2310 {2311 this.test_member_attribute(member);2312 }2313 if (member.special === "stringifier") {2314 this.test_member_stringifier(member);2315 }2316 break;2317 case "operation":2318 // TODO: Need to correctly handle multiple operations with the same2319 // identifier.2320 // For unforgeable operations, we do the checks in2321 // test_interface_of instead.2322 if (member.name) {2323 if (!member.isUnforgeable)2324 {2325 this.test_member_operation(member);2326 }2327 } else if (member.special === "stringifier") {2328 this.test_member_stringifier(member);2329 }2330 break;2331 case "iterable":2332 this.test_member_iterable(member);2333 break;2334 default:2335 // TODO: check more member types.2336 break;2337 }2338 }2339};2340IdlInterface.prototype.test_object = function(desc)2341{2342 var obj, exception = null;2343 try2344 {2345 obj = eval(desc);2346 }2347 catch(e)2348 {2349 exception = e;2350 }2351 var expected_typeof =2352 this.members.some(function(member) { return member.legacycaller; })2353 ? "function"2354 : "object";2355 this.test_primary_interface_of(desc, obj, exception, expected_typeof);2356 var current_interface = this;2357 while (current_interface)2358 {2359 if (!(current_interface.name in this.array.members))2360 {2361 throw new IdlHarnessError("Interface " + current_interface.name + " not found (inherited by " + this.name + ")");2362 }2363 if (current_interface.prevent_multiple_testing && current_interface.already_tested)2364 {2365 return;2366 }2367 current_interface.test_interface_of(desc, obj, exception, expected_typeof);2368 current_interface = this.array.members[current_interface.base];2369 }2370};2371IdlInterface.prototype.test_primary_interface_of = function(desc, obj, exception, expected_typeof)2372{2373 // Only the object itself, not its members, are tested here, so if the2374 // interface is untested, there is nothing to do.2375 if (this.untested)2376 {2377 return;2378 }2379 // "The internal [[SetPrototypeOf]] method of every platform object that2380 // implements an interface with the [Global] extended2381 // attribute must execute the same algorithm as is defined for the2382 // [[SetPrototypeOf]] internal method of an immutable prototype exotic2383 // object."2384 // https://heycam.github.io/webidl/#platform-object-setprototypeof2385 if (this.is_global())2386 {2387 this.test_immutable_prototype("global platform object", obj);2388 }2389 // We can't easily test that its prototype is correct if there's no2390 // interface object, or the object is from a different global environment2391 // (not instanceof Object). TODO: test in this case that its prototype at2392 // least looks correct, even if we can't test that it's actually correct.2393 if (!this.has_extended_attribute("NoInterfaceObject")2394 && (typeof obj != expected_typeof || obj instanceof Object))2395 {2396 subsetTestByKey(this.name, test, function()2397 {2398 assert_equals(exception, null, "Unexpected exception when evaluating object");2399 assert_equals(typeof obj, expected_typeof, "wrong typeof object");2400 this.assert_interface_object_exists();2401 assert_own_property(this.get_interface_object(), "prototype",2402 'interface "' + this.name + '" does not have own property "prototype"');2403 // "The value of the internal [[Prototype]] property of the2404 // platform object is the interface prototype object of the primary2405 // interface from the platform objectâs associated global2406 // environment."2407 assert_equals(Object.getPrototypeOf(obj),2408 this.get_interface_object().prototype,2409 desc + "'s prototype is not " + this.name + ".prototype");2410 }.bind(this), this.name + " must be primary interface of " + desc);2411 }2412 // "The class string of a platform object that implements one or more2413 // interfaces must be the qualified name of the primary interface of the2414 // platform object."2415 subsetTestByKey(this.name, test, function()2416 {2417 assert_equals(exception, null, "Unexpected exception when evaluating object");2418 assert_equals(typeof obj, expected_typeof, "wrong typeof object");2419 assert_class_string(obj, this.get_qualified_name(), "class string of " + desc);2420 if (!this.has_stringifier())2421 {2422 assert_equals(String(obj), "[object " + this.get_qualified_name() + "]", "String(" + desc + ")");2423 }2424 }.bind(this), "Stringification of " + desc);2425};2426IdlInterface.prototype.test_interface_of = function(desc, obj, exception, expected_typeof)2427{2428 // TODO: Indexed and named properties, more checks on interface members2429 this.already_tested = true;2430 if (!shouldRunSubTest(this.name)) {2431 return;2432 }2433 for (var i = 0; i < this.members.length; i++)2434 {2435 var member = this.members[i];2436 if (member.untested) {2437 continue;2438 }2439 if (!exposed_in(exposure_set(member, this.exposureSet))) {2440 subsetTestByKey(this.name, test, function() {2441 assert_equals(exception, null, "Unexpected exception when evaluating object");2442 assert_false(member.name in obj);2443 }.bind(this), this.name + " interface: " + desc + ' must not have property "' + member.name + '"');2444 continue;2445 }2446 if (member.type == "attribute" && member.isUnforgeable)2447 {2448 var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: " + desc + ' must have own property "' + member.name + '"');2449 a_test.step(function() {2450 assert_equals(exception, null, "Unexpected exception when evaluating object");2451 assert_equals(typeof obj, expected_typeof, "wrong typeof object");2452 // Call do_interface_attribute_asserts last, since it will call a_test.done()2453 this.do_interface_attribute_asserts(obj, member, a_test);2454 }.bind(this));2455 }2456 else if (member.type == "operation" &&2457 member.name &&2458 member.isUnforgeable)2459 {2460 var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: " + desc + ' must have own property "' + member.name + '"');2461 a_test.step(function()2462 {2463 assert_equals(exception, null, "Unexpected exception when evaluating object");2464 assert_equals(typeof obj, expected_typeof, "wrong typeof object");2465 assert_own_property(obj, member.name,2466 "Doesn't have the unforgeable operation property");2467 this.do_member_operation_asserts(obj, member, a_test);2468 }.bind(this));2469 }2470 else if ((member.type == "const"2471 || member.type == "attribute"2472 || member.type == "operation")2473 && member.name)2474 {2475 var described_name = member.name;2476 if (member.type == "operation")2477 {2478 described_name += "(" + member.arguments.map(arg => arg.idlType.idlType).join(", ") + ")";2479 }2480 subsetTestByKey(this.name, test, function()2481 {2482 assert_equals(exception, null, "Unexpected exception when evaluating object");2483 assert_equals(typeof obj, expected_typeof, "wrong typeof object");2484 if (member.special !== "static") {2485 if (!this.is_global()) {2486 assert_inherits(obj, member.name);2487 } else {2488 assert_own_property(obj, member.name);2489 }2490 if (member.type == "const")2491 {2492 assert_equals(obj[member.name], constValue(member.value));2493 }2494 if (member.type == "attribute")2495 {2496 // Attributes are accessor properties, so they might2497 // legitimately throw an exception rather than returning2498 // anything.2499 var property, thrown = false;2500 try2501 {2502 property = obj[member.name];2503 }2504 catch (e)2505 {2506 thrown = true;2507 }2508 if (!thrown)2509 {2510 this.array.assert_type_is(property, member.idlType);2511 }2512 }2513 if (member.type == "operation")2514 {2515 assert_equals(typeof obj[member.name], "function");2516 }2517 }2518 }.bind(this), this.name + " interface: " + desc + ' must inherit property "' + described_name + '" with the proper type');2519 }2520 // TODO: This is wrong if there are multiple operations with the same2521 // identifier.2522 // TODO: Test passing arguments of the wrong type.2523 if (member.type == "operation" && member.name && member.arguments.length)2524 {2525 var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: calling " + member.name +2526 "(" + member.arguments.map(function(m) { return m.idlType.idlType; }).join(", ") +2527 ") on " + desc + " with too few arguments must throw TypeError");2528 a_test.step(function()2529 {2530 assert_equals(exception, null, "Unexpected exception when evaluating object");2531 assert_equals(typeof obj, expected_typeof, "wrong typeof object");2532 var fn;2533 if (member.special !== "static") {2534 if (!this.is_global() && !member.isUnforgeable) {2535 assert_inherits(obj, member.name);2536 } else {2537 assert_own_property(obj, member.name);2538 }2539 fn = obj[member.name];2540 }2541 else2542 {2543 assert_own_property(obj.constructor, member.name, "interface object must have static operation as own property");2544 fn = obj.constructor[member.name];2545 }2546 var minLength = minOverloadLength(this.members.filter(function(m) {2547 return m.type == "operation" && m.name == member.name;2548 }));2549 var args = [];2550 var cb = awaitNCallbacks(minLength, a_test.done.bind(a_test));2551 for (var i = 0; i < minLength; i++) {2552 throwOrReject(a_test, member, fn, obj, args, "Called with " + i + " arguments", cb);2553 args.push(create_suitable_object(member.arguments[i].idlType));2554 }2555 if (minLength === 0) {2556 cb();2557 }2558 }.bind(this));2559 }2560 if (member.is_to_json_regular_operation()) {2561 this.test_to_json_operation(desc, obj, member);2562 }2563 }2564};2565IdlInterface.prototype.has_stringifier = function()2566{2567 if (this.name === "DOMException") {2568 // toString is inherited from Error, so don't assume we have the2569 // default stringifer2570 return true;2571 }2572 if (this.members.some(function(member) { return member.special === "stringifier"; })) {2573 return true;2574 }2575 if (this.base &&2576 this.array.members[this.base].has_stringifier()) {2577 return true;2578 }2579 return false;2580};2581IdlInterface.prototype.do_interface_attribute_asserts = function(obj, member, a_test)2582{2583 // This function tests WebIDL as of 2015-01-27.2584 // TODO: Consider [Exposed].2585 // This is called by test_member_attribute() with the prototype as obj if2586 // it is not a global, and the global otherwise, and by test_interface_of()2587 // with the object as obj.2588 var pendingPromises = [];2589 // "For each exposed attribute of the interface, whether it was declared on2590 // the interface itself or one of its consequential interfaces, there MUST2591 // exist a corresponding property. The characteristics of this property are2592 // as follows:"2593 // "The name of the property is the identifier of the attribute."2594 assert_own_property(obj, member.name);2595 // "The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:2596 // true, [[Configurable]]: configurable }, where:2597 // "configurable is false if the attribute was declared with the2598 // [Unforgeable] extended attribute and true otherwise;2599 // "G is the attribute getter, defined below; and2600 // "S is the attribute setter, also defined below."2601 var desc = Object.getOwnPropertyDescriptor(obj, member.name);2602 assert_false("value" in desc, 'property descriptor should not have a "value" field');2603 assert_false("writable" in desc, 'property descriptor should not have a "writable" field');2604 assert_true(desc.enumerable, "property should be enumerable");2605 if (member.isUnforgeable)2606 {2607 assert_false(desc.configurable, "[Unforgeable] property must not be configurable");2608 }2609 else2610 {2611 assert_true(desc.configurable, "property must be configurable");2612 }2613 // "The attribute getter is a Function object whose behavior when invoked2614 // is as follows:"2615 assert_equals(typeof desc.get, "function", "getter must be Function");2616 // "If the attribute is a regular attribute, then:"2617 if (member.special !== "static") {2618 // "If O is not a platform object that implements I, then:2619 // "If the attribute was specified with the [LenientThis] extended2620 // attribute, then return undefined.2621 // "Otherwise, throw a TypeError."2622 if (!member.has_extended_attribute("LenientThis")) {2623 if (member.idlType.generic !== "Promise") {2624 assert_throws(new TypeError(), function() {2625 desc.get.call({});2626 }.bind(this), "calling getter on wrong object type must throw TypeError");2627 } else {2628 pendingPromises.push(2629 promise_rejects(a_test, new TypeError(), desc.get.call({}),2630 "calling getter on wrong object type must reject the return promise with TypeError"));2631 }2632 } else {2633 assert_equals(desc.get.call({}), undefined,2634 "calling getter on wrong object type must return undefined");2635 }2636 }2637 // "The value of the Function objectâs âlengthâ property is the Number2638 // value 0."2639 assert_equals(desc.get.length, 0, "getter length must be 0");2640 // "Let name be the string "get " prepended to attributeâs identifier."2641 // "Perform ! SetFunctionName(F, name)."2642 assert_equals(desc.get.name, "get " + member.name,2643 "getter must have the name 'get " + member.name + "'");2644 // TODO: Test calling setter on the interface prototype (should throw2645 // TypeError in most cases).2646 if (member.readonly2647 && !member.has_extended_attribute("LenientSetter")2648 && !member.has_extended_attribute("PutForwards")2649 && !member.has_extended_attribute("Replaceable"))2650 {2651 // "The attribute setter is undefined if the attribute is declared2652 // readonly and has neither a [PutForwards] nor a [Replaceable]2653 // extended attribute declared on it."2654 assert_equals(desc.set, undefined, "setter must be undefined for readonly attributes");2655 }2656 else2657 {2658 // "Otherwise, it is a Function object whose behavior when2659 // invoked is as follows:"2660 assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes");2661 // "If the attribute is a regular attribute, then:"2662 if (member.special !== "static") {2663 // "If /validThis/ is false and the attribute was not specified2664 // with the [LenientThis] extended attribute, then throw a2665 // TypeError."2666 // "If the attribute is declared with a [Replaceable] extended2667 // attribute, then: ..."2668 // "If validThis is false, then return."2669 if (!member.has_extended_attribute("LenientThis")) {2670 assert_throws(new TypeError(), function() {2671 desc.set.call({});2672 }.bind(this), "calling setter on wrong object type must throw TypeError");2673 } else {2674 assert_equals(desc.set.call({}), undefined,2675 "calling setter on wrong object type must return undefined");2676 }2677 }2678 // "The value of the Function objectâs âlengthâ property is the Number2679 // value 1."2680 assert_equals(desc.set.length, 1, "setter length must be 1");2681 // "Let name be the string "set " prepended to id."2682 // "Perform ! SetFunctionName(F, name)."2683 assert_equals(desc.set.name, "set " + member.name,2684 "The attribute setter must have the name 'set " + member.name + "'");2685 }2686 Promise.all(pendingPromises).then(a_test.done.bind(a_test));2687}2688/// IdlInterfaceMember ///2689function IdlInterfaceMember(obj)2690{2691 /**2692 * obj is an object produced by the WebIDLParser.js "ifMember" production.2693 * We just forward all properties to this object without modification,2694 * except for special extAttrs handling.2695 */2696 for (var k in obj.toJSON())2697 {2698 this[k] = obj[k];2699 }2700 if (!("extAttrs" in this))2701 {2702 this.extAttrs = [];2703 }2704 this.isUnforgeable = this.has_extended_attribute("Unforgeable");2705 this.isUnscopable = this.has_extended_attribute("Unscopable");2706}2707IdlInterfaceMember.prototype = Object.create(IdlObject.prototype);2708IdlInterfaceMember.prototype.toJSON = function() {2709 return this;2710};2711IdlInterfaceMember.prototype.is_to_json_regular_operation = function() {2712 return this.type == "operation" && this.special !== "static" && this.name == "toJSON";2713};2714/// Internal helper functions ///2715function create_suitable_object(type)2716{2717 /**2718 * type is an object produced by the WebIDLParser.js "type" production. We2719 * return a JavaScript value that matches the type, if we can figure out2720 * how.2721 */2722 if (type.nullable)2723 {2724 return null;2725 }2726 switch (type.idlType)2727 {2728 case "any":2729 case "boolean":2730 return true;2731 case "byte": case "octet": case "short": case "unsigned short":2732 case "long": case "unsigned long": case "long long":2733 case "unsigned long long": case "float": case "double":2734 case "unrestricted float": case "unrestricted double":2735 return 7;2736 case "DOMString":2737 case "ByteString":2738 case "USVString":2739 return "foo";2740 case "object":2741 return {a: "b"};2742 case "Node":2743 return document.createTextNode("abc");2744 }2745 return null;2746}2747/// IdlEnum ///2748// Used for IdlArray.prototype.assert_type_is2749function IdlEnum(obj)2750{2751 /**2752 * obj is an object produced by the WebIDLParser.js "dictionary"2753 * production.2754 */2755 /** Self-explanatory. */2756 this.name = obj.name;2757 /** An array of values produced by the "enum" production. */2758 this.values = obj.values;2759}2760IdlEnum.prototype = Object.create(IdlObject.prototype);2761/// IdlTypedef ///2762// Used for IdlArray.prototype.assert_type_is2763function IdlTypedef(obj)2764{2765 /**2766 * obj is an object produced by the WebIDLParser.js "typedef"2767 * production.2768 */2769 /** Self-explanatory. */2770 this.name = obj.name;2771 /** The idlType that we are supposed to be typedeffing to. */2772 this.idlType = obj.idlType;2773}2774IdlTypedef.prototype = Object.create(IdlObject.prototype);2775/// IdlNamespace ///2776function IdlNamespace(obj)2777{2778 this.name = obj.name;2779 this.extAttrs = obj.extAttrs;2780 this.untested = obj.untested;2781 /** A back-reference to our IdlArray. */2782 this.array = obj.array;2783 /** An array of IdlInterfaceMembers. */2784 this.members = obj.members.map(m => new IdlInterfaceMember(m));2785}2786IdlNamespace.prototype = Object.create(IdlObject.prototype);2787IdlNamespace.prototype.do_member_operation_asserts = function (memberHolderObject, member, a_test)2788{2789 var desc = Object.getOwnPropertyDescriptor(memberHolderObject, member.name);2790 assert_false("get" in desc, "property should not have a getter");2791 assert_false("set" in desc, "property should not have a setter");2792 assert_equals(2793 desc.writable,2794 !member.isUnforgeable,2795 "property should be writable if and only if not unforgeable");2796 assert_true(desc.enumerable, "property should be enumerable");2797 assert_equals(2798 desc.configurable,2799 !member.isUnforgeable,2800 "property should be configurable if and only if not unforgeable");2801 assert_equals(2802 typeof memberHolderObject[member.name],2803 "function",2804 "property must be a function");2805 assert_equals(2806 memberHolderObject[member.name].length,2807 minOverloadLength(this.members.filter(function(m) {2808 return m.type == "operation" && m.name == member.name;2809 })),2810 "operation has wrong .length");2811 a_test.done();2812}2813IdlNamespace.prototype.test_member_operation = function(member)2814{2815 if (!shouldRunSubTest(this.name)) {2816 return;2817 }2818 var args = member.arguments.map(function(a) {2819 var s = a.idlType.idlType;2820 if (a.variadic) {2821 s += '...';2822 }2823 return s;2824 }).join(", ");2825 var a_test = subsetTestByKey(2826 this.name,2827 async_test,2828 this.name + ' namespace: operation ' + member.name + '(' + args + ')');2829 a_test.step(function() {2830 assert_own_property(2831 self[this.name],2832 member.name,2833 'namespace object missing operation ' + format_value(member.name));2834 this.do_member_operation_asserts(self[this.name], member, a_test);2835 }.bind(this));2836};2837IdlNamespace.prototype.test_member_attribute = function (member)2838{2839 if (!shouldRunSubTest(this.name)) {2840 return;2841 }2842 var a_test = subsetTestByKey(2843 this.name,2844 async_test,2845 this.name + ' namespace: attribute ' + member.name);2846 a_test.step(function()2847 {2848 assert_own_property(2849 self[this.name],2850 member.name,2851 this.name + ' does not have property ' + format_value(member.name));2852 var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name);2853 assert_equals(desc.set, undefined, "setter must be undefined for namespace members");2854 a_test.done();2855 }.bind(this));2856};2857IdlNamespace.prototype.test = function ()2858{2859 /**2860 * TODO(lukebjerring): Assert:2861 * - "Note that unlike interfaces or dictionaries, namespaces do not create types."2862 * - "Of the extended attributes defined in this specification, only the2863 * [Exposed] and [SecureContext] extended attributes are applicable to namespaces."2864 * - "Namespaces must be annotated with the [Exposed] extended attribute."2865 */2866 for (const v of Object.values(this.members)) {2867 switch (v.type) {2868 case 'operation':2869 this.test_member_operation(v);2870 break;2871 case 'attribute':2872 this.test_member_attribute(v);2873 break;2874 default:2875 throw 'Invalid namespace member ' + v.name + ': ' + v.type + ' not supported';2876 }2877 };2878};2879}());2880/**2881 * idl_test is a promise_test wrapper that handles the fetching of the IDL,2882 * avoiding repetitive boilerplate.2883 *2884 * @param {String|String[]} srcs Spec name(s) for source idl files (fetched from2885 * /interfaces/{name}.idl).2886 * @param {String|String[]} deps Spec name(s) for dependency idl files (fetched2887 * from /interfaces/{name}.idl). Order is important - dependencies from2888 * each source will only be included if they're already know to be a2889 * dependency (i.e. have already been seen).2890 * @param {Function} setup_func Function for extra setup of the idl_array, such2891 * as adding objects. Do not call idl_array.test() in the setup; it is2892 * called by this function (idl_test).2893 */2894function idl_test(srcs, deps, idl_setup_func) {2895 return promise_test(function (t) {2896 var idl_array = new IdlArray();2897 srcs = (srcs instanceof Array) ? srcs : [srcs] || [];2898 deps = (deps instanceof Array) ? deps : [deps] || [];2899 var setup_error = null;2900 return Promise.all(2901 srcs.concat(deps).map(function(spec) {2902 return fetch_spec(spec);2903 }))2904 .then(function(idls) {2905 for (var i = 0; i < srcs.length; i++) {2906 idl_array.add_idls(idls[i]);2907 }2908 for (var i = srcs.length; i < srcs.length + deps.length; i++) {2909 idl_array.add_dependency_idls(idls[i]);2910 }2911 })2912 .then(function() {2913 if (idl_setup_func) {2914 return idl_setup_func(idl_array, t);2915 }2916 })2917 .catch(function(e) { setup_error = e || 'IDL setup failed.'; })2918 .then(function () {2919 var error = setup_error;2920 try {2921 idl_array.test(); // Test what we can.2922 } catch (e) {2923 // If testing fails hard here, the original setup error2924 // is more likely to be the real cause.2925 error = error || e;2926 }2927 if (error) {2928 throw error;2929 }2930 });2931 }, 'idl_test setup');2932}2933/**2934 * fetch_spec is a shorthand for a Promise that fetches the spec's content.2935 */2936function fetch_spec(spec) {2937 var url = '/interfaces/' + spec + '.idl';2938 return fetch(url).then(function (r) {2939 if (!r.ok) {2940 throw new IdlHarnessError("Error fetching " + url + ".");2941 }2942 return r.text();2943 });2944}...
aflprep_idlharness.js
Source:aflprep_idlharness.js
1 * Notes for people who want to edit this file (not just use it as a library):2 *3 * Most of the interesting stuff happens in the derived classes of IdlObject,4 * especially IdlInterface. The entry point for all IdlObjects is .test(),5 * which is called by IdlArray.test(). An IdlObject is conceptually just6 * "thing we want to run tests on", and an IdlArray is an array of IdlObjects7 * with some additional data thrown in.8 *9 * The object model is based on what WebIDLParser.js produces, which is in turn10 * based on its pegjs grammar. If you want to figure out what properties an11 * object will have from WebIDLParser.js, the best way is to look at the12 * grammar:13 *14 *15 * So for instance:16 *17 * interface18 * = extAttrs:extendedAttributeList? S? "interface" S name:identifier w herit:ifInheritance? w "{" w mem:ifMember* w "}" w ";" w19 * { return { type: "interface", name: name, inheritance: herit, members: mem, extAttrs: extAttrs }; }20 *21 * This means that an "interface" object will have a .type property equal to22 * the string "interface", a .name property equal to the identifier that the23 * parser found, an .inheritance property equal to either null or the result of24 * the "ifInheritance" production found elsewhere in the grammar, and so on.25 * After each grammatical production is a JavaScript function in curly braces26 * that gets called with suitable arguments and returns some JavaScript value.27 *28 * (Note that the version of WebIDLParser.js we use might sometimes be29 * out-of-date or forked.)30 *31 * The members and methods of the classes defined by this file are all at least32 * briefly documented, hopefully.33(function(){34"use strict";35if (!('subsetTestByKey' in self)) {36 self.subsetTestByKey = function(key, callback, ...args) {37 return callback(...args);38 }39 self.shouldRunSubTest = () => true;40}41function constValue (cnt)42{43 if (cnt.type === "null") return null;44 if (cnt.type === "NaN") return NaN;45 if (cnt.type === "Infinity") return cnt.negative ? -Infinity : Infinity;46 if (cnt.type === "number") return +cnt.value;47 return cnt.value;48}49function minOverloadLength(overloads)50{51 if (!overloads.length) {52 return 0;53 }54 return overloads.map(function(attr) {55 return attr.arguments ? attr.arguments.filter(function(arg) {56 return !arg.optional && !arg.variadic;57 }).length : 0;58 })59 .reduce(function(m, n) { return Math.min(m, n); });60}61function globalOf(func)62{63 try {64 return func.constructor("return this;")();65 } catch (e) {66 }67 return self;68}69function isConstructor(o) {70 try {71 new (new Proxy(o, {construct: () => ({})}));72 return true;73 } catch(e) {74 return false;75 }76}77function throwOrReject(a_test, operation, fn, obj, args, message, cb)78{79 if (operation.idlType.generic !== "Promise") {80 assert_throws_js(globalOf(fn).TypeError, function() {81 fn.apply(obj, args);82 }, message);83 cb();84 } else {85 try {86 promise_rejects_js(a_test, TypeError, fn.apply(obj, args), message).then(cb, cb);87 } catch (e){88 a_test.step(function() {89 assert_unreached("Throws \"" + e + "\" instead of rejecting promise");90 cb();91 });92 }93 }94}95function awaitNCallbacks(n, cb, ctx)96{97 var counter = 0;98 return function() {99 counter++;100 if (counter >= n) {101 cb();102 }103 };104}105self.IdlHarnessError = function(message)106{107 * Message to be printed as the error's toString invocation.108 this.message = message;109};110IdlHarnessError.prototype = Object.create(Error.prototype);111IdlHarnessError.prototype.toString = function()112{113 return this.message;114};115self.IdlArray = function()116{117 * A map from strings to the corresponding named IdlObject, such as118 * IdlInterface or IdlException. These are the things that test() will run119 * tests on.120 this.members = {};121 * A map from strings to arrays of strings. The keys are interface or122 * exception names, and are expected to also exist as keys in this.members123 * (otherwise they'll be ignored). This is populated by add_objects() --124 * see documentation at the start of the file. The actual tests will be125 * run by calling this.members[name].test_object(obj) for each obj in126 * this.objects[name]. obj is a string that will be eval'd to produce a127 * JavaScript value, which is supposed to be an object implementing the128 * given IdlObject (interface, exception, etc.).129 this.objects = {};130 * When adding multiple collections of IDLs one at a time, an earlier one131 * might contain a partial interface or includes statement that depends132 * on a later one. Save these up and handle them right before we run133 * tests.134 *135 * Both this.partials and this.includes will be the objects as parsed by136 * WebIDLParser.js, not wrapped in IdlInterface or similar.137 this.partials = [];138 this.includes = [];139 * Record of skipped IDL items, in case we later realize that they are a140 * dependency (to retroactively process them).141 this.skipped = new Map();142};143IdlArray.prototype.add_idls = function(raw_idls, options)144{145 this.internal_add_idls(WebIDL2.parse(raw_idls), options);146};147IdlArray.prototype.add_untested_idls = function(raw_idls, options)148{149 var parsed_idls = WebIDL2.parse(raw_idls);150 this.mark_as_untested(parsed_idls);151 this.internal_add_idls(parsed_idls, options);152};153IdlArray.prototype.mark_as_untested = function (parsed_idls)154{155 for (var i = 0; i < parsed_idls.length; i++) {156 parsed_idls[i].untested = true;157 if ("members" in parsed_idls[i]) {158 for (var j = 0; j < parsed_idls[i].members.length; j++) {159 parsed_idls[i].members[j].untested = true;160 }161 }162 }163};164IdlArray.prototype.is_excluded_by_options = function (name, options)165{166 return options &&167 (options.except && options.except.includes(name)168 || options.only && !options.only.includes(name));169};170IdlArray.prototype.add_dependency_idls = function(raw_idls, options)171{172 return this.internal_add_dependency_idls(WebIDL2.parse(raw_idls), options);173};174IdlArray.prototype.internal_add_dependency_idls = function(parsed_idls, options)175{176 const new_options = { only: [] }177 const all_deps = new Set();178 Object.values(this.members).forEach(v => {179 if (v.base) {180 all_deps.add(v.base);181 }182 });183 this.includes.forEach(i => {184 all_deps.add(i.target);185 all_deps.add(i.includes);186 });187 this.partials.forEach(p => all_deps.add(p.name));188 Object.entries(this.members).forEach(([k, v]) => {189 if (v instanceof IdlTypedef) {190 let defs = v.idlType.union191 ? v.idlType.idlType.map(t => t.idlType)192 : [v.idlType.idlType];193 defs.forEach(d => all_deps.add(d));194 }195 });196 const attrDeps = parsedIdls => {197 return parsedIdls.reduce((deps, parsed) => {198 if (parsed.members) {199 for (const attr of Object.values(parsed.members).filter(m => m.type === 'attribute')) {200 let attrType = attr.idlType;201 if (attrType.generic) {202 deps.add(attrType.generic);203 attrType = attrType.idlType;204 }205 deps.add(attrType.idlType);206 }207 }208 if (parsed.base in this.members) {209 attrDeps([this.members[parsed.base]]).forEach(dep => deps.add(dep));210 }211 return deps;212 }, new Set());213 };214 const testedMembers = Object.values(this.members).filter(m => !m.untested && m.members);215 attrDeps(testedMembers).forEach(dep => all_deps.add(dep));216 const testedPartials = this.partials.filter(m => !m.untested && m.members);217 attrDeps(testedPartials).forEach(dep => all_deps.add(dep));218 if (options && options.except && options.only) {219 throw new IdlHarnessError("The only and except options can't be used together.");220 }221 const defined_or_untested = name => {222 return name in this.members223 || this.is_excluded_by_options(name, options);224 }225 const process = function(parsed) {226 var deps = [];227 if (parsed.name) {228 deps.push(parsed.name);229 } else if (parsed.type === "includes") {230 deps.push(parsed.target);231 deps.push(parsed.includes);232 }233 deps = deps.filter(function(name) {234 if (!name235 || name === parsed.name && defined_or_untested(name)236 || !all_deps.has(name)) {237 if (name && !(name in this.members)) {238 this.skipped.has(name)239 ? this.skipped.get(name).push(parsed)240 : this.skipped.set(name, [parsed]);241 }242 return false;243 }244 return true;245 }.bind(this));246 deps.forEach(function(name) {247 if (!new_options.only.includes(name)) {248 new_options.only.push(name);249 }250 const follow_up = new Set();251 for (const dep_type of ["inheritance", "includes"]) {252 if (parsed[dep_type]) {253 const inheriting = parsed[dep_type];254 const inheritor = parsed.name || parsed.target;255 const deps = [inheriting];256 if (dep_type !== "includes"257 || inheriting in this.members && !this.members[inheriting].untested258 || this.partials.some(function(p) {259 return p.name === inheriting;260 })) {261 deps.push(inheritor);262 }263 for (const dep of deps) {264 if (!new_options.only.includes(dep)) {265 new_options.only.push(dep);266 }267 all_deps.add(dep);268 follow_up.add(dep);269 }270 }271 }272 for (const deferred of follow_up) {273 if (this.skipped.has(deferred)) {274 const next = this.skipped.get(deferred);275 this.skipped.delete(deferred);276 next.forEach(process);277 }278 }279 }.bind(this));280 }.bind(this);281 for (let parsed of parsed_idls) {282 process(parsed);283 }284 this.mark_as_untested(parsed_idls);285 if (new_options.only.length) {286 this.internal_add_idls(parsed_idls, new_options);287 }288}289IdlArray.prototype.internal_add_idls = function(parsed_idls, options)290{291 * Internal helper called by add_idls() and add_untested_idls().292 *293 * parsed_idls is an array of objects that come from WebIDLParser.js's294 * "definitions" production. The add_untested_idls() entry point295 * additionally sets an .untested property on each object (and its296 * .members) so that they'll be skipped by test() -- they'll only be297 * used for base interfaces of tested interfaces, return types, etc.298 *299 * options is a dictionary that can have an only or except member which are300 * arrays. If only is given then only members, partials and interface301 * targets listed will be added, and if except is given only those that302 * aren't listed will be added. Only one of only and except can be used.303 if (options && options.only && options.except)304 {305 throw new IdlHarnessError("The only and except options can't be used together.");306 }307 var should_skip = name => {308 return this.is_excluded_by_options(name, options);309 }310 parsed_idls.forEach(function(parsed_idl)311 {312 var partial_types = [313 "interface",314 "interface mixin",315 "dictionary",316 "namespace",317 ];318 if (parsed_idl.partial && partial_types.includes(parsed_idl.type))319 {320 if (should_skip(parsed_idl.name))321 {322 return;323 }324 this.partials.push(parsed_idl);325 return;326 }327 if (parsed_idl.type == "includes")328 {329 if (should_skip(parsed_idl.target))330 {331 return;332 }333 this.includes.push(parsed_idl);334 return;335 }336 parsed_idl.array = this;337 if (should_skip(parsed_idl.name))338 {339 return;340 }341 if (parsed_idl.name in this.members)342 {343 throw new IdlHarnessError("Duplicate identifier " + parsed_idl.name);344 }345 switch(parsed_idl.type)346 {347 case "interface":348 this.members[parsed_idl.name] =349 break;350 case "interface mixin":351 this.members[parsed_idl.name] =352 break;353 case "dictionary":354 this.members[parsed_idl.name] = new IdlDictionary(parsed_idl);355 break;356 case "typedef":357 this.members[parsed_idl.name] = new IdlTypedef(parsed_idl);358 break;359 case "callback":360 this.members[parsed_idl.name] = new IdlCallback(parsed_idl);361 break;362 case "enum":363 this.members[parsed_idl.name] = new IdlEnum(parsed_idl);364 break;365 case "callback interface":366 this.members[parsed_idl.name] =367 break;368 case "namespace":369 this.members[parsed_idl.name] = new IdlNamespace(parsed_idl);370 break;371 default:372 throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported";373 }374 }.bind(this));375};376IdlArray.prototype.add_objects = function(dict)377{378 for (var k in dict)379 {380 if (k in this.objects)381 {382 this.objects[k] = this.objects[k].concat(dict[k]);383 }384 else385 {386 this.objects[k] = dict[k];387 }388 }389};390IdlArray.prototype.prevent_multiple_testing = function(name)391{392 this.members[name].prevent_multiple_testing = true;393};394IdlArray.prototype.is_json_type = function(type)395{396 * Checks whether type is a JSON type as per397 var idlType = type.idlType;398 if (type.generic == "Promise") { return false; }399 if (type.union || type.generic == "record") {400 return idlType.every(this.is_json_type, this);401 }402 if (type.generic == "sequence" || type.generic == "FrozenArray") {403 return this.is_json_type(idlType[0]);404 }405 if (typeof idlType != "string") { throw new Error("Unexpected type " + JSON.stringify(idlType)); }406 switch (idlType)407 {408 case "byte":409 case "octet":410 case "short":411 case "unsigned short":412 case "long":413 case "unsigned long":414 case "long long":415 case "unsigned long long":416 case "float":417 case "double":418 case "unrestricted float":419 case "unrestricted double":420 case "boolean":421 case "DOMString":422 case "ByteString":423 case "USVString":424 case "object":425 return true;426 case "Error":427 case "DOMException":428 case "Int8Array":429 case "Int16Array":430 case "Int32Array":431 case "Uint8Array":432 case "Uint16Array":433 case "Uint32Array":434 case "Uint8ClampedArray":435 case "BigInt64Array":436 case "BigUint64Array":437 case "Float32Array":438 case "Float64Array":439 case "ArrayBuffer":440 case "DataView":441 case "any":442 return false;443 default:444 var thing = this.members[idlType];445 if (!thing) { throw new Error("Type " + idlType + " not found"); }446 if (thing instanceof IdlEnum) { return true; }447 if (thing instanceof IdlTypedef) {448 return this.is_json_type(thing.idlType);449 }450 if (thing instanceof IdlDictionary) {451 const map = new Map();452 for (const dict of thing.get_reverse_inheritance_stack()) {453 for (const m of dict.members) {454 map.set(m.name, m.idlType);455 }456 }457 return Array.from(map.values()).every(this.is_json_type, this);458 }459 if (thing instanceof IdlInterface) {460 var base;461 while (thing)462 {463 if (thing.has_to_json_regular_operation()) { return true; }464 var mixins = this.includes[thing.name];465 if (mixins) {466 mixins = mixins.map(function(id) {467 var mixin = this.members[id];468 if (!mixin) {469 throw new Error("Interface " + id + " not found (implemented by " + thing.name + ")");470 }471 return mixin;472 }, this);473 if (mixins.some(function(m) { return m.has_to_json_regular_operation() } )) { return true; }474 }475 if (!thing.base) { return false; }476 base = this.members[thing.base];477 if (!base) {478 throw new Error("Interface " + thing.base + " not found (inherited by " + thing.name + ")");479 }480 thing = base;481 }482 return false;483 }484 return false;485 }486};487function exposure_set(object, default_set) {488 var exposed = object.extAttrs && object.extAttrs.filter(a => a.name === "Exposed");489 if (exposed && exposed.length > 1) {490 throw new IdlHarnessError(491 `Multiple 'Exposed' extended attributes on ${object.name}`);492 }493 let result = default_set || ["Window"];494 if (result && !(result instanceof Set)) {495 result = new Set(result);496 }497 if (exposed && exposed.length) {498 const { rhs } = exposed[0];499 const set = rhs.type === "identifier-list" ?500 rhs.value.map(id => id.value) :501 [ rhs.value ];502 result = new Set(set);503 }504 if (result && result.has("Worker")) {505 result.delete("Worker");506 result.add("DedicatedWorker");507 result.add("ServiceWorker");508 result.add("SharedWorker");509 }510 return result;511}512function exposed_in(globals) {513 if ('Window' in self) {514 return globals.has("Window");515 }516 if ('DedicatedWorkerGlobalScope' in self &&517 self instanceof DedicatedWorkerGlobalScope) {518 return globals.has("DedicatedWorker");519 }520 if ('SharedWorkerGlobalScope' in self &&521 self instanceof SharedWorkerGlobalScope) {522 return globals.has("SharedWorker");523 }524 if ('ServiceWorkerGlobalScope' in self &&525 self instanceof ServiceWorkerGlobalScope) {526 return globals.has("ServiceWorker");527 }528 throw new IdlHarnessError("Unexpected global object");529}530 * Asserts that the given error message is thrown for the given function.531 * @param {string|IdlHarnessError} error Expected Error message.532 * @param {Function} idlArrayFunc Function operating on an IdlArray that should throw.533IdlArray.prototype.assert_throws = function(error, idlArrayFunc)534{535 try {536 idlArrayFunc.call(this, this);537 } catch (e) {538 if (e instanceof AssertionError) {539 throw e;540 }541 if (error instanceof IdlHarnessError) {542 error = error.message;543 }544 if (e.message !== error) {545 throw new IdlHarnessError(`${idlArrayFunc} threw "${e}", not the expected IdlHarnessError "${error}"`);546 }547 return;548 }549 throw new IdlHarnessError(`${idlArrayFunc} did not throw the expected IdlHarnessError`);550}551IdlArray.prototype.test = function()552{553 this.merge_partials();554 this.merge_mixins();555 for (const member of Object.values(this.members).filter(m => m.base)) {556 const lhs = member.name;557 const rhs = member.base;558 if (!(rhs in this.members)) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${rhs} is undefined.`);559 const lhs_is_interface = this.members[lhs] instanceof IdlInterface;560 const rhs_is_interface = this.members[rhs] instanceof IdlInterface;561 if (rhs_is_interface != lhs_is_interface) {562 if (!lhs_is_interface) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${lhs} is not an interface.`);563 if (!rhs_is_interface) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${rhs} is not an interface.`);564 }565 member.get_reverse_inheritance_stack();566 }567 Object.getOwnPropertyNames(this.members).forEach(function(memberName) {568 var member = this.members[memberName];569 if (!(member instanceof IdlInterface)) {570 return;571 }572 var globals = exposure_set(member);573 member.exposed = exposed_in(globals);574 member.exposureSet = globals;575 }.bind(this));576 for (var name in this.members)577 {578 this.members[name].test();579 if (name in this.objects)580 {581 const objects = this.objects[name];582 if (!objects || !Array.isArray(objects)) {583 throw new IdlHarnessError(`Invalid or empty objects for member ${name}`);584 }585 objects.forEach(function(str)586 {587 if (!this.members[name] || !(this.members[name] instanceof IdlInterface)) {588 throw new IdlHarnessError(`Invalid object member name ${name}`);589 }590 this.members[name].test_object(str);591 }.bind(this));592 }593 }594};595IdlArray.prototype.merge_partials = function()596{597 const testedPartials = new Map();598 this.partials.forEach(function(parsed_idl)599 {600 const originalExists = parsed_idl.name in this.members601 && (this.members[parsed_idl.name] instanceof IdlInterface602 || this.members[parsed_idl.name] instanceof IdlDictionary603 || this.members[parsed_idl.name] instanceof IdlNamespace);604 let partialTestName = parsed_idl.name;605 let partialTestCount = 1;606 if (testedPartials.has(parsed_idl.name)) {607 partialTestCount += testedPartials.get(parsed_idl.name);608 partialTestName = `${partialTestName}[${partialTestCount}]`;609 }610 testedPartials.set(parsed_idl.name, partialTestCount);611 if (!parsed_idl.untested) {612 test(function () {613 assert_true(originalExists, `Original ${parsed_idl.type} should be defined`);614 var expected;615 switch (parsed_idl.type) {616 case 'dictionary': expected = IdlDictionary; break;617 case 'namespace': expected = IdlNamespace; break;618 case 'interface':619 case 'interface mixin':620 default:621 expected = IdlInterface; break;622 }623 assert_true(624 expected.prototype.isPrototypeOf(this.members[parsed_idl.name]),625 `Original ${parsed_idl.name} definition should have type ${parsed_idl.type}`);626 }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: original ${parsed_idl.type} defined`);627 }628 if (!originalExists) {629 return;630 }631 if (parsed_idl.extAttrs)632 {633 const exposureAttr = parsed_idl.extAttrs.find(a => a.name === "Exposed");634 if (exposureAttr) {635 if (!parsed_idl.untested) {636 test(function () {637 const partialExposure = exposure_set(parsed_idl);638 const memberExposure = exposure_set(this.members[parsed_idl.name]);639 partialExposure.forEach(name => {640 if (!memberExposure || !memberExposure.has(name)) {641 throw new IdlHarnessError(642 `Partial ${parsed_idl.name} ${parsed_idl.type} is exposed to '${name}', the original ${parsed_idl.type} is not.`);643 }644 });645 }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: valid exposure set`);646 }647 parsed_idl.members.forEach(function (member) {648 member.extAttrs.push(exposureAttr);649 }.bind(this));650 }651 parsed_idl.extAttrs.forEach(function(extAttr)652 {653 if (extAttr.name === "Exposed") {654 return;655 }656 this.members[parsed_idl.name].extAttrs.push(extAttr);657 }.bind(this));658 }659 if (parsed_idl.members.length) {660 test(function () {661 var clash = parsed_idl.members.find(function(member) {662 return this.members[parsed_idl.name].members.find(function(m) {663 return this.are_duplicate_members(m, member);664 }.bind(this));665 }.bind(this));666 parsed_idl.members.forEach(function(member)667 {668 this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member));669 }.bind(this));670 assert_true(!clash, "member " + (clash && clash.name) + " is unique");671 }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: member names are unique`);672 }673 }.bind(this));674 this.partials = [];675}676IdlArray.prototype.merge_mixins = function()677{678 for (const parsed_idl of this.includes)679 {680 const lhs = parsed_idl.target;681 const rhs = parsed_idl.includes;682 var errStr = lhs + " includes " + rhs + ", but ";683 if (!(lhs in this.members)) throw errStr + lhs + " is undefined.";684 if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface.";685 if (!(rhs in this.members)) throw errStr + rhs + " is undefined.";686 if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface.";687 if (this.members[rhs].members.length) {688 test(function () {689 var clash = this.members[rhs].members.find(function(member) {690 return this.members[lhs].members.find(function(m) {691 return this.are_duplicate_members(m, member);692 }.bind(this));693 }.bind(this));694 this.members[rhs].members.forEach(function(member) {695 assert_true(696 this.members[lhs].members.every(m => !this.are_duplicate_members(m, member)),697 "member " + member.name + " is unique");698 this.members[lhs].members.push(new IdlInterfaceMember(member));699 }.bind(this));700 assert_true(!clash, "member " + (clash && clash.name) + " is unique");701 }.bind(this), lhs + " includes " + rhs + ": member names are unique");702 }703 }704 this.includes = [];705}706IdlArray.prototype.are_duplicate_members = function(m1, m2) {707 if (m1.name !== m2.name) {708 return false;709 }710 if (m1.type === 'operation' && m2.type === 'operation'711 && m1.arguments.length !== m2.arguments.length) {712 return false;713 }714 return true;715}716IdlArray.prototype.assert_type_is = function(value, type)717{718 if (type.idlType in this.members719 && this.members[type.idlType] instanceof IdlTypedef) {720 this.assert_type_is(value, this.members[type.idlType].idlType);721 return;722 }723 if (type.nullable && value === null)724 {725 return;726 }727 if (type.union) {728 for (var i = 0; i < type.idlType.length; i++) {729 try {730 this.assert_type_is(value, type.idlType[i]);731 return;732 } catch(e) {733 if (e instanceof AssertionError) {734 continue;735 }736 throw e;737 }738 }739 assert_true(false, "Attribute has value " + format_value(value)740 + " which doesn't match any of the types in the union");741 }742 * Helper function that tests that value is an instance of type according743 * to the rules of WebIDL. value is any JavaScript value, and type is an744 * object produced by WebIDLParser.js' "type" production. That production745 * is fairly elaborate due to the complexity of WebIDL's types, so it's746 * best to look at the grammar to figure out what properties it might have.747 if (type.idlType == "any")748 {749 return;750 }751 if (type.array)752 {753 return;754 }755 if (type.generic === "sequence")756 {757 assert_true(Array.isArray(value), "should be an Array");758 if (!value.length)759 {760 return;761 }762 this.assert_type_is(value[0], type.idlType[0]);763 return;764 }765 if (type.generic === "Promise") {766 assert_true("then" in value, "Attribute with a Promise type should have a then property");767 return;768 }769 if (type.generic === "FrozenArray") {770 assert_true(Array.isArray(value), "Value should be array");771 assert_true(Object.isFrozen(value), "Value should be frozen");772 if (!value.length)773 {774 return;775 }776 this.assert_type_is(value[0], type.idlType[0]);777 return;778 }779 type = Array.isArray(type.idlType) ? type.idlType[0] : type.idlType;780 switch(type)781 {782 case "undefined":783 assert_equals(value, undefined);784 return;785 case "boolean":786 assert_equals(typeof value, "boolean");787 return;788 case "byte":789 assert_equals(typeof value, "number");790 assert_equals(value, Math.floor(value), "should be an integer");791 assert_true(-128 <= value && value <= 127, "byte " + value + " should be in range [-128, 127]");792 return;793 case "octet":794 assert_equals(typeof value, "number");795 assert_equals(value, Math.floor(value), "should be an integer");796 assert_true(0 <= value && value <= 255, "octet " + value + " should be in range [0, 255]");797 return;798 case "short":799 assert_equals(typeof value, "number");800 assert_equals(value, Math.floor(value), "should be an integer");801 assert_true(-32768 <= value && value <= 32767, "short " + value + " should be in range [-32768, 32767]");802 return;803 case "unsigned short":804 assert_equals(typeof value, "number");805 assert_equals(value, Math.floor(value), "should be an integer");806 assert_true(0 <= value && value <= 65535, "unsigned short " + value + " should be in range [0, 65535]");807 return;808 case "long":809 assert_equals(typeof value, "number");810 assert_equals(value, Math.floor(value), "should be an integer");811 assert_true(-2147483648 <= value && value <= 2147483647, "long " + value + " should be in range [-2147483648, 2147483647]");812 return;813 case "unsigned long":814 assert_equals(typeof value, "number");815 assert_equals(value, Math.floor(value), "should be an integer");816 assert_true(0 <= value && value <= 4294967295, "unsigned long " + value + " should be in range [0, 4294967295]");817 return;818 case "long long":819 assert_equals(typeof value, "number");820 return;821 case "unsigned long long":822 case "DOMTimeStamp":823 assert_equals(typeof value, "number");824 assert_true(0 <= value, "unsigned long long should be positive");825 return;826 case "float":827 assert_equals(typeof value, "number");828 assert_equals(value, Math.fround(value), "float rounded to 32-bit float should be itself");829 assert_not_equals(value, Infinity);830 assert_not_equals(value, -Infinity);831 assert_not_equals(value, NaN);832 return;833 case "DOMHighResTimeStamp":834 case "double":835 assert_equals(typeof value, "number");836 assert_not_equals(value, Infinity);837 assert_not_equals(value, -Infinity);838 assert_not_equals(value, NaN);839 return;840 case "unrestricted float":841 assert_equals(typeof value, "number");842 assert_equals(value, Math.fround(value), "unrestricted float rounded to 32-bit float should be itself");843 return;844 case "unrestricted double":845 assert_equals(typeof value, "number");846 return;847 case "DOMString":848 assert_equals(typeof value, "string");849 return;850 case "ByteString":851 assert_equals(typeof value, "string");852 return;853 case "USVString":854 assert_equals(typeof value, "string");855 return;856 case "ArrayBufferView":857 assert_true(ArrayBuffer.isView(value));858 return;859 case "object":860 assert_in_array(typeof value, ["object", "function"], "wrong type: not object or function");861 return;862 }863 if (!(type in this.members))864 {865 assert_true(value instanceof self[type], "wrong type: not a " + type);866 return;867 }868 if (this.members[type] instanceof IdlInterface)869 {870 assert_in_array(typeof value, ["object", "function"], "wrong type: not object or function");871 if (value instanceof Object872 && !this.members[type].has_extended_attribute("LegacyNoInterfaceObject")873 && type in self)874 {875 assert_true(value instanceof self[type], "instanceof " + type);876 }877 }878 else if (this.members[type] instanceof IdlEnum)879 {880 assert_equals(typeof value, "string");881 }882 else if (this.members[type] instanceof IdlDictionary)883 {884 }885 else if (this.members[type] instanceof IdlCallback)886 {887 assert_equals(typeof value, "function");888 }889 else890 {891 throw new IdlHarnessError("Type " + type + " isn't an interface, callback or dictionary");892 }893};894function IdlObject() {}895IdlObject.prototype.test = function()896{897 * By default, this does nothing, so no actual tests are run for IdlObjects898 * that don't define any (e.g., IdlDictionary at the time of this writing).899};900IdlObject.prototype.has_extended_attribute = function(name)901{902 * This is only meaningful for things that support extended attributes,903 * such as interfaces, exceptions, and members.904 return this.extAttrs.some(function(o)905 {906 return o.name == name;907 });908};909function IdlDictionary(obj)910{911 * obj is an object produced by the WebIDLParser.js "dictionary"912 * production.913 this.name = obj.name;914 this.array = obj.array;915 this.members = obj.members;916 * The name (as a string) of the dictionary type we inherit from, or null917 * if there is none.918 this.base = obj.inheritance;919}920IdlDictionary.prototype = Object.create(IdlObject.prototype);921IdlDictionary.prototype.get_reverse_inheritance_stack = function() {922 return IdlInterface.prototype.get_reverse_inheritance_stack.call(this);923};924function IdlInterface(obj, is_callback, is_mixin)925{926 * obj is an object produced by the WebIDLParser.js "interface" production.927 this.name = obj.name;928 this.array = obj.array;929 * An indicator of whether we should run tests on the interface object and930 * interface prototype object. Tests on members are controlled by .untested931 * on each member, not this.932 this.untested = obj.untested;933 this.extAttrs = obj.extAttrs;934 this.members = obj.members.map(function(m){return new IdlInterfaceMember(m); });935 if (this.has_extended_attribute("LegacyUnforgeable")) {936 this.members937 .filter(function(m) { return m.special !== "static" && (m.type == "attribute" || m.type == "operation"); })938 .forEach(function(m) { return m.isUnforgeable = true; });939 }940 * The name (as a string) of the type we inherit from, or null if there is941 * none.942 this.base = obj.inheritance;943 this._is_callback = is_callback;944 this._is_mixin = is_mixin;945}946IdlInterface.prototype = Object.create(IdlObject.prototype);947IdlInterface.prototype.is_callback = function()948{949 return this._is_callback;950};951IdlInterface.prototype.is_mixin = function()952{953 return this._is_mixin;954};955IdlInterface.prototype.has_constants = function()956{957 return this.members.some(function(member) {958 return member.type === "const";959 });960};961IdlInterface.prototype.get_unscopables = function()962{963 return this.members.filter(function(member) {964 return member.isUnscopable;965 });966};967IdlInterface.prototype.is_global = function()968{969 return this.extAttrs.some(function(attribute) {970 return attribute.name === "Global";971 });972};973 * Value of the LegacyNamespace extended attribute, if any.974 *975IdlInterface.prototype.get_legacy_namespace = function()976{977 var legacyNamespace = this.extAttrs.find(function(attribute) {978 return attribute.name === "LegacyNamespace";979 });980 return legacyNamespace ? legacyNamespace.rhs.value : undefined;981};982IdlInterface.prototype.get_interface_object_owner = function()983{984 var legacyNamespace = this.get_legacy_namespace();985 return legacyNamespace ? self[legacyNamespace] : self;986};987IdlInterface.prototype.should_have_interface_object = function()988{989 return this.is_callback() ? this.has_constants() : !this.has_extended_attribute("LegacyNoInterfaceObject");990};991IdlInterface.prototype.assert_interface_object_exists = function()992{993 var owner = this.get_legacy_namespace() || "self";994 assert_own_property(self[owner], this.name, owner + " does not have own property " + format_value(this.name));995};996IdlInterface.prototype.get_interface_object = function() {997 if (!this.should_have_interface_object()) {998 var reason = this.is_callback() ? "lack of declared constants" : "declared [LegacyNoInterfaceObject] attribute";999 throw new IdlHarnessError(this.name + " has no interface object due to " + reason);1000 }1001 return this.get_interface_object_owner()[this.name];1002};1003IdlInterface.prototype.get_qualified_name = function() {1004 var legacyNamespace = this.get_legacy_namespace();1005 if (legacyNamespace) {1006 return legacyNamespace + "." + this.name;1007 }1008 return this.name;1009};1010IdlInterface.prototype.has_to_json_regular_operation = function() {1011 return this.members.some(function(m) {1012 return m.is_to_json_regular_operation();1013 });1014};1015IdlInterface.prototype.has_default_to_json_regular_operation = function() {1016 return this.members.some(function(m) {1017 return m.is_to_json_regular_operation() && m.has_extended_attribute("Default");1018 });1019};1020 * with the order reversed.1021 *1022 * The order is reversed so that the base class comes first in the list, because1023 * this is what all call sites need.1024 *1025 * So given:1026 *1027 * A : B {};1028 * B : C {};1029 * C {};1030 *1031 * then A.get_reverse_inheritance_stack() returns [C, B, A],1032 * and B.get_reverse_inheritance_stack() returns [C, B].1033 *1034 * Note: as dictionary inheritance is expressed identically by the AST,1035 * this works just as well for getting a stack of inherited dictionaries.1036IdlInterface.prototype.get_reverse_inheritance_stack = function() {1037 const stack = [this];1038 let idl_interface = this;1039 while (idl_interface.base) {1040 const base = this.array.members[idl_interface.base];1041 if (!base) {1042 throw new Error(idl_interface.type + " " + idl_interface.base + " not found (inherited by " + idl_interface.name + ")");1043 } else if (stack.indexOf(base) > -1) {1044 stack.unshift(base);1045 const dep_chain = stack.map(i => i.name).join(',');1046 throw new IdlHarnessError(`${this.name} has a circular dependency: ${dep_chain}`);1047 }1048 idl_interface = base;1049 stack.unshift(idl_interface);1050 }1051 return stack;1052};1053 * Implementation of1054 * for testing purposes.1055 *1056 * Collects the IDL types of the attributes that meet the criteria1057 * for inclusion in the default toJSON operation for easy1058 * comparison with actual value1059IdlInterface.prototype.default_to_json_operation = function() {1060 const map = new Map()1061 let isDefault = false;1062 for (const I of this.get_reverse_inheritance_stack()) {1063 if (I.has_default_to_json_regular_operation()) {1064 isDefault = true;1065 for (const m of I.members) {1066 if (m.special !== "static" && m.type == "attribute" && I.array.is_json_type(m.idlType)) {1067 map.set(m.name, m.idlType);1068 }1069 }1070 } else if (I.has_to_json_regular_operation()) {1071 isDefault = false;1072 }1073 }1074 return isDefault ? map : null;1075};1076IdlInterface.prototype.test = function()1077{1078 if (this.has_extended_attribute("LegacyNoInterfaceObject") || this.is_mixin())1079 {1080 return;1081 }1082 if (!this.exposed)1083 {1084 if (!this.untested)1085 {1086 subsetTestByKey(this.name, test, function() {1087 assert_false(this.name in self);1088 }.bind(this), this.name + " interface: existence and properties of interface object");1089 }1090 return;1091 }1092 if (!this.untested)1093 {1094 this.test_self();1095 }1096 this.test_members();1097};1098IdlInterface.prototype.constructors = function()1099{1100 return this.members1101 .filter(function(m) { return m.type == "constructor"; });1102}1103IdlInterface.prototype.test_self = function()1104{1105 subsetTestByKey(this.name, test, function()1106 {1107 if (!this.should_have_interface_object()) {1108 return;1109 }1110 this.assert_interface_object_exists();1111 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object_owner(), this.name);1112 assert_false("get" in desc, "self's property " + format_value(this.name) + " should not have a getter");1113 assert_false("set" in desc, "self's property " + format_value(this.name) + " should not have a setter");1114 assert_true(desc.writable, "self's property " + format_value(this.name) + " should be writable");1115 assert_false(desc.enumerable, "self's property " + format_value(this.name) + " should not be enumerable");1116 assert_true(desc.configurable, "self's property " + format_value(this.name) + " should be configurable");1117 if (this.is_callback()) {1118 assert_equals(Object.getPrototypeOf(this.get_interface_object()), Function.prototype,1119 "prototype of self's property " + format_value(this.name) + " is not Object.prototype");1120 return;1121 }1122 assert_class_string(this.get_interface_object(), "Function", "class string of " + this.name);1123 var prototype = Object.getPrototypeOf(this.get_interface_object());1124 if (this.base) {1125 var inherited_interface = this.array.members[this.base];1126 if (!inherited_interface.has_extended_attribute("LegacyNoInterfaceObject")) {1127 inherited_interface.assert_interface_object_exists();1128 assert_equals(prototype, inherited_interface.get_interface_object(),1129 'prototype of ' + this.name + ' is not ' +1130 this.base);1131 }1132 } else {1133 assert_equals(prototype, Function.prototype,1134 "prototype of self's property " + format_value(this.name) + " is not Function.prototype");1135 }1136 assert_true(isConstructor(this.get_interface_object()), "interface object must pass IsConstructor check");1137 if (!this.constructors().length) {1138 var interface_object = this.get_interface_object();1139 assert_throws_js(globalOf(interface_object).TypeError, function() {1140 interface_object();1141 }, "interface object didn't throw TypeError when called as a function");1142 assert_throws_js(globalOf(interface_object).TypeError, function() {1143 new interface_object();1144 }, "interface object didn't throw TypeError when called as a constructor");1145 }1146 }.bind(this), this.name + " interface: existence and properties of interface object");1147 if (this.should_have_interface_object() && !this.is_callback()) {1148 subsetTestByKey(this.name, test, function() {1149 this.assert_interface_object_exists();1150 assert_own_property(this.get_interface_object(), "length");1151 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "length");1152 assert_false("get" in desc, this.name + ".length should not have a getter");1153 assert_false("set" in desc, this.name + ".length should not have a setter");1154 assert_false(desc.writable, this.name + ".length should not be writable");1155 assert_false(desc.enumerable, this.name + ".length should not be enumerable");1156 assert_true(desc.configurable, this.name + ".length should be configurable");1157 var constructors = this.constructors();1158 var expected_length = minOverloadLength(constructors);1159 assert_equals(this.get_interface_object().length, expected_length, "wrong value for " + this.name + ".length");1160 }.bind(this), this.name + " interface object length");1161 }1162 if (this.should_have_interface_object()) {1163 subsetTestByKey(this.name, test, function() {1164 this.assert_interface_object_exists();1165 assert_own_property(this.get_interface_object(), "name");1166 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "name");1167 assert_false("get" in desc, this.name + ".name should not have a getter");1168 assert_false("set" in desc, this.name + ".name should not have a setter");1169 assert_false(desc.writable, this.name + ".name should not be writable");1170 assert_false(desc.enumerable, this.name + ".name should not be enumerable");1171 assert_true(desc.configurable, this.name + ".name should be configurable");1172 assert_equals(this.get_interface_object().name, this.name, "wrong value for " + this.name + ".name");1173 }.bind(this), this.name + " interface object name");1174 }1175 if (this.has_extended_attribute("LegacyWindowAlias")) {1176 subsetTestByKey(this.name, test, function()1177 {1178 var aliasAttrs = this.extAttrs.filter(function(o) { return o.name === "LegacyWindowAlias"; });1179 if (aliasAttrs.length > 1) {1180 throw new IdlHarnessError("Invalid IDL: multiple LegacyWindowAlias extended attributes on " + this.name);1181 }1182 if (this.is_callback()) {1183 throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on non-interface " + this.name);1184 }1185 if (!this.exposureSet.has("Window")) {1186 throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " which is not exposed in Window");1187 }1188 var rhs = aliasAttrs[0].rhs;1189 if (!rhs) {1190 throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " without identifier");1191 }1192 var aliases;1193 if (rhs.type === "identifier-list") {1194 aliases = rhs.value.map(id => id.value);1195 aliases = [ rhs.value ];1196 }1197 var alias;1198 if (exposed_in(exposure_set(this, this.exposureSet)) && 'document' in self) {1199 for (alias of aliases) {1200 assert_true(alias in self, alias + " should exist");1201 assert_equals(self[alias], this.get_interface_object(), "self." + alias + " should be the same value as self." + this.get_qualified_name());1202 var desc = Object.getOwnPropertyDescriptor(self, alias);1203 assert_equals(desc.value, this.get_interface_object(), "wrong value in " + alias + " property descriptor");1204 assert_true(desc.writable, alias + " should be writable");1205 assert_false(desc.enumerable, alias + " should not be enumerable");1206 assert_true(desc.configurable, alias + " should be configurable");1207 assert_false('get' in desc, alias + " should not have a getter");1208 assert_false('set' in desc, alias + " should not have a setter");1209 }1210 } else {1211 for (alias of aliases) {1212 assert_false(alias in self, alias + " should not exist");1213 }1214 }1215 }.bind(this), this.name + " interface: legacy window alias");1216 }1217 if (this.has_extended_attribute("LegacyFactoryFunction")) {1218 var constructors = this.extAttrs1219 .filter(function(attr) { return attr.name == "LegacyFactoryFunction"; });1220 if (constructors.length !== 1) {1221 throw new IdlHarnessError("Internal error: missing support for multiple LegacyFactoryFunction extended attributes");1222 }1223 var constructor = constructors[0];1224 var min_length = minOverloadLength([constructor]);1225 subsetTestByKey(this.name, test, function()1226 {1227 var name = constructor.rhs.value;1228 assert_own_property(self, name);1229 var desc = Object.getOwnPropertyDescriptor(self, name);1230 assert_equals(desc.value, self[name], "wrong value in " + name + " property descriptor");1231 assert_true(desc.writable, name + " should be writable");1232 assert_false(desc.enumerable, name + " should not be enumerable");1233 assert_true(desc.configurable, name + " should be configurable");1234 assert_false("get" in desc, name + " should not have a getter");1235 assert_false("set" in desc, name + " should not have a setter");1236 }.bind(this), this.name + " interface: named constructor");1237 subsetTestByKey(this.name, test, function()1238 {1239 var name = constructor.rhs.value;1240 var value = self[name];1241 assert_equals(typeof value, "function", "type of value in " + name + " property descriptor");1242 assert_not_equals(value, this.get_interface_object(), "wrong value in " + name + " property descriptor");1243 assert_equals(Object.getPrototypeOf(value), Function.prototype, "wrong value for " + name + "'s prototype");1244 }.bind(this), this.name + " interface: named constructor object");1245 subsetTestByKey(this.name, test, function()1246 {1247 var name = constructor.rhs.value;1248 var expected = this.get_interface_object().prototype;1249 var desc = Object.getOwnPropertyDescriptor(self[name], "prototype");1250 assert_equals(desc.value, expected, "wrong value for " + name + ".prototype");1251 assert_false(desc.writable, "prototype should not be writable");1252 assert_false(desc.enumerable, "prototype should not be enumerable");1253 assert_false(desc.configurable, "prototype should not be configurable");1254 assert_false("get" in desc, "prototype should not have a getter");1255 assert_false("set" in desc, "prototype should not have a setter");1256 }.bind(this), this.name + " interface: named constructor prototype property");1257 subsetTestByKey(this.name, test, function()1258 {1259 var name = constructor.rhs.value;1260 var desc = Object.getOwnPropertyDescriptor(self[name], "name");1261 assert_equals(desc.value, name, "wrong value for " + name + ".name");1262 assert_false(desc.writable, "name should not be writable");1263 assert_false(desc.enumerable, "name should not be enumerable");1264 assert_true(desc.configurable, "name should be configurable");1265 assert_false("get" in desc, "name should not have a getter");1266 assert_false("set" in desc, "name should not have a setter");1267 }.bind(this), this.name + " interface: named constructor name");1268 subsetTestByKey(this.name, test, function()1269 {1270 var name = constructor.rhs.value;1271 var desc = Object.getOwnPropertyDescriptor(self[name], "length");1272 assert_equals(desc.value, min_length, "wrong value for " + name + ".length");1273 assert_false(desc.writable, "length should not be writable");1274 assert_false(desc.enumerable, "length should not be enumerable");1275 assert_true(desc.configurable, "length should be configurable");1276 assert_false("get" in desc, "length should not have a getter");1277 assert_false("set" in desc, "length should not have a setter");1278 }.bind(this), this.name + " interface: named constructor length");1279 subsetTestByKey(this.name, test, function()1280 {1281 var name = constructor.rhs.value;1282 var args = constructor.arguments.map(function(arg) {1283 return create_suitable_object(arg.idlType);1284 });1285 assert_throws_js(globalOf(self[name]).TypeError, function() {1286 self[name](...args);1287 }.bind(this));1288 }.bind(this), this.name + " interface: named constructor without 'new'");1289 }1290 subsetTestByKey(this.name, test, function()1291 {1292 if (!this.should_have_interface_object()) {1293 return;1294 }1295 this.assert_interface_object_exists();1296 if (this.is_callback()) {1297 assert_false("prototype" in this.get_interface_object(),1298 this.name + ' should not have a "prototype" property');1299 return;1300 }1301 assert_own_property(this.get_interface_object(), "prototype",1302 'interface "' + this.name + '" does not have own property "prototype"');1303 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "prototype");1304 assert_false("get" in desc, this.name + ".prototype should not have a getter");1305 assert_false("set" in desc, this.name + ".prototype should not have a setter");1306 assert_false(desc.writable, this.name + ".prototype should not be writable");1307 assert_false(desc.enumerable, this.name + ".prototype should not be enumerable");1308 assert_false(desc.configurable, this.name + ".prototype should not be configurable");1309 if (this.name === "Window") {1310 assert_class_string(Object.getPrototypeOf(this.get_interface_object().prototype),1311 'WindowProperties',1312 'Class name for prototype of Window' +1313 '.prototype is not "WindowProperties"');1314 } else {1315 var inherit_interface, inherit_interface_interface_object;1316 if (this.base) {1317 inherit_interface = this.base;1318 var parent = this.array.members[inherit_interface];1319 if (!parent.has_extended_attribute("LegacyNoInterfaceObject")) {1320 parent.assert_interface_object_exists();1321 inherit_interface_interface_object = parent.get_interface_object();1322 }1323 } else if (this.name === "DOMException") {1324 inherit_interface = 'Error';1325 inherit_interface_interface_object = self.Error;1326 } else {1327 inherit_interface = 'Object';1328 inherit_interface_interface_object = self.Object;1329 }1330 if (inherit_interface_interface_object) {1331 assert_not_equals(inherit_interface_interface_object, undefined,1332 'should inherit from ' + inherit_interface + ', but there is no such property');1333 assert_own_property(inherit_interface_interface_object, 'prototype',1334 'should inherit from ' + inherit_interface + ', but that object has no "prototype" property');1335 assert_equals(Object.getPrototypeOf(this.get_interface_object().prototype),1336 inherit_interface_interface_object.prototype,1337 'prototype of ' + this.name + '.prototype is not ' + inherit_interface + '.prototype');1338 } else {1339 assert_class_string(Object.getPrototypeOf(this.get_interface_object().prototype),1340 inherit_interface + 'Prototype',1341 'Class name for prototype of ' + this.name +1342 '.prototype is not "' + inherit_interface + 'Prototype"');1343 }1344 }1345 if (!this.has_stringifier()) {1346 }1347 }.bind(this), this.name + " interface: existence and properties of interface prototype object");1348 if (this.is_global()) {1349 this.test_immutable_prototype("interface prototype object", this.get_interface_object().prototype);1350 }1351 subsetTestByKey(this.name, test, function()1352 {1353 if (!this.should_have_interface_object()) {1354 return;1355 }1356 this.assert_interface_object_exists();1357 if (this.is_callback()) {1358 assert_false("prototype" in this.get_interface_object(),1359 this.name + ' should not have a "prototype" property');1360 return;1361 }1362 assert_own_property(this.get_interface_object(), "prototype",1363 'interface "' + this.name + '" does not have own property "prototype"');1364 assert_own_property(this.get_interface_object().prototype, "constructor",1365 this.name + '.prototype does not have own property "constructor"');1366 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object().prototype, "constructor");1367 assert_false("get" in desc, this.name + ".prototype.constructor should not have a getter");1368 assert_false("set" in desc, this.name + ".prototype.constructor should not have a setter");1369 assert_true(desc.writable, this.name + ".prototype.constructor should be writable");1370 assert_false(desc.enumerable, this.name + ".prototype.constructor should not be enumerable");1371 assert_true(desc.configurable, this.name + ".prototype.constructor should be configurable");1372 assert_equals(this.get_interface_object().prototype.constructor, this.get_interface_object(),1373 this.name + '.prototype.constructor is not the same object as ' + this.name);1374 }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property');1375 subsetTestByKey(this.name, test, function()1376 {1377 if (!this.should_have_interface_object()) {1378 return;1379 }1380 this.assert_interface_object_exists();1381 if (this.is_callback()) {1382 assert_false("prototype" in this.get_interface_object(),1383 this.name + ' should not have a "prototype" property');1384 return;1385 }1386 assert_own_property(this.get_interface_object(), "prototype",1387 'interface "' + this.name + '" does not have own property "prototype"');1388 var unscopables = this.get_unscopables().map(m => m.name);1389 var proto = this.get_interface_object().prototype;1390 if (unscopables.length != 0) {1391 assert_own_property(1392 proto, Symbol.unscopables,1393 this.name + '.prototype should have an @@unscopables property');1394 var desc = Object.getOwnPropertyDescriptor(proto, Symbol.unscopables);1395 assert_false("get" in desc,1396 this.name + ".prototype[Symbol.unscopables] should not have a getter");1397 assert_false("set" in desc, this.name + ".prototype[Symbol.unscopables] should not have a setter");1398 assert_false(desc.writable, this.name + ".prototype[Symbol.unscopables] should not be writable");1399 assert_false(desc.enumerable, this.name + ".prototype[Symbol.unscopables] should not be enumerable");1400 assert_true(desc.configurable, this.name + ".prototype[Symbol.unscopables] should be configurable");1401 assert_equals(desc.value, proto[Symbol.unscopables],1402 this.name + '.prototype[Symbol.unscopables] should be in the descriptor');1403 assert_equals(typeof desc.value, "object",1404 this.name + '.prototype[Symbol.unscopables] should be an object');1405 assert_equals(Object.getPrototypeOf(desc.value), null,1406 this.name + '.prototype[Symbol.unscopables] should have a null prototype');1407 assert_equals(Object.getOwnPropertySymbols(desc.value).length,1408 0,1409 this.name + '.prototype[Symbol.unscopables] should have the right number of symbol-named properties');1410 var observed = Object.getOwnPropertyNames(desc.value);1411 for (var prop of observed) {1412 assert_not_equals(unscopables.indexOf(prop),1413 -1,1414 this.name + '.prototype[Symbol.unscopables] has unexpected property "' + prop + '"');1415 }1416 } else {1417 assert_equals(Object.getOwnPropertyDescriptor(this.get_interface_object().prototype, Symbol.unscopables),1418 undefined,1419 this.name + '.prototype should not have @@unscopables');1420 }1421 }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s @@unscopables property');1422};1423IdlInterface.prototype.test_immutable_prototype = function(type, obj)1424{1425 if (typeof Object.setPrototypeOf !== "function") {1426 return;1427 }1428 subsetTestByKey(this.name, test, function(t) {1429 var originalValue = Object.getPrototypeOf(obj);1430 var newValue = Object.create(null);1431 t.add_cleanup(function() {1432 try {1433 Object.setPrototypeOf(obj, originalValue);1434 } catch (err) {}1435 });1436 assert_throws_js(TypeError, function() {1437 Object.setPrototypeOf(obj, newValue);1438 });1439 assert_equals(1440 Object.getPrototypeOf(obj),1441 originalValue,1442 "original value not modified"1443 );1444 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1445 "of " + type + " - setting to a new value via Object.setPrototypeOf " +1446 "should throw a TypeError");1447 subsetTestByKey(this.name, test, function(t) {1448 var originalValue = Object.getPrototypeOf(obj);1449 var newValue = Object.create(null);1450 t.add_cleanup(function() {1451 let setter = Object.getOwnPropertyDescriptor(1452 Object.prototype, '__proto__'1453 ).set;1454 try {1455 setter.call(obj, originalValue);1456 } catch (err) {}1457 });1458 let setter;1459 {1460 let cur = obj;1461 while (cur) {1462 const desc = Object.getOwnPropertyDescriptor(cur, "__proto__");1463 if (desc) {1464 setter = desc.set;1465 break;1466 }1467 cur = Object.getPrototypeOf(cur);1468 }1469 }1470 assert_throws_js(globalOf(setter).TypeError, function() {1471 obj.__proto__ = newValue;1472 });1473 assert_equals(1474 Object.getPrototypeOf(obj),1475 originalValue,1476 "original value not modified"1477 );1478 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1479 "of " + type + " - setting to a new value via __proto__ " +1480 "should throw a TypeError");1481 subsetTestByKey(this.name, test, function(t) {1482 var originalValue = Object.getPrototypeOf(obj);1483 var newValue = Object.create(null);1484 t.add_cleanup(function() {1485 try {1486 Reflect.setPrototypeOf(obj, originalValue);1487 } catch (err) {}1488 });1489 assert_false(Reflect.setPrototypeOf(obj, newValue));1490 assert_equals(1491 Object.getPrototypeOf(obj),1492 originalValue,1493 "original value not modified"1494 );1495 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1496 "of " + type + " - setting to a new value via Reflect.setPrototypeOf " +1497 "should return false");1498 subsetTestByKey(this.name, test, function() {1499 var originalValue = Object.getPrototypeOf(obj);1500 Object.setPrototypeOf(obj, originalValue);1501 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1502 "of " + type + " - setting to its original value via Object.setPrototypeOf " +1503 "should not throw");1504 subsetTestByKey(this.name, test, function() {1505 var originalValue = Object.getPrototypeOf(obj);1506 obj.__proto__ = originalValue;1507 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1508 "of " + type + " - setting to its original value via __proto__ " +1509 "should not throw");1510 subsetTestByKey(this.name, test, function() {1511 var originalValue = Object.getPrototypeOf(obj);1512 assert_true(Reflect.setPrototypeOf(obj, originalValue));1513 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1514 "of " + type + " - setting to its original value via Reflect.setPrototypeOf " +1515 "should return true");1516};1517IdlInterface.prototype.test_member_const = function(member)1518{1519 if (!this.has_constants()) {1520 throw new IdlHarnessError("Internal error: test_member_const called without any constants");1521 }1522 subsetTestByKey(this.name, test, function()1523 {1524 this.assert_interface_object_exists();1525 assert_own_property(this.get_interface_object(), member.name);1526 assert_equals(this.get_interface_object()[member.name], constValue(member.value),1527 "property has wrong value");1528 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), member.name);1529 assert_false("get" in desc, "property should not have a getter");1530 assert_false("set" in desc, "property should not have a setter");1531 assert_false(desc.writable, "property should not be writable");1532 assert_true(desc.enumerable, "property should be enumerable");1533 assert_false(desc.configurable, "property should not be configurable");1534 }.bind(this), this.name + " interface: constant " + member.name + " on interface object");1535 subsetTestByKey(this.name, test, function()1536 {1537 this.assert_interface_object_exists();1538 if (this.is_callback()) {1539 assert_false("prototype" in this.get_interface_object(),1540 this.name + ' should not have a "prototype" property');1541 return;1542 }1543 assert_own_property(this.get_interface_object(), "prototype",1544 'interface "' + this.name + '" does not have own property "prototype"');1545 assert_own_property(this.get_interface_object().prototype, member.name);1546 assert_equals(this.get_interface_object().prototype[member.name], constValue(member.value),1547 "property has wrong value");1548 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), member.name);1549 assert_false("get" in desc, "property should not have a getter");1550 assert_false("set" in desc, "property should not have a setter");1551 assert_false(desc.writable, "property should not be writable");1552 assert_true(desc.enumerable, "property should be enumerable");1553 assert_false(desc.configurable, "property should not be configurable");1554 }.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object");1555};1556IdlInterface.prototype.test_member_attribute = function(member)1557 {1558 if (!shouldRunSubTest(this.name)) {1559 return;1560 }1561 var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: attribute " + member.name);1562 a_test.step(function()1563 {1564 if (!this.should_have_interface_object()) {1565 a_test.done();1566 return;1567 }1568 this.assert_interface_object_exists();1569 assert_own_property(this.get_interface_object(), "prototype",1570 'interface "' + this.name + '" does not have own property "prototype"');1571 if (member.special === "static") {1572 assert_own_property(this.get_interface_object(), member.name,1573 "The interface object must have a property " +1574 format_value(member.name));1575 a_test.done();1576 return;1577 }1578 this.do_member_unscopable_asserts(member);1579 if (this.is_global()) {1580 assert_own_property(self, member.name,1581 "The global object must have a property " +1582 format_value(member.name));1583 assert_false(member.name in this.get_interface_object().prototype,1584 "The prototype object should not have a property " +1585 format_value(member.name));1586 var getter = Object.getOwnPropertyDescriptor(self, member.name).get;1587 assert_equals(typeof(getter), "function",1588 format_value(member.name) + " must have a getter");1589 var gotValue;1590 var propVal;1591 try {1592 propVal = self[member.name];1593 gotValue = true;1594 } catch (e) {1595 gotValue = false;1596 }1597 if (gotValue) {1598 assert_equals(propVal, getter.call(undefined),1599 "Gets on a global should not require an explicit this");1600 }1601 this.do_interface_attribute_asserts(self, member, a_test);1602 } else {1603 assert_true(member.name in this.get_interface_object().prototype,1604 "The prototype object must have a property " +1605 format_value(member.name));1606 if (!member.has_extended_attribute("LegacyLenientThis")) {1607 if (member.idlType.generic !== "Promise") {1608 assert_throws_js(TypeError, function() {1609 this.get_interface_object().prototype[member.name];1610 }.bind(this), "getting property on prototype object must throw TypeError");1611 this.do_interface_attribute_asserts(this.get_interface_object().prototype, member, a_test);1612 } else {1613 promise_rejects_js(a_test, TypeError,1614 this.get_interface_object().prototype[member.name])1615 .then(a_test.step_func(function() {1616 this.do_interface_attribute_asserts(this.get_interface_object().prototype,1617 member, a_test);1618 }.bind(this)));1619 }1620 } else {1621 assert_equals(this.get_interface_object().prototype[member.name], undefined,1622 "getting property on prototype object must return undefined");1623 this.do_interface_attribute_asserts(this.get_interface_object().prototype, member, a_test);1624 }1625 }1626 }.bind(this));1627};1628IdlInterface.prototype.test_member_operation = function(member)1629{1630 if (!shouldRunSubTest(this.name)) {1631 return;1632 }1633 var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: operation " + member);1634 a_test.step(function()1635 {1636 if (!this.should_have_interface_object()) {1637 a_test.done();1638 return;1639 }1640 this.assert_interface_object_exists();1641 if (this.is_callback()) {1642 assert_false("prototype" in this.get_interface_object(),1643 this.name + ' should not have a "prototype" property');1644 a_test.done();1645 return;1646 }1647 assert_own_property(this.get_interface_object(), "prototype",1648 'interface "' + this.name + '" does not have own property "prototype"');1649 var memberHolderObject;1650 if (member.special === "static") {1651 assert_own_property(this.get_interface_object(), member.name,1652 "interface object missing static operation");1653 memberHolderObject = this.get_interface_object();1654 } else if (this.is_global()) {1655 assert_own_property(self, member.name,1656 "global object missing non-static operation");1657 memberHolderObject = self;1658 } else {1659 assert_own_property(this.get_interface_object().prototype, member.name,1660 "interface prototype object missing non-static operation");1661 memberHolderObject = this.get_interface_object().prototype;1662 }1663 this.do_member_unscopable_asserts(member);1664 this.do_member_operation_asserts(memberHolderObject, member, a_test);1665 }.bind(this));1666};1667IdlInterface.prototype.do_member_unscopable_asserts = function(member)1668{1669 if (!member.isUnscopable) {1670 return;1671 }1672 var unscopables = this.get_interface_object().prototype[Symbol.unscopables];1673 var prop = member.name;1674 var propDesc = Object.getOwnPropertyDescriptor(unscopables, prop);1675 assert_equals(typeof propDesc, "object",1676 this.name + '.prototype[Symbol.unscopables].' + prop + ' must exist')1677 assert_false("get" in propDesc,1678 this.name + '.prototype[Symbol.unscopables].' + prop + ' must have no getter');1679 assert_false("set" in propDesc,1680 this.name + '.prototype[Symbol.unscopables].' + prop + ' must have no setter');1681 assert_true(propDesc.writable,1682 this.name + '.prototype[Symbol.unscopables].' + prop + ' must be writable');1683 assert_true(propDesc.enumerable,1684 this.name + '.prototype[Symbol.unscopables].' + prop + ' must be enumerable');1685 assert_true(propDesc.configurable,1686 this.name + '.prototype[Symbol.unscopables].' + prop + ' must be configurable');1687 assert_equals(propDesc.value, true,1688 this.name + '.prototype[Symbol.unscopables].' + prop + ' must have the value `true`');1689};1690IdlInterface.prototype.do_member_operation_asserts = function(memberHolderObject, member, a_test)1691{1692 var done = a_test.done.bind(a_test);1693 var operationUnforgeable = member.isUnforgeable;1694 var desc = Object.getOwnPropertyDescriptor(memberHolderObject, member.name);1695 assert_false("get" in desc, "property should not have a getter");1696 assert_false("set" in desc, "property should not have a setter");1697 assert_equals(desc.writable, !operationUnforgeable,1698 "property should be writable if and only if not unforgeable");1699 assert_true(desc.enumerable, "property should be enumerable");1700 assert_equals(desc.configurable, !operationUnforgeable,1701 "property should be configurable if and only if not unforgeable");1702 assert_equals(typeof memberHolderObject[member.name], "function",1703 "property must be a function");1704 const ctors = this.members.filter(function(m) {1705 return m.type == "operation" && m.name == member.name;1706 });1707 assert_equals(1708 memberHolderObject[member.name].length,1709 minOverloadLength(ctors),1710 "property has wrong .length");1711 assert_equals(1712 memberHolderObject[member.name].name,1713 member.name,1714 "property has wrong .name");1715 var args = member.arguments.map(function(arg) {1716 return create_suitable_object(arg.idlType);1717 });1718 if (member.special !== "static") {1719 var cb;1720 if (!this.is_global() &&1721 memberHolderObject[member.name] != self[member.name])1722 {1723 cb = awaitNCallbacks(2, done);1724 throwOrReject(a_test, member, memberHolderObject[member.name], null, args,1725 "calling operation with this = null didn't throw TypeError", cb);1726 } else {1727 cb = awaitNCallbacks(1, done);1728 }1729 throwOrReject(a_test, member, memberHolderObject[member.name], {}, args,1730 "calling operation with this = {} didn't throw TypeError", cb);1731 } else {1732 done();1733 }1734}1735IdlInterface.prototype.test_to_json_operation = function(desc, memberHolderObject, member) {1736 var instanceName = memberHolderObject && memberHolderObject.constructor.name1737 || member.name + " object";1738 if (member.has_extended_attribute("Default")) {1739 subsetTestByKey(this.name, test, function() {1740 var map = this.default_to_json_operation();1741 var json = memberHolderObject.toJSON();1742 map.forEach(function(type, k) {1743 assert_true(k in json, "property " + JSON.stringify(k) + " should be present in the output of " + this.name + ".prototype.toJSON()");1744 var descriptor = Object.getOwnPropertyDescriptor(json, k);1745 assert_true(descriptor.writable, "property " + k + " should be writable");1746 assert_true(descriptor.configurable, "property " + k + " should be configurable");1747 assert_true(descriptor.enumerable, "property " + k + " should be enumerable");1748 this.array.assert_type_is(json[k], type);1749 delete json[k];1750 }, this);1751 }.bind(this), this.name + " interface: default toJSON operation on " + desc);1752 } else {1753 subsetTestByKey(this.name, test, function() {1754 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);1755 this.array.assert_type_is(memberHolderObject.toJSON(), member.idlType);1756 }.bind(this), this.name + " interface: toJSON operation on " + desc);1757 }1758};1759IdlInterface.prototype.test_member_iterable = function(member)1760{1761 subsetTestByKey(this.name, test, function()1762 {1763 var isPairIterator = member.idlType.length === 2;1764 var proto = this.get_interface_object().prototype;1765 var iteratorDesc = Object.getOwnPropertyDescriptor(proto, Symbol.iterator);1766 assert_true(iteratorDesc.writable, "@@iterator property should be writable");1767 assert_true(iteratorDesc.configurable, "@@iterator property should be configurable");1768 assert_false(iteratorDesc.enumerable, "@@iterator property should not be enumerable");1769 assert_equals(typeof iteratorDesc.value, "function", "@@iterator property should be a function");1770 assert_equals(iteratorDesc.value.length, 0, "@@iterator function object length should be 0");1771 assert_equals(iteratorDesc.value.name, isPairIterator ? "entries" : "values", "@@iterator function object should have the right name");1772 if (isPairIterator) {1773 assert_equals(proto["entries"], proto[Symbol.iterator], "entries method should be the same as @@iterator method");1774 [1775 ["entries", 0],1776 ["keys", 0],1777 ["values", 0],1778 ["forEach", 1]1779 ].forEach(([property, length]) => {1780 var desc = Object.getOwnPropertyDescriptor(proto, property);1781 assert_equals(typeof desc.value, "function", property + " property should be a function");1782 assert_equals(desc.value.length, length, property + " function object length should be " + length);1783 assert_equals(desc.value.name, property, property + " function object should have the right name");1784 });1785 } else {1786 assert_equals(proto[Symbol.iterator], Array.prototype[Symbol.iterator], "@@iterator method should be the same as Array prototype's");1787 ["entries", "keys", "values", "forEach", Symbol.iterator].forEach(property => {1788 var propertyName = property === Symbol.iterator ? "@@iterator" : property;1789 assert_equals(proto[property], Array.prototype[property], propertyName + " method should be the same as Array prototype's");1790 });1791 }1792 }.bind(this), this.name + " interface: iterable<" + member.idlType.map(function(t) { return t.idlType; }).join(", ") + ">");1793};1794IdlInterface.prototype.test_member_async_iterable = function(member)1795{1796 subsetTestByKey(this.name, test, function()1797 {1798 var isPairIterator = member.idlType.length === 2;1799 var proto = this.get_interface_object().prototype;1800 var iteratorDesc = Object.getOwnPropertyDescriptor(proto, Symbol.asyncIterator);1801 assert_true(iteratorDesc.writable, "@@asyncIterator property should be writable");1802 assert_true(iteratorDesc.configurable, "@@asyncIterator property should be configurable");1803 assert_false(iteratorDesc.enumerable, "@@asyncIterator property should not be enumerable");1804 assert_equals(typeof iteratorDesc.value, "function", "@@asyncIterator property should be a function");1805 assert_equals(iteratorDesc.value.length, 0, "@@asyncIterator function object length should be 0");1806 assert_equals(iteratorDesc.value.name, isPairIterator ? "entries" : "values", "@@asyncIterator function object should have the right name");1807 if (isPairIterator) {1808 assert_equals(proto["entries"], proto[Symbol.asyncIterator], "entries method should be the same as @@asyncIterator method");1809 ["entries", "keys", "values"].forEach(property => {1810 var desc = Object.getOwnPropertyDescriptor(proto, property);1811 assert_equals(typeof desc.value, "function", property + " property should be a function");1812 assert_equals(desc.value.length, 0, property + " function object length should be 0");1813 assert_equals(desc.value.name, property, property + " function object should have the right name");1814 });1815 } else {1816 assert_equals(proto["values"], proto[Symbol.asyncIterator], "values method should be the same as @@asyncIterator method");1817 assert_false("entries" in proto, "should not have an entries method");1818 assert_false("keys" in proto, "should not have a keys method");1819 }1820 }.bind(this), this.name + " interface: async iterable<" + member.idlType.map(function(t) { return t.idlType; }).join(", ") + ">");1821};1822IdlInterface.prototype.test_member_stringifier = function(member)1823{1824 subsetTestByKey(this.name, test, function()1825 {1826 if (!this.should_have_interface_object()) {1827 return;1828 }1829 this.assert_interface_object_exists();1830 if (this.is_callback()) {1831 assert_false("prototype" in this.get_interface_object(),1832 this.name + ' should not have a "prototype" property');1833 return;1834 }1835 assert_own_property(this.get_interface_object(), "prototype",1836 'interface "' + this.name + '" does not have own property "prototype"');1837 var interfacePrototypeObject = this.get_interface_object().prototype;1838 assert_own_property(interfacePrototypeObject, "toString",1839 "interface prototype object missing non-static operation");1840 var stringifierUnforgeable = member.isUnforgeable;1841 var desc = Object.getOwnPropertyDescriptor(interfacePrototypeObject, "toString");1842 assert_false("get" in desc, "property should not have a getter");1843 assert_false("set" in desc, "property should not have a setter");1844 assert_equals(desc.writable, !stringifierUnforgeable,1845 "property should be writable if and only if not unforgeable");1846 assert_true(desc.enumerable, "property should be enumerable");1847 assert_equals(desc.configurable, !stringifierUnforgeable,1848 "property should be configurable if and only if not unforgeable");1849 assert_equals(typeof interfacePrototypeObject.toString, "function",1850 "property must be a function");1851 assert_equals(interfacePrototypeObject.toString.length, 0,1852 "property has wrong .length");1853 assert_throws_js(globalOf(interfacePrototypeObject.toString).TypeError, function() {1854 interfacePrototypeObject.toString.apply(null, []);1855 }, "calling stringifier with this = null didn't throw TypeError");1856 assert_throws_js(globalOf(interfacePrototypeObject.toString).TypeError, function() {1857 interfacePrototypeObject.toString.apply({}, []);1858 }, "calling stringifier with this = {} didn't throw TypeError");1859 }.bind(this), this.name + " interface: stringifier");1860};1861IdlInterface.prototype.test_members = function()1862{1863 for (var i = 0; i < this.members.length; i++)1864 {1865 var member = this.members[i];1866 if (member.untested) {1867 continue;1868 }1869 if (!exposed_in(exposure_set(member, this.exposureSet))) {1870 subsetTestByKey(this.name, test, function() {1871 assert_false(member.name in this.get_interface_object(),1872 "The interface object must not have a property " +1873 format_value(member.name));1874 assert_false(member.name in this.get_interface_object().prototype,1875 "The prototype object must not have a property " +1876 format_value(member.name));1877 }.bind(this), this.name + " interface: member " + member.name);1878 continue;1879 }1880 switch (member.type) {1881 case "const":1882 this.test_member_const(member);1883 break;1884 case "attribute":1885 if (!member.isUnforgeable)1886 {1887 this.test_member_attribute(member);1888 }1889 if (member.special === "stringifier") {1890 this.test_member_stringifier(member);1891 }1892 break;1893 case "operation":1894 if (member.name) {1895 if (!member.isUnforgeable)1896 {1897 this.test_member_operation(member);1898 }1899 } else if (member.special === "stringifier") {1900 this.test_member_stringifier(member);1901 }1902 break;1903 case "iterable":1904 if (member.async) {1905 this.test_member_async_iterable(member);1906 } else {1907 this.test_member_iterable(member);1908 }1909 break;1910 default:1911 break;1912 }1913 }1914};1915IdlInterface.prototype.test_object = function(desc)1916{1917 var obj, exception = null;1918 try1919 {1920 obj = eval(desc);1921 }1922 catch(e)1923 {1924 exception = e;1925 }1926 var expected_typeof;1927 if (this.name == "HTMLAllCollection")1928 {1929 expected_typeof = "undefined";1930 }1931 else1932 {1933 expected_typeof = "object";1934 }1935 this.test_primary_interface_of(desc, obj, exception, expected_typeof);1936 var current_interface = this;1937 while (current_interface)1938 {1939 if (!(current_interface.name in this.array.members))1940 {1941 throw new IdlHarnessError("Interface " + current_interface.name + " not found (inherited by " + this.name + ")");1942 }1943 if (current_interface.prevent_multiple_testing && current_interface.already_tested)1944 {1945 return;1946 }1947 current_interface.test_interface_of(desc, obj, exception, expected_typeof);1948 current_interface = this.array.members[current_interface.base];1949 }1950};1951IdlInterface.prototype.test_primary_interface_of = function(desc, obj, exception, expected_typeof)1952{1953 if (this.untested)1954 {1955 return;1956 }1957 if (this.is_global())1958 {1959 this.test_immutable_prototype("global platform object", obj);1960 }1961 if (this.should_have_interface_object()1962 && (typeof obj != expected_typeof || obj instanceof Object))1963 {1964 subsetTestByKey(this.name, test, function()1965 {1966 assert_equals(exception, null, "Unexpected exception when evaluating object");1967 assert_equals(typeof obj, expected_typeof, "wrong typeof object");1968 this.assert_interface_object_exists();1969 assert_own_property(this.get_interface_object(), "prototype",1970 'interface "' + this.name + '" does not have own property "prototype"');1971 assert_equals(Object.getPrototypeOf(obj),1972 this.get_interface_object().prototype,1973 desc + "'s prototype is not " + this.name + ".prototype");1974 }.bind(this), this.name + " must be primary interface of " + desc);1975 }1976 subsetTestByKey(this.name, test, function()1977 {1978 assert_equals(exception, null, "Unexpected exception when evaluating object");1979 assert_equals(typeof obj, expected_typeof, "wrong typeof object");1980 assert_class_string(obj, this.get_qualified_name(), "class string of " + desc);1981 if (!this.has_stringifier())1982 {1983 assert_equals(String(obj), "[object " + this.get_qualified_name() + "]", "String(" + desc + ")");1984 }1985 }.bind(this), "Stringification of " + desc);1986};1987IdlInterface.prototype.test_interface_of = function(desc, obj, exception, expected_typeof)1988{1989 this.already_tested = true;1990 if (!shouldRunSubTest(this.name)) {1991 return;1992 }1993 for (var i = 0; i < this.members.length; i++)1994 {1995 var member = this.members[i];1996 if (member.untested) {1997 continue;1998 }1999 if (!exposed_in(exposure_set(member, this.exposureSet))) {2000 subsetTestByKey(this.name, test, function() {2001 assert_equals(exception, null, "Unexpected exception when evaluating object");2002 assert_false(member.name in obj);2003 }.bind(this), this.name + " interface: " + desc + ' must not have property "' + member.name + '"');2004 continue;2005 }2006 if (member.type == "attribute" && member.isUnforgeable)2007 {2008 var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: " + desc + ' must have own property "' + member.name + '"');2009 a_test.step(function() {2010 assert_equals(exception, null, "Unexpected exception when evaluating object");2011 assert_equals(typeof obj, expected_typeof, "wrong typeof object");2012 this.do_interface_attribute_asserts(obj, member, a_test);2013 }.bind(this));2014 }2015 else if (member.type == "operation" &&2016 member.name &&2017 member.isUnforgeable)2018 {2019 var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: " + desc + ' must have own property "' + member.name + '"');2020 a_test.step(function()2021 {2022 assert_equals(exception, null, "Unexpected exception when evaluating object");2023 assert_equals(typeof obj, expected_typeof, "wrong typeof object");2024 assert_own_property(obj, member.name,2025 "Doesn't have the unforgeable operation property");2026 this.do_member_operation_asserts(obj, member, a_test);2027 }.bind(this));2028 }2029 else if ((member.type == "const"2030 || member.type == "attribute"2031 || member.type == "operation")2032 && member.name)2033 {2034 subsetTestByKey(this.name, test, function()2035 {2036 assert_equals(exception, null, "Unexpected exception when evaluating object");2037 assert_equals(typeof obj, expected_typeof, "wrong typeof object");2038 if (member.special !== "static") {2039 if (!this.is_global()) {2040 assert_inherits(obj, member.name);2041 } else {2042 assert_own_property(obj, member.name);2043 }2044 if (member.type == "const")2045 {2046 assert_equals(obj[member.name], constValue(member.value));2047 }2048 if (member.type == "attribute")2049 {2050 var property, thrown = false;2051 try2052 {2053 property = obj[member.name];2054 }2055 catch (e)2056 {2057 thrown = true;2058 }2059 if (!thrown)2060 {2061 if (this.name == "Document" && member.name == "all")2062 {2063 assert_equals(typeof property, "undefined");2064 }2065 else2066 {2067 this.array.assert_type_is(property, member.idlType);2068 }2069 }2070 }2071 if (member.type == "operation")2072 {2073 assert_equals(typeof obj[member.name], "function");2074 }2075 }2076 }.bind(this), this.name + " interface: " + desc + ' must inherit property "' + member + '" with the proper type');2077 }2078 if (member.type == "operation" && member.name && member.arguments.length)2079 {2080 var description =2081 this.name + " interface: calling " + member + " on " + desc +2082 " with too few arguments must throw TypeError";2083 var a_test = subsetTestByKey(this.name, async_test, description);2084 a_test.step(function()2085 {2086 assert_equals(exception, null, "Unexpected exception when evaluating object");2087 assert_equals(typeof obj, expected_typeof, "wrong typeof object");2088 var fn;2089 if (member.special !== "static") {2090 if (!this.is_global() && !member.isUnforgeable) {2091 assert_inherits(obj, member.name);2092 } else {2093 assert_own_property(obj, member.name);2094 }2095 fn = obj[member.name];2096 }2097 else2098 {2099 assert_own_property(obj.constructor, member.name, "interface object must have static operation as own property");2100 fn = obj.constructor[member.name];2101 }2102 var minLength = minOverloadLength(this.members.filter(function(m) {2103 return m.type == "operation" && m.name == member.name;2104 }));2105 var args = [];2106 var cb = awaitNCallbacks(minLength, a_test.done.bind(a_test));2107 for (var i = 0; i < minLength; i++) {2108 throwOrReject(a_test, member, fn, obj, args, "Called with " + i + " arguments", cb);2109 args.push(create_suitable_object(member.arguments[i].idlType));2110 }2111 if (minLength === 0) {2112 cb();2113 }2114 }.bind(this));2115 }2116 if (member.is_to_json_regular_operation()) {2117 this.test_to_json_operation(desc, obj, member);2118 }2119 }2120};2121IdlInterface.prototype.has_stringifier = function()2122{2123 if (this.name === "DOMException") {2124 return true;2125 }2126 if (this.members.some(function(member) { return member.special === "stringifier"; })) {2127 return true;2128 }2129 if (this.base &&2130 this.array.members[this.base].has_stringifier()) {2131 return true;2132 }2133 return false;2134};2135IdlInterface.prototype.do_interface_attribute_asserts = function(obj, member, a_test)2136{2137 var pendingPromises = [];2138 assert_own_property(obj, member.name);2139 var desc = Object.getOwnPropertyDescriptor(obj, member.name);2140 assert_false("value" in desc, 'property descriptor should not have a "value" field');2141 assert_false("writable" in desc, 'property descriptor should not have a "writable" field');2142 assert_true(desc.enumerable, "property should be enumerable");2143 if (member.isUnforgeable)2144 {2145 assert_false(desc.configurable, "[LegacyUnforgeable] property must not be configurable");2146 }2147 else2148 {2149 assert_true(desc.configurable, "property must be configurable");2150 }2151 assert_equals(typeof desc.get, "function", "getter must be Function");2152 if (member.special !== "static") {2153 if (!member.has_extended_attribute("LegacyLenientThis")) {2154 if (member.idlType.generic !== "Promise") {2155 assert_throws_js(globalOf(desc.get).TypeError, function() {2156 desc.get.call({});2157 }.bind(this), "calling getter on wrong object type must throw TypeError");2158 } else {2159 pendingPromises.push(2160 promise_rejects_js(a_test, TypeError, desc.get.call({}),2161 "calling getter on wrong object type must reject the return promise with TypeError"));2162 }2163 } else {2164 assert_equals(desc.get.call({}), undefined,2165 "calling getter on wrong object type must return undefined");2166 }2167 }2168 assert_equals(desc.get.length, 0, "getter length must be 0");2169 assert_equals(desc.get.name, "get " + member.name,2170 "getter must have the name 'get " + member.name + "'");2171 if (member.readonly2172 && !member.has_extended_attribute("LegacyLenientSetter")2173 && !member.has_extended_attribute("PutForwards")2174 && !member.has_extended_attribute("Replaceable"))2175 {2176 assert_equals(desc.set, undefined, "setter must be undefined for readonly attributes");2177 }2178 else2179 {2180 assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes");2181 if (member.special !== "static") {2182 if (!member.has_extended_attribute("LegacyLenientThis")) {2183 assert_throws_js(globalOf(desc.set).TypeError, function() {2184 desc.set.call({});2185 }.bind(this), "calling setter on wrong object type must throw TypeError");2186 } else {2187 assert_equals(desc.set.call({}), undefined,2188 "calling setter on wrong object type must return undefined");2189 }2190 }2191 assert_equals(desc.set.length, 1, "setter length must be 1");2192 assert_equals(desc.set.name, "set " + member.name,2193 "The attribute setter must have the name 'set " + member.name + "'");2194 }2195 Promise.all(pendingPromises).then(a_test.done.bind(a_test));2196}2197function IdlInterfaceMember(obj)2198{2199 * obj is an object produced by the WebIDLParser.js "ifMember" production.2200 * We just forward all properties to this object without modification,2201 * except for special extAttrs handling.2202 for (var k in obj.toJSON())2203 {2204 this[k] = obj[k];2205 }2206 if (!("extAttrs" in this))2207 {2208 this.extAttrs = [];2209 }2210 this.isUnforgeable = this.has_extended_attribute("LegacyUnforgeable");2211 this.isUnscopable = this.has_extended_attribute("Unscopable");2212}2213IdlInterfaceMember.prototype = Object.create(IdlObject.prototype);2214IdlInterfaceMember.prototype.toJSON = function() {2215 return this;2216};2217IdlInterfaceMember.prototype.is_to_json_regular_operation = function() {2218 return this.type == "operation" && this.special !== "static" && this.name == "toJSON";2219};2220IdlInterfaceMember.prototype.toString = function() {2221 function formatType(type) {2222 var result;2223 if (type.generic) {2224 result = type.generic + "<" + type.idlType.map(formatType).join(", ") + ">";2225 } else if (type.union) {2226 result = "(" + type.subtype.map(formatType).join(" or ") + ")";2227 } else {2228 result = type.idlType;2229 }2230 if (type.nullable) {2231 result += "?"2232 }2233 return result;2234 }2235 if (this.type === "operation") {2236 var args = this.arguments.map(function(m) {2237 return [2238 m.optional ? "optional " : "",2239 formatType(m.idlType),2240 m.variadic ? "..." : "",2241 ].join("");2242 }).join(", ");2243 return this.name + "(" + args + ")";2244 }2245 return this.name;2246}2247function create_suitable_object(type)2248{2249 * type is an object produced by the WebIDLParser.js "type" production. We2250 * return a JavaScript value that matches the type, if we can figure out2251 * how.2252 if (type.nullable)2253 {2254 return null;2255 }2256 switch (type.idlType)2257 {2258 case "any":2259 case "boolean":2260 return true;2261 case "byte": case "octet": case "short": case "unsigned short":2262 case "long": case "unsigned long": case "long long":2263 case "unsigned long long": case "float": case "double":2264 case "unrestricted float": case "unrestricted double":2265 return 7;2266 case "DOMString":2267 case "ByteString":2268 case "USVString":2269 return "foo";2270 case "object":2271 return {a: "b"};2272 case "Node":2273 return document.createTextNode("abc");2274 }2275 return null;2276}2277function IdlEnum(obj)2278{2279 * obj is an object produced by the WebIDLParser.js "dictionary"2280 * production.2281 this.name = obj.name;2282 this.values = obj.values;2283}2284IdlEnum.prototype = Object.create(IdlObject.prototype);2285function IdlCallback(obj)2286{2287 * obj is an object produced by the WebIDLParser.js "callback"2288 * production.2289 this.name = obj.name;2290 this.arguments = obj.arguments;2291}2292IdlCallback.prototype = Object.create(IdlObject.prototype);2293function IdlTypedef(obj)2294{2295 * obj is an object produced by the WebIDLParser.js "typedef"2296 * production.2297 this.name = obj.name;2298 this.idlType = obj.idlType;2299}2300IdlTypedef.prototype = Object.create(IdlObject.prototype);2301function IdlNamespace(obj)2302{2303 this.name = obj.name;2304 this.extAttrs = obj.extAttrs;2305 this.untested = obj.untested;2306 this.array = obj.array;2307 this.members = obj.members.map(m => new IdlInterfaceMember(m));2308}2309IdlNamespace.prototype = Object.create(IdlObject.prototype);2310IdlNamespace.prototype.do_member_operation_asserts = function (memberHolderObject, member, a_test)2311{2312 var desc = Object.getOwnPropertyDescriptor(memberHolderObject, member.name);2313 assert_false("get" in desc, "property should not have a getter");2314 assert_false("set" in desc, "property should not have a setter");2315 assert_equals(2316 desc.writable,2317 !member.isUnforgeable,2318 "property should be writable if and only if not unforgeable");2319 assert_true(desc.enumerable, "property should be enumerable");2320 assert_equals(2321 desc.configurable,2322 !member.isUnforgeable,2323 "property should be configurable if and only if not unforgeable");2324 assert_equals(2325 typeof memberHolderObject[member.name],2326 "function",2327 "property must be a function");2328 assert_equals(2329 memberHolderObject[member.name].length,2330 minOverloadLength(this.members.filter(function(m) {2331 return m.type == "operation" && m.name == member.name;2332 })),2333 "operation has wrong .length");2334 a_test.done();2335}2336IdlNamespace.prototype.test_member_operation = function(member)2337{2338 if (!shouldRunSubTest(this.name)) {2339 return;2340 }2341 var a_test = subsetTestByKey(2342 this.name,2343 async_test,2344 this.name + ' namespace: operation ' + member);2345 a_test.step(function() {2346 assert_own_property(2347 self[this.name],2348 member.name,2349 'namespace object missing operation ' + format_value(member.name));2350 this.do_member_operation_asserts(self[this.name], member, a_test);2351 }.bind(this));2352};2353IdlNamespace.prototype.test_member_attribute = function (member)2354{2355 if (!shouldRunSubTest(this.name)) {2356 return;2357 }2358 var a_test = subsetTestByKey(2359 this.name,2360 async_test,2361 this.name + ' namespace: attribute ' + member.name);2362 a_test.step(function()2363 {2364 assert_own_property(2365 self[this.name],2366 member.name,2367 this.name + ' does not have property ' + format_value(member.name));2368 var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name);2369 assert_equals(desc.set, undefined, "setter must be undefined for namespace members");2370 a_test.done();2371 }.bind(this));2372};2373IdlNamespace.prototype.test_self = function ()2374{2375 * TODO(lukebjerring): Assert:2376 * - "Note that unlike interfaces or dictionaries, namespaces do not create types."2377 subsetTestByKey(this.name, test, () => {2378 assert_true(this.extAttrs.every(o => o.name === "Exposed" || o.name === "SecureContext"),2379 "Only the [Exposed] and [SecureContext] extended attributes are applicable to namespaces");2380 assert_true(this.has_extended_attribute("Exposed"),2381 "Namespaces must be annotated with the [Exposed] extended attribute");2382 }, `${this.name} namespace: extended attributes`);2383 const namespaceObject = self[this.name];2384 subsetTestByKey(this.name, test, () => {2385 const desc = Object.getOwnPropertyDescriptor(self, this.name);2386 assert_equals(desc.value, namespaceObject, `wrong value for ${this.name} namespace object`);2387 assert_true(desc.writable, "namespace object should be writable");2388 assert_false(desc.enumerable, "namespace object should not be enumerable");2389 assert_true(desc.configurable, "namespace object should be configurable");2390 assert_false("get" in desc, "namespace object should not have a getter");2391 assert_false("set" in desc, "namespace object should not have a setter");2392 }, `${this.name} namespace: property descriptor`);2393 subsetTestByKey(this.name, test, () => {2394 assert_true(Object.isExtensible(namespaceObject));2395 }, `${this.name} namespace: [[Extensible]] is true`);2396 subsetTestByKey(this.name, test, () => {2397 assert_true(namespaceObject instanceof Object);2398 if (this.name === "console") {2399 const namespacePrototype = Object.getPrototypeOf(namespaceObject);2400 assert_equals(Reflect.ownKeys(namespacePrototype).length, 0);2401 assert_equals(Object.getPrototypeOf(namespacePrototype), Object.prototype);2402 } else {2403 assert_equals(Object.getPrototypeOf(namespaceObject), Object.prototype);2404 }2405 }, `${this.name} namespace: [[Prototype]] is Object.prototype`);2406 subsetTestByKey(this.name, test, () => {2407 assert_equals(typeof namespaceObject, "object");2408 }, `${this.name} namespace: typeof is "object"`);2409 subsetTestByKey(this.name, test, () => {2410 assert_equals(2411 Object.getOwnPropertyDescriptor(namespaceObject, "length"),2412 undefined,2413 "length property must be undefined"2414 );2415 }, `${this.name} namespace: has no length property`);2416 subsetTestByKey(this.name, test, () => {2417 assert_equals(2418 Object.getOwnPropertyDescriptor(namespaceObject, "name"),2419 undefined,2420 "name property must be undefined"2421 );2422 }, `${this.name} namespace: has no name property`);2423};2424IdlNamespace.prototype.test = function ()2425{2426 if (!this.untested) {2427 this.test_self();2428 }2429 for (const v of Object.values(this.members)) {2430 switch (v.type) {2431 case 'operation':2432 this.test_member_operation(v);2433 break;2434 case 'attribute':2435 this.test_member_attribute(v);2436 break;2437 default:2438 throw 'Invalid namespace member ' + v.name + ': ' + v.type + ' not supported';2439 }2440 };2441};2442}());2443 * idl_test is a promise_test wrapper that handles the fetching of the IDL,2444 * avoiding repetitive boilerplate.2445 *2446 * @param {String|String[]} srcs Spec name(s) for source idl files (fetched from2447 * @param {String|String[]} deps Spec name(s) for dependency idl files (fetched2448 * each source will only be included if they're already know to be a2449 * dependency (i.e. have already been seen).2450 * @param {Function} setup_func Function for extra setup of the idl_array, such2451 * as adding objects. Do not call idl_array.test() in the setup; it is2452 * called by this function (idl_test).2453function idl_test(srcs, deps, idl_setup_func) {2454 return promise_test(function (t) {2455 var idl_array = new IdlArray();2456 var setup_error = null;2457 const validationIgnored = [2458 "constructor-member",2459 "dict-arg-default",2460 "require-exposed"2461 ];2462 return Promise.all(2463 srcs.concat(deps).map(fetch_spec))2464 .then(function(results) {2465 const astArray = results.map(result =>2466 WebIDL2.parse(result.idl, { sourceName: result.spec })2467 );2468 test(() => {2469 const validations = WebIDL2.validate(astArray)2470 .filter(v => !validationIgnored.includes(v.ruleName));2471 if (validations.length) {2472 const message = validations.map(v => v.message).join("\n\n");2473 throw new Error(message);2474 }2475 }, "idl_test validation");2476 for (var i = 0; i < srcs.length; i++) {2477 idl_array.internal_add_idls(astArray[i]);2478 }2479 for (var i = srcs.length; i < srcs.length + deps.length; i++) {2480 idl_array.internal_add_dependency_idls(astArray[i]);2481 }2482 })2483 .then(function() {2484 if (idl_setup_func) {2485 return idl_setup_func(idl_array, t);2486 }2487 })2488 .catch(function(e) { setup_error = e || 'IDL setup failed.'; })2489 .then(function () {2490 var error = setup_error;2491 try {2492 } catch (e) {2493 error = error || e;2494 }2495 if (error) {2496 throw error;2497 }2498 });2499 }, 'idl_test setup');2500}2501 * fetch_spec is a shorthand for a Promise that fetches the spec's content.2502function fetch_spec(spec) {2503 return fetch(url).then(function (r) {2504 if (!r.ok) {2505 throw new IdlHarnessError("Error fetching " + url + ".");2506 }2507 return r.text();2508 }).then(idl => ({ spec, idl }));...
Using AI Code Generation
1var wpt = require('wpt');2wpt.testedMembers(function(err, data) {3 if (err) {4 console.log(err);5 } else {6 console.log(data);7 }8});9var wpt = require('wpt');10wpt.testedMembers(function(err, data) {11 if (err) {12 console.log(err);13 } else {14 console.log(data);15 }16});17### wpt.testedMembers([options], callback)
Using AI Code Generation
1var wpt = require('wpt');2var wpt = new wpt('API_KEY');3wpt.testedMembers(function(err, data) {4 if (err) throw err;5 console.log(data);6});7var wpt = require('wpt');8var wpt = new wpt('API_KEY');9wpt.testedMembers(function(err, data) {10 if (err) throw err;11 console.log(data);12});13var wpt = require('wpt');14var wpt = new wpt('API_KEY');15wpt.testedMembers(function(err, data) {16 if (err) throw err;17 console.log(data);18});19var wpt = require('wpt');20var wpt = new wpt('API_KEY');21wpt.testedMembers(function(err, data) {22 if (err) throw err;23 console.log(data);24});25var wpt = require('wpt');26var wpt = new wpt('API_KEY');27wpt.testedMembers(function(err, data) {28 if (err) throw err;29 console.log(data);30});31var wpt = require('wpt');32var wpt = new wpt('API_KEY');33wpt.testedMembers(function(err, data) {34 if (err) throw err;35 console.log(data);36});37var wpt = require('wpt');38var wpt = new wpt('API_KEY');39wpt.testedMembers(function(err, data) {40 if (err) throw err;41 console.log(data);42});43var wpt = require('wpt');44var wpt = new wpt('API_KEY');45wpt.testedMembers(function(err, data) {46 if (err) throw err;47 console.log(data);48});
Using AI Code Generation
1var wptest = require('./wptest.js');2wptest.testedMembers('test');3module.exports = {4 testedMembers: function (test) {5 console.log('testedMembers called with ' + test);6 }7};8var wptest = require('./wptest.js');9wptest.testedMembers('test');10module.exports = {11 testedMembers: function (test) {12 console.log('testedMembers called with ' + test);13 }14};15var wptest = require('./wptest.js');16wptest.testedMembers('test');17module.exports = {18 testedMembers: function (test) {19 console.log('testedMembers called with ' + test);20 }21};22var wptest = require('./wptest.js');23wptest.testedMembers('test');24module.exports = {25 testedMembers: function (test) {26 console.log('testedMembers called with ' + test);27 }28};29var wptest = require('./wptest.js');30wptest.testedMembers('test');31module.exports = {32 testedMembers: function (test) {33 console.log('testedMembers called with ' + test);34 }35};36var wptest = require('./wptest.js');37wptest.testedMembers('test');38module.exports = {39 testedMembers: function (test) {40 console.log('testedMembers called with ' + test);41 }42};43var wptest = require('./wptest.js');44wptest.testedMembers('test');
Using AI Code Generation
1var wptest = require('./wptest');2var members = wptest.testedMembers();3console.log(members);4module.exports = {5 testedMembers: function() {6 return ['a', 'b', 'c'];7 }8};9var wptest = require('../wptest');10var members = wptest.testedMembers();11console.log(members);12var wptest = require('../wptest');13var members = wptest.testedMembers();14console.log(members);15var wptest = require('../wptest');16var members = wptest.testedMembers();17console.log(members);
Using AI Code Generation
1var testedMembers = require('./wpt.js');2var assert = require('assert');3describe('wpt', function() {4 describe('testedMembers', function() {5 it('should return the list of members who have been tested', function() {6 var wpt = new Wpt();7 assert.deepEqual(wpt.testedMembers(), ['John', 'Mary']);8 });9 });10});11var assert = require('assert');12var wpt = require('./wpt.js');13describe('wpt', function() {14 describe('testedMembers', function() {15 it('should return the list of members who have been tested', function() {16 var wpt = new Wpt();17 assert.deepEqual(wpt.testedMembers(), ['John', 'Mary']);18 });19 });20});21var Wpt = function() {22 { name: 'John', tested: true },23 { name: 'Mary', tested: true },24 { name: 'Bob', tested: false }25 ];26};27Wpt.prototype.testedMembers = function() {28 return this.members.filter(function(member) {29 return member.tested;30 }).map(function(member) {31 return member.name;32 });33};34module.exports = Wpt;35var Wpt = function() {36 { name: 'John', tested: true },37 { name: 'Mary', tested: true },38 { name: 'Bob', tested: false }39 ];40};41Wpt.prototype.testedMembers = function() {42 return this.members.filter(function(member) {43 return member.tested;44 }).map(function(member) {45 return member.name;46 });47};48module.exports = Wpt;49var Wpt = function() {50 { name: 'John', tested: true },51 { name: 'Mary', tested: true },52 { name: 'Bob', tested: false }53 ];54};55Wpt.prototype.testedMembers = function() {56 return this.members.filter(function(member) {57 return member.tested;58 }).map(function(member) {59 return member.name;60 });61};62module.exports = Wpt;63var Wpt = function()
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!!