Best JavaScript code snippet using wpt
idlharness.js
Source:idlharness.js
1/* For user documentation see docs/_writing-tests/idlharness.md */2/**3 * Notes for people who want to edit this file (not just use it as a library):4 *5 * Most of the interesting stuff happens in the derived classes of IdlObject,6 * especially IdlInterface. The entry point for all IdlObjects is .test(),7 * which is called by IdlArray.test(). An IdlObject is conceptually just8 * "thing we want to run tests on", and an IdlArray is an array of IdlObjects9 * with some additional data thrown in.10 *11 * The object model is based on what WebIDLParser.js produces, which is in turn12 * based on its pegjs grammar. If you want to figure out what properties an13 * object will have from WebIDLParser.js, the best way is to look at the14 * grammar:15 *16 * https://github.com/darobin/webidl.js/blob/master/lib/grammar.peg17 *18 * So for instance:19 *20 * // interface definition21 * interface22 * = extAttrs:extendedAttributeList? S? "interface" S name:identifier w herit:ifInheritance? w "{" w mem:ifMember* w "}" w ";" w23 * { return { type: "interface", name: name, inheritance: herit, members: mem, extAttrs: extAttrs }; }24 *25 * This means that an "interface" object will have a .type property equal to26 * the string "interface", a .name property equal to the identifier that the27 * parser found, an .inheritance property equal to either null or the result of28 * the "ifInheritance" production found elsewhere in the grammar, and so on.29 * After each grammatical production is a JavaScript function in curly braces30 * that gets called with suitable arguments and returns some JavaScript value.31 *32 * (Note that the version of WebIDLParser.js we use might sometimes be33 * out-of-date or forked.)34 *35 * The members and methods of the classes defined by this file are all at least36 * briefly documented, hopefully.37 */38(function(){39"use strict";40// Support subsetTestByKey from /common/subset-tests-by-key.js, but make it optional41if (!('subsetTestByKey' in self)) {42 self.subsetTestByKey = function(key, callback, ...args) {43 return callback(...args);44 }45 self.shouldRunSubTest = () => true;46}47/// Helpers ///48function constValue (cnt)49{50 if (cnt.type === "null") return null;51 if (cnt.type === "NaN") return NaN;52 if (cnt.type === "Infinity") return cnt.negative ? -Infinity : Infinity;53 if (cnt.type === "number") return +cnt.value;54 return cnt.value;55}56function minOverloadLength(overloads)57{58 // "The value of the Function objectâs âlengthâ property is59 // a Number determined as follows:60 // ". . .61 // "Return the length of the shortest argument list of the62 // entries in S."63 if (!overloads.length) {64 return 0;65 }66 return overloads.map(function(attr) {67 return attr.arguments ? attr.arguments.filter(function(arg) {68 return !arg.optional && !arg.variadic;69 }).length : 0;70 })71 .reduce(function(m, n) { return Math.min(m, n); });72}73// A helper to get the global of a Function object. This is needed to determine74// which global exceptions the function throws will come from.75function globalOf(func)76{77 try {78 // Use the fact that .constructor for a Function object is normally the79 // Function constructor, which can be used to mint a new function in the80 // right global.81 return func.constructor("return this;")();82 } catch (e) {83 }84 // If the above fails, because someone gave us a non-function, or a function85 // with a weird proto chain or weird .constructor property, just fall back86 // to 'self'.87 return self;88}89// https://esdiscuss.org/topic/isconstructor#content-1190function isConstructor(o) {91 try {92 new (new Proxy(o, {construct: () => ({})}));93 return true;94 } catch(e) {95 return false;96 }97}98function throwOrReject(a_test, operation, fn, obj, args, message, cb)99{100 if (operation.idlType.generic !== "Promise") {101 assert_throws_js(globalOf(fn).TypeError, function() {102 fn.apply(obj, args);103 }, message);104 cb();105 } else {106 try {107 promise_rejects_js(a_test, TypeError, fn.apply(obj, args), message).then(cb, cb);108 } catch (e){109 a_test.step(function() {110 assert_unreached("Throws \"" + e + "\" instead of rejecting promise");111 cb();112 });113 }114 }115}116function awaitNCallbacks(n, cb, ctx)117{118 var counter = 0;119 return function() {120 counter++;121 if (counter >= n) {122 cb();123 }124 };125}126/// IdlHarnessError ///127// Entry point128self.IdlHarnessError = function(message)129{130 /**131 * Message to be printed as the error's toString invocation.132 */133 this.message = message;134};135IdlHarnessError.prototype = Object.create(Error.prototype);136IdlHarnessError.prototype.toString = function()137{138 return this.message;139};140/// IdlArray ///141// Entry point142self.IdlArray = function()143{144 /**145 * A map from strings to the corresponding named IdlObject, such as146 * IdlInterface or IdlException. These are the things that test() will run147 * tests on.148 */149 this.members = {};150 /**151 * A map from strings to arrays of strings. The keys are interface or152 * exception names, and are expected to also exist as keys in this.members153 * (otherwise they'll be ignored). This is populated by add_objects() --154 * see documentation at the start of the file. The actual tests will be155 * run by calling this.members[name].test_object(obj) for each obj in156 * this.objects[name]. obj is a string that will be eval'd to produce a157 * JavaScript value, which is supposed to be an object implementing the158 * given IdlObject (interface, exception, etc.).159 */160 this.objects = {};161 /**162 * When adding multiple collections of IDLs one at a time, an earlier one163 * might contain a partial interface or includes statement that depends164 * on a later one. Save these up and handle them right before we run165 * tests.166 *167 * Both this.partials and this.includes will be the objects as parsed by168 * WebIDLParser.js, not wrapped in IdlInterface or similar.169 */170 this.partials = [];171 this.includes = [];172 /**173 * Record of skipped IDL items, in case we later realize that they are a174 * dependency (to retroactively process them).175 */176 this.skipped = new Map();177};178IdlArray.prototype.add_idls = function(raw_idls, options)179{180 /** Entry point. See documentation at beginning of file. */181 this.internal_add_idls(WebIDL2.parse(raw_idls), options);182};183IdlArray.prototype.add_untested_idls = function(raw_idls, options)184{185 /** Entry point. See documentation at beginning of file. */186 var parsed_idls = WebIDL2.parse(raw_idls);187 this.mark_as_untested(parsed_idls);188 this.internal_add_idls(parsed_idls, options);189};190IdlArray.prototype.mark_as_untested = function (parsed_idls)191{192 for (var i = 0; i < parsed_idls.length; i++) {193 parsed_idls[i].untested = true;194 if ("members" in parsed_idls[i]) {195 for (var j = 0; j < parsed_idls[i].members.length; j++) {196 parsed_idls[i].members[j].untested = true;197 }198 }199 }200};201IdlArray.prototype.is_excluded_by_options = function (name, options)202{203 return options &&204 (options.except && options.except.includes(name)205 || options.only && !options.only.includes(name));206};207IdlArray.prototype.add_dependency_idls = function(raw_idls, options)208{209 return this.internal_add_dependency_idls(WebIDL2.parse(raw_idls), options);210};211IdlArray.prototype.internal_add_dependency_idls = function(parsed_idls, options)212{213 const new_options = { only: [] }214 const all_deps = new Set();215 Object.values(this.members).forEach(v => {216 if (v.base) {217 all_deps.add(v.base);218 }219 });220 // Add both 'A' and 'B' for each 'A includes B' entry.221 this.includes.forEach(i => {222 all_deps.add(i.target);223 all_deps.add(i.includes);224 });225 this.partials.forEach(p => all_deps.add(p.name));226 // Add 'TypeOfType' for each "typedef TypeOfType MyType;" entry.227 Object.entries(this.members).forEach(([k, v]) => {228 if (v instanceof IdlTypedef) {229 let defs = v.idlType.union230 ? v.idlType.idlType.map(t => t.idlType)231 : [v.idlType.idlType];232 defs.forEach(d => all_deps.add(d));233 }234 });235 // Add the attribute idlTypes of all the nested members of idls.236 const attrDeps = parsedIdls => {237 return parsedIdls.reduce((deps, parsed) => {238 if (parsed.members) {239 for (const attr of Object.values(parsed.members).filter(m => m.type === 'attribute')) {240 let attrType = attr.idlType;241 // Check for generic members (e.g. FrozenArray<MyType>)242 if (attrType.generic) {243 deps.add(attrType.generic);244 attrType = attrType.idlType;245 }246 deps.add(attrType.idlType);247 }248 }249 if (parsed.base in this.members) {250 attrDeps([this.members[parsed.base]]).forEach(dep => deps.add(dep));251 }252 return deps;253 }, new Set());254 };255 const testedMembers = Object.values(this.members).filter(m => !m.untested && m.members);256 attrDeps(testedMembers).forEach(dep => all_deps.add(dep));257 const testedPartials = this.partials.filter(m => !m.untested && m.members);258 attrDeps(testedPartials).forEach(dep => all_deps.add(dep));259 if (options && options.except && options.only) {260 throw new IdlHarnessError("The only and except options can't be used together.");261 }262 const defined_or_untested = name => {263 // NOTE: Deps are untested, so we're lenient, and skip re-encountered definitions.264 // e.g. for 'idl' containing A:B, B:C, C:D265 // array.add_idls(idl, {only: ['A','B']}).266 // array.add_dependency_idls(idl);267 // B would be encountered as tested, and encountered as a dep, so we ignore.268 return name in this.members269 || this.is_excluded_by_options(name, options);270 }271 // Maps name -> [parsed_idl, ...]272 const process = function(parsed) {273 var deps = [];274 if (parsed.name) {275 deps.push(parsed.name);276 } else if (parsed.type === "includes") {277 deps.push(parsed.target);278 deps.push(parsed.includes);279 }280 deps = deps.filter(function(name) {281 if (!name282 || name === parsed.name && defined_or_untested(name)283 || !all_deps.has(name)) {284 // Flag as skipped, if it's not already processed, so we can285 // come back to it later if we retrospectively call it a dep.286 if (name && !(name in this.members)) {287 this.skipped.has(name)288 ? this.skipped.get(name).push(parsed)289 : this.skipped.set(name, [parsed]);290 }291 return false;292 }293 return true;294 }.bind(this));295 deps.forEach(function(name) {296 if (!new_options.only.includes(name)) {297 new_options.only.push(name);298 }299 const follow_up = new Set();300 for (const dep_type of ["inheritance", "includes"]) {301 if (parsed[dep_type]) {302 const inheriting = parsed[dep_type];303 const inheritor = parsed.name || parsed.target;304 const deps = [inheriting];305 // For A includes B, we can ignore A, unless B (or some of its306 // members) is being tested.307 if (dep_type !== "includes"308 || inheriting in this.members && !this.members[inheriting].untested309 || this.partials.some(function(p) {310 return p.name === inheriting;311 })) {312 deps.push(inheritor);313 }314 for (const dep of deps) {315 if (!new_options.only.includes(dep)) {316 new_options.only.push(dep);317 }318 all_deps.add(dep);319 follow_up.add(dep);320 }321 }322 }323 for (const deferred of follow_up) {324 if (this.skipped.has(deferred)) {325 const next = this.skipped.get(deferred);326 this.skipped.delete(deferred);327 next.forEach(process);328 }329 }330 }.bind(this));331 }.bind(this);332 for (let parsed of parsed_idls) {333 process(parsed);334 }335 this.mark_as_untested(parsed_idls);336 if (new_options.only.length) {337 this.internal_add_idls(parsed_idls, new_options);338 }339}340IdlArray.prototype.internal_add_idls = function(parsed_idls, options)341{342 /**343 * Internal helper called by add_idls() and add_untested_idls().344 *345 * parsed_idls is an array of objects that come from WebIDLParser.js's346 * "definitions" production. The add_untested_idls() entry point347 * additionally sets an .untested property on each object (and its348 * .members) so that they'll be skipped by test() -- they'll only be349 * used for base interfaces of tested interfaces, return types, etc.350 *351 * options is a dictionary that can have an only or except member which are352 * arrays. If only is given then only members, partials and interface353 * targets listed will be added, and if except is given only those that354 * aren't listed will be added. Only one of only and except can be used.355 */356 if (options && options.only && options.except)357 {358 throw new IdlHarnessError("The only and except options can't be used together.");359 }360 var should_skip = name => {361 return this.is_excluded_by_options(name, options);362 }363 parsed_idls.forEach(function(parsed_idl)364 {365 var partial_types = [366 "interface",367 "interface mixin",368 "dictionary",369 "namespace",370 ];371 if (parsed_idl.partial && partial_types.includes(parsed_idl.type))372 {373 if (should_skip(parsed_idl.name))374 {375 return;376 }377 this.partials.push(parsed_idl);378 return;379 }380 if (parsed_idl.type == "includes")381 {382 if (should_skip(parsed_idl.target))383 {384 return;385 }386 this.includes.push(parsed_idl);387 return;388 }389 parsed_idl.array = this;390 if (should_skip(parsed_idl.name))391 {392 return;393 }394 if (parsed_idl.name in this.members)395 {396 throw new IdlHarnessError("Duplicate identifier " + parsed_idl.name);397 }398 switch(parsed_idl.type)399 {400 case "interface":401 this.members[parsed_idl.name] =402 new IdlInterface(parsed_idl, /* is_callback = */ false, /* is_mixin = */ false);403 break;404 case "interface mixin":405 this.members[parsed_idl.name] =406 new IdlInterface(parsed_idl, /* is_callback = */ false, /* is_mixin = */ true);407 break;408 case "dictionary":409 // Nothing to test, but we need the dictionary info around for type410 // checks411 this.members[parsed_idl.name] = new IdlDictionary(parsed_idl);412 break;413 case "typedef":414 this.members[parsed_idl.name] = new IdlTypedef(parsed_idl);415 break;416 case "callback":417 this.members[parsed_idl.name] = new IdlCallback(parsed_idl);418 break;419 case "enum":420 this.members[parsed_idl.name] = new IdlEnum(parsed_idl);421 break;422 case "callback interface":423 this.members[parsed_idl.name] =424 new IdlInterface(parsed_idl, /* is_callback = */ true, /* is_mixin = */ false);425 break;426 case "namespace":427 this.members[parsed_idl.name] = new IdlNamespace(parsed_idl);428 break;429 default:430 throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported";431 }432 }.bind(this));433};434IdlArray.prototype.add_objects = function(dict)435{436 /** Entry point. See documentation at beginning of file. */437 for (var k in dict)438 {439 if (k in this.objects)440 {441 this.objects[k] = this.objects[k].concat(dict[k]);442 }443 else444 {445 this.objects[k] = dict[k];446 }447 }448};449IdlArray.prototype.prevent_multiple_testing = function(name)450{451 /** Entry point. See documentation at beginning of file. */452 this.members[name].prevent_multiple_testing = true;453};454IdlArray.prototype.is_json_type = function(type)455{456 /**457 * Checks whether type is a JSON type as per458 * https://heycam.github.io/webidl/#dfn-json-types459 */460 var idlType = type.idlType;461 if (type.generic == "Promise") { return false; }462 // nullable and annotated types don't need to be handled separately,463 // as webidl2 doesn't represent them wrapped-up (as they're described464 // in WebIDL).465 // union and record types466 if (type.union || type.generic == "record") {467 return idlType.every(this.is_json_type, this);468 }469 // sequence types470 if (type.generic == "sequence" || type.generic == "FrozenArray") {471 return this.is_json_type(idlType[0]);472 }473 if (typeof idlType != "string") { throw new Error("Unexpected type " + JSON.stringify(idlType)); }474 switch (idlType)475 {476 // Numeric types477 case "byte":478 case "octet":479 case "short":480 case "unsigned short":481 case "long":482 case "unsigned long":483 case "long long":484 case "unsigned long long":485 case "float":486 case "double":487 case "unrestricted float":488 case "unrestricted double":489 // boolean490 case "boolean":491 // string types492 case "DOMString":493 case "ByteString":494 case "USVString":495 // object type496 case "object":497 return true;498 case "Error":499 case "DOMException":500 case "Int8Array":501 case "Int16Array":502 case "Int32Array":503 case "Uint8Array":504 case "Uint16Array":505 case "Uint32Array":506 case "Uint8ClampedArray":507 case "Float32Array":508 case "Float64Array":509 case "ArrayBuffer":510 case "DataView":511 case "any":512 return false;513 default:514 var thing = this.members[idlType];515 if (!thing) { throw new Error("Type " + idlType + " not found"); }516 if (thing instanceof IdlEnum) { return true; }517 if (thing instanceof IdlTypedef) {518 return this.is_json_type(thing.idlType);519 }520 // dictionaries where all of their members are JSON types521 if (thing instanceof IdlDictionary) {522 const map = new Map();523 for (const dict of thing.get_reverse_inheritance_stack()) {524 for (const m of dict.members) {525 map.set(m.name, m.idlType);526 }527 }528 return Array.from(map.values()).every(this.is_json_type, this);529 }530 // interface types that have a toJSON operation declared on themselves or531 // one of their inherited interfaces.532 if (thing instanceof IdlInterface) {533 var base;534 while (thing)535 {536 if (thing.has_to_json_regular_operation()) { return true; }537 var mixins = this.includes[thing.name];538 if (mixins) {539 mixins = mixins.map(function(id) {540 var mixin = this.members[id];541 if (!mixin) {542 throw new Error("Interface " + id + " not found (implemented by " + thing.name + ")");543 }544 return mixin;545 }, this);546 if (mixins.some(function(m) { return m.has_to_json_regular_operation() } )) { return true; }547 }548 if (!thing.base) { return false; }549 base = this.members[thing.base];550 if (!base) {551 throw new Error("Interface " + thing.base + " not found (inherited by " + thing.name + ")");552 }553 thing = base;554 }555 return false;556 }557 return false;558 }559};560function exposure_set(object, default_set) {561 var exposed = object.extAttrs && object.extAttrs.filter(a => a.name === "Exposed");562 if (exposed && exposed.length > 1) {563 throw new IdlHarnessError(564 `Multiple 'Exposed' extended attributes on ${object.name}`);565 }566 let result = default_set || ["Window"];567 if (result && !(result instanceof Set)) {568 result = new Set(result);569 }570 if (exposed && exposed.length) {571 const { rhs } = exposed[0];572 // Could be a list or a string.573 const set = rhs.type === "identifier-list" ?574 rhs.value.map(id => id.value) :575 [ rhs.value ];576 result = new Set(set);577 }578 if (result && result.has("Worker")) {579 result.delete("Worker");580 result.add("DedicatedWorker");581 result.add("ServiceWorker");582 result.add("SharedWorker");583 }584 return result;585}586function exposed_in(globals) {587 if ('Window' in self) {588 return globals.has("Window");589 }590 if ('DedicatedWorkerGlobalScope' in self &&591 self instanceof DedicatedWorkerGlobalScope) {592 return globals.has("DedicatedWorker");593 }594 if ('SharedWorkerGlobalScope' in self &&595 self instanceof SharedWorkerGlobalScope) {596 return globals.has("SharedWorker");597 }598 if ('ServiceWorkerGlobalScope' in self &&599 self instanceof ServiceWorkerGlobalScope) {600 return globals.has("ServiceWorker");601 }602 throw new IdlHarnessError("Unexpected global object");603}604/**605 * Asserts that the given error message is thrown for the given function.606 * @param {string|IdlHarnessError} error Expected Error message.607 * @param {Function} idlArrayFunc Function operating on an IdlArray that should throw.608 */609IdlArray.prototype.assert_throws = function(error, idlArrayFunc)610{611 try {612 idlArrayFunc.call(this, this);613 } catch (e) {614 if (e instanceof AssertionError) {615 throw e;616 }617 // Assertions for behaviour of the idlharness.js engine.618 if (error instanceof IdlHarnessError) {619 error = error.message;620 }621 if (e.message !== error) {622 throw new IdlHarnessError(`${idlArrayFunc} threw "${e}", not the expected IdlHarnessError "${error}"`);623 }624 return;625 }626 throw new IdlHarnessError(`${idlArrayFunc} did not throw the expected IdlHarnessError`);627}628IdlArray.prototype.test = function()629{630 /** Entry point. See documentation at beginning of file. */631 // First merge in all partial definitions and interface mixins.632 this.merge_partials();633 this.merge_mixins();634 // Assert B defined for A : B635 for (const member of Object.values(this.members).filter(m => m.base)) {636 const lhs = member.name;637 const rhs = member.base;638 if (!(rhs in this.members)) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${rhs} is undefined.`);639 const lhs_is_interface = this.members[lhs] instanceof IdlInterface;640 const rhs_is_interface = this.members[rhs] instanceof IdlInterface;641 if (rhs_is_interface != lhs_is_interface) {642 if (!lhs_is_interface) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${lhs} is not an interface.`);643 if (!rhs_is_interface) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${rhs} is not an interface.`);644 }645 // Check for circular dependencies.646 member.get_reverse_inheritance_stack();647 }648 Object.getOwnPropertyNames(this.members).forEach(function(memberName) {649 var member = this.members[memberName];650 if (!(member instanceof IdlInterface)) {651 return;652 }653 var globals = exposure_set(member);654 member.exposed = exposed_in(globals);655 member.exposureSet = globals;656 }.bind(this));657 // Now run test() on every member, and test_object() for every object.658 for (var name in this.members)659 {660 this.members[name].test();661 if (name in this.objects)662 {663 const objects = this.objects[name];664 if (!objects || !Array.isArray(objects)) {665 throw new IdlHarnessError(`Invalid or empty objects for member ${name}`);666 }667 objects.forEach(function(str)668 {669 if (!this.members[name] || !(this.members[name] instanceof IdlInterface)) {670 throw new IdlHarnessError(`Invalid object member name ${name}`);671 }672 this.members[name].test_object(str);673 }.bind(this));674 }675 }676};677IdlArray.prototype.merge_partials = function()678{679 const testedPartials = new Map();680 this.partials.forEach(function(parsed_idl)681 {682 const originalExists = parsed_idl.name in this.members683 && (this.members[parsed_idl.name] instanceof IdlInterface684 || this.members[parsed_idl.name] instanceof IdlDictionary685 || this.members[parsed_idl.name] instanceof IdlNamespace);686 // Ensure unique test name in case of multiple partials.687 let partialTestName = parsed_idl.name;688 let partialTestCount = 1;689 if (testedPartials.has(parsed_idl.name)) {690 partialTestCount += testedPartials.get(parsed_idl.name);691 partialTestName = `${partialTestName}[${partialTestCount}]`;692 }693 testedPartials.set(parsed_idl.name, partialTestCount);694 if (!parsed_idl.untested) {695 test(function () {696 assert_true(originalExists, `Original ${parsed_idl.type} should be defined`);697 var expected;698 switch (parsed_idl.type) {699 case 'dictionary': expected = IdlDictionary; break;700 case 'namespace': expected = IdlNamespace; break;701 case 'interface':702 case 'interface mixin':703 default:704 expected = IdlInterface; break;705 }706 assert_true(707 expected.prototype.isPrototypeOf(this.members[parsed_idl.name]),708 `Original ${parsed_idl.name} definition should have type ${parsed_idl.type}`);709 }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: original ${parsed_idl.type} defined`);710 }711 if (!originalExists) {712 // Not good.. but keep calm and carry on.713 return;714 }715 if (parsed_idl.extAttrs)716 {717 // Special-case "Exposed". Must be a subset of original interface's exposure.718 // Exposed on a partial is the equivalent of having the same Exposed on all nested members.719 // See https://github.com/heycam/webidl/issues/154 for discrepency between Exposed and720 // other extended attributes on partial interfaces.721 const exposureAttr = parsed_idl.extAttrs.find(a => a.name === "Exposed");722 if (exposureAttr) {723 if (!parsed_idl.untested) {724 test(function () {725 const partialExposure = exposure_set(parsed_idl);726 const memberExposure = exposure_set(this.members[parsed_idl.name]);727 partialExposure.forEach(name => {728 if (!memberExposure || !memberExposure.has(name)) {729 throw new IdlHarnessError(730 `Partial ${parsed_idl.name} ${parsed_idl.type} is exposed to '${name}', the original ${parsed_idl.type} is not.`);731 }732 });733 }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: valid exposure set`);734 }735 parsed_idl.members.forEach(function (member) {736 member.extAttrs.push(exposureAttr);737 }.bind(this));738 }739 parsed_idl.extAttrs.forEach(function(extAttr)740 {741 // "Exposed" already handled above.742 if (extAttr.name === "Exposed") {743 return;744 }745 this.members[parsed_idl.name].extAttrs.push(extAttr);746 }.bind(this));747 }748 if (parsed_idl.members.length) {749 test(function () {750 var clash = parsed_idl.members.find(function(member) {751 return this.members[parsed_idl.name].members.find(function(m) {752 return this.are_duplicate_members(m, member);753 }.bind(this));754 }.bind(this));755 parsed_idl.members.forEach(function(member)756 {757 this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member));758 }.bind(this));759 assert_true(!clash, "member " + (clash && clash.name) + " is unique");760 }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: member names are unique`);761 }762 }.bind(this));763 this.partials = [];764}765IdlArray.prototype.merge_mixins = function()766{767 for (const parsed_idl of this.includes)768 {769 const lhs = parsed_idl.target;770 const rhs = parsed_idl.includes;771 var errStr = lhs + " includes " + rhs + ", but ";772 if (!(lhs in this.members)) throw errStr + lhs + " is undefined.";773 if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface.";774 if (!(rhs in this.members)) throw errStr + rhs + " is undefined.";775 if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface.";776 if (this.members[rhs].members.length) {777 test(function () {778 var clash = this.members[rhs].members.find(function(member) {779 return this.members[lhs].members.find(function(m) {780 return this.are_duplicate_members(m, member);781 }.bind(this));782 }.bind(this));783 this.members[rhs].members.forEach(function(member) {784 assert_true(785 this.members[lhs].members.every(m => !this.are_duplicate_members(m, member)),786 "member " + member.name + " is unique");787 this.members[lhs].members.push(new IdlInterfaceMember(member));788 }.bind(this));789 assert_true(!clash, "member " + (clash && clash.name) + " is unique");790 }.bind(this), lhs + " includes " + rhs + ": member names are unique");791 }792 }793 this.includes = [];794}795IdlArray.prototype.are_duplicate_members = function(m1, m2) {796 if (m1.name !== m2.name) {797 return false;798 }799 if (m1.type === 'operation' && m2.type === 'operation'800 && m1.arguments.length !== m2.arguments.length) {801 // Method overload. TODO: Deep comparison of arguments.802 return false;803 }804 return true;805}806IdlArray.prototype.assert_type_is = function(value, type)807{808 if (type.idlType in this.members809 && this.members[type.idlType] instanceof IdlTypedef) {810 this.assert_type_is(value, this.members[type.idlType].idlType);811 return;812 }813 if (type.nullable && value === null)814 {815 // This is fine816 return;817 }818 if (type.union) {819 for (var i = 0; i < type.idlType.length; i++) {820 try {821 this.assert_type_is(value, type.idlType[i]);822 // No AssertionError, so we match one type in the union823 return;824 } catch(e) {825 if (e instanceof AssertionError) {826 // We didn't match this type, let's try some others827 continue;828 }829 throw e;830 }831 }832 // TODO: Is there a nice way to list the union's types in the message?833 assert_true(false, "Attribute has value " + format_value(value)834 + " which doesn't match any of the types in the union");835 }836 /**837 * Helper function that tests that value is an instance of type according838 * to the rules of WebIDL. value is any JavaScript value, and type is an839 * object produced by WebIDLParser.js' "type" production. That production840 * is fairly elaborate due to the complexity of WebIDL's types, so it's841 * best to look at the grammar to figure out what properties it might have.842 */843 if (type.idlType == "any")844 {845 // No assertions to make846 return;847 }848 if (type.array)849 {850 // TODO: not supported yet851 return;852 }853 if (type.generic === "sequence")854 {855 assert_true(Array.isArray(value), "should be an Array");856 if (!value.length)857 {858 // Nothing we can do.859 return;860 }861 this.assert_type_is(value[0], type.idlType[0]);862 return;863 }864 if (type.generic === "Promise") {865 assert_true("then" in value, "Attribute with a Promise type should have a then property");866 // TODO: Ideally, we would check on project fulfillment867 // that we get the right type868 // but that would require making the type check async869 return;870 }871 if (type.generic === "FrozenArray") {872 assert_true(Array.isArray(value), "Value should be array");873 assert_true(Object.isFrozen(value), "Value should be frozen");874 if (!value.length)875 {876 // Nothing we can do.877 return;878 }879 this.assert_type_is(value[0], type.idlType[0]);880 return;881 }882 type = Array.isArray(type.idlType) ? type.idlType[0] : type.idlType;883 switch(type)884 {885 case "undefined":886 assert_equals(value, undefined);887 return;888 case "boolean":889 assert_equals(typeof value, "boolean");890 return;891 case "byte":892 assert_equals(typeof value, "number");893 assert_equals(value, Math.floor(value), "should be an integer");894 assert_true(-128 <= value && value <= 127, "byte " + value + " should be in range [-128, 127]");895 return;896 case "octet":897 assert_equals(typeof value, "number");898 assert_equals(value, Math.floor(value), "should be an integer");899 assert_true(0 <= value && value <= 255, "octet " + value + " should be in range [0, 255]");900 return;901 case "short":902 assert_equals(typeof value, "number");903 assert_equals(value, Math.floor(value), "should be an integer");904 assert_true(-32768 <= value && value <= 32767, "short " + value + " should be in range [-32768, 32767]");905 return;906 case "unsigned short":907 assert_equals(typeof value, "number");908 assert_equals(value, Math.floor(value), "should be an integer");909 assert_true(0 <= value && value <= 65535, "unsigned short " + value + " should be in range [0, 65535]");910 return;911 case "long":912 assert_equals(typeof value, "number");913 assert_equals(value, Math.floor(value), "should be an integer");914 assert_true(-2147483648 <= value && value <= 2147483647, "long " + value + " should be in range [-2147483648, 2147483647]");915 return;916 case "unsigned long":917 assert_equals(typeof value, "number");918 assert_equals(value, Math.floor(value), "should be an integer");919 assert_true(0 <= value && value <= 4294967295, "unsigned long " + value + " should be in range [0, 4294967295]");920 return;921 case "long long":922 assert_equals(typeof value, "number");923 return;924 case "unsigned long long":925 case "DOMTimeStamp":926 assert_equals(typeof value, "number");927 assert_true(0 <= value, "unsigned long long should be positive");928 return;929 case "float":930 assert_equals(typeof value, "number");931 assert_equals(value, Math.fround(value), "float rounded to 32-bit float should be itself");932 assert_not_equals(value, Infinity);933 assert_not_equals(value, -Infinity);934 assert_not_equals(value, NaN);935 return;936 case "DOMHighResTimeStamp":937 case "double":938 assert_equals(typeof value, "number");939 assert_not_equals(value, Infinity);940 assert_not_equals(value, -Infinity);941 assert_not_equals(value, NaN);942 return;943 case "unrestricted float":944 assert_equals(typeof value, "number");945 assert_equals(value, Math.fround(value), "unrestricted float rounded to 32-bit float should be itself");946 return;947 case "unrestricted double":948 assert_equals(typeof value, "number");949 return;950 case "DOMString":951 assert_equals(typeof value, "string");952 return;953 case "ByteString":954 assert_equals(typeof value, "string");955 assert_regexp_match(value, /^[\x00-\x7F]*$/);956 return;957 case "USVString":958 assert_equals(typeof value, "string");959 assert_regexp_match(value, /^([\x00-\ud7ff\ue000-\uffff]|[\ud800-\udbff][\udc00-\udfff])*$/);960 return;961 case "ArrayBufferView":962 assert_true(ArrayBuffer.isView(value));963 return;964 case "object":965 assert_in_array(typeof value, ["object", "function"], "wrong type: not object or function");966 return;967 }968 // This is a catch-all for any IDL type name which follows JS class969 // semantics. This includes some non-interface IDL types (e.g. Int8Array,970 // Function, ...), as well as any interface types that are not in the IDL971 // that is fed to the harness. If an IDL type does not follow JS class972 // semantics then it should go in the switch statement above. If an IDL973 // type needs full checking, then the test should include it in the IDL it974 // feeds to the harness.975 if (!(type in this.members))976 {977 assert_true(value instanceof self[type], "wrong type: not a " + type);978 return;979 }980 if (this.members[type] instanceof IdlInterface)981 {982 // We don't want to run the full983 // IdlInterface.prototype.test_instance_of, because that could result984 // in an infinite loop. TODO: This means we don't have tests for985 // LegacyNoInterfaceObject interfaces, and we also can't test objects986 // that come from another self.987 assert_in_array(typeof value, ["object", "function"], "wrong type: not object or function");988 if (value instanceof Object989 && !this.members[type].has_extended_attribute("LegacyNoInterfaceObject")990 && type in self)991 {992 assert_true(value instanceof self[type], "instanceof " + type);993 }994 }995 else if (this.members[type] instanceof IdlEnum)996 {997 assert_equals(typeof value, "string");998 }999 else if (this.members[type] instanceof IdlDictionary)1000 {1001 // TODO: Test when we actually have something to test this on1002 }1003 else if (this.members[type] instanceof IdlCallback)1004 {1005 assert_equals(typeof value, "function");1006 }1007 else1008 {1009 throw new IdlHarnessError("Type " + type + " isn't an interface, callback or dictionary");1010 }1011};1012/// IdlObject ///1013function IdlObject() {}1014IdlObject.prototype.test = function()1015{1016 /**1017 * By default, this does nothing, so no actual tests are run for IdlObjects1018 * that don't define any (e.g., IdlDictionary at the time of this writing).1019 */1020};1021IdlObject.prototype.has_extended_attribute = function(name)1022{1023 /**1024 * This is only meaningful for things that support extended attributes,1025 * such as interfaces, exceptions, and members.1026 */1027 return this.extAttrs.some(function(o)1028 {1029 return o.name == name;1030 });1031};1032/// IdlDictionary ///1033// Used for IdlArray.prototype.assert_type_is1034function IdlDictionary(obj)1035{1036 /**1037 * obj is an object produced by the WebIDLParser.js "dictionary"1038 * production.1039 */1040 /** Self-explanatory. */1041 this.name = obj.name;1042 /** A back-reference to our IdlArray. */1043 this.array = obj.array;1044 /** An array of objects produced by the "dictionaryMember" production. */1045 this.members = obj.members;1046 /**1047 * The name (as a string) of the dictionary type we inherit from, or null1048 * if there is none.1049 */1050 this.base = obj.inheritance;1051}1052IdlDictionary.prototype = Object.create(IdlObject.prototype);1053IdlDictionary.prototype.get_reverse_inheritance_stack = function() {1054 return IdlInterface.prototype.get_reverse_inheritance_stack.call(this);1055};1056/// IdlInterface ///1057function IdlInterface(obj, is_callback, is_mixin)1058{1059 /**1060 * obj is an object produced by the WebIDLParser.js "interface" production.1061 */1062 /** Self-explanatory. */1063 this.name = obj.name;1064 /** A back-reference to our IdlArray. */1065 this.array = obj.array;1066 /**1067 * An indicator of whether we should run tests on the interface object and1068 * interface prototype object. Tests on members are controlled by .untested1069 * on each member, not this.1070 */1071 this.untested = obj.untested;1072 /** An array of objects produced by the "ExtAttr" production. */1073 this.extAttrs = obj.extAttrs;1074 /** An array of IdlInterfaceMembers. */1075 this.members = obj.members.map(function(m){return new IdlInterfaceMember(m); });1076 if (this.has_extended_attribute("LegacyUnforgeable")) {1077 this.members1078 .filter(function(m) { return m.special !== "static" && (m.type == "attribute" || m.type == "operation"); })1079 .forEach(function(m) { return m.isUnforgeable = true; });1080 }1081 /**1082 * The name (as a string) of the type we inherit from, or null if there is1083 * none.1084 */1085 this.base = obj.inheritance;1086 this._is_callback = is_callback;1087 this._is_mixin = is_mixin;1088}1089IdlInterface.prototype = Object.create(IdlObject.prototype);1090IdlInterface.prototype.is_callback = function()1091{1092 return this._is_callback;1093};1094IdlInterface.prototype.is_mixin = function()1095{1096 return this._is_mixin;1097};1098IdlInterface.prototype.has_constants = function()1099{1100 return this.members.some(function(member) {1101 return member.type === "const";1102 });1103};1104IdlInterface.prototype.get_unscopables = function()1105{1106 return this.members.filter(function(member) {1107 return member.isUnscopable;1108 });1109};1110IdlInterface.prototype.is_global = function()1111{1112 return this.extAttrs.some(function(attribute) {1113 return attribute.name === "Global";1114 });1115};1116/**1117 * Value of the LegacyNamespace extended attribute, if any.1118 *1119 * https://heycam.github.io/webidl/#LegacyNamespace1120 */1121IdlInterface.prototype.get_legacy_namespace = function()1122{1123 var legacyNamespace = this.extAttrs.find(function(attribute) {1124 return attribute.name === "LegacyNamespace";1125 });1126 return legacyNamespace ? legacyNamespace.rhs.value : undefined;1127};1128IdlInterface.prototype.get_interface_object_owner = function()1129{1130 var legacyNamespace = this.get_legacy_namespace();1131 return legacyNamespace ? self[legacyNamespace] : self;1132};1133IdlInterface.prototype.should_have_interface_object = function()1134{1135 // "For every interface that is exposed in a given ECMAScript global1136 // environment and:1137 // * is a callback interface that has constants declared on it, or1138 // * is a non-callback interface that is not declared with the1139 // [LegacyNoInterfaceObject] extended attribute,1140 // a corresponding property MUST exist on the ECMAScript global object.1141 return this.is_callback() ? this.has_constants() : !this.has_extended_attribute("LegacyNoInterfaceObject");1142};1143IdlInterface.prototype.assert_interface_object_exists = function()1144{1145 var owner = this.get_legacy_namespace() || "self";1146 assert_own_property(self[owner], this.name, owner + " does not have own property " + format_value(this.name));1147};1148IdlInterface.prototype.get_interface_object = function() {1149 if (!this.should_have_interface_object()) {1150 var reason = this.is_callback() ? "lack of declared constants" : "declared [LegacyNoInterfaceObject] attribute";1151 throw new IdlHarnessError(this.name + " has no interface object due to " + reason);1152 }1153 return this.get_interface_object_owner()[this.name];1154};1155IdlInterface.prototype.get_qualified_name = function() {1156 // https://heycam.github.io/webidl/#qualified-name1157 var legacyNamespace = this.get_legacy_namespace();1158 if (legacyNamespace) {1159 return legacyNamespace + "." + this.name;1160 }1161 return this.name;1162};1163IdlInterface.prototype.has_to_json_regular_operation = function() {1164 return this.members.some(function(m) {1165 return m.is_to_json_regular_operation();1166 });1167};1168IdlInterface.prototype.has_default_to_json_regular_operation = function() {1169 return this.members.some(function(m) {1170 return m.is_to_json_regular_operation() && m.has_extended_attribute("Default");1171 });1172};1173/**1174 * Implementation of https://heycam.github.io/webidl/#create-an-inheritance-stack1175 * with the order reversed.1176 *1177 * The order is reversed so that the base class comes first in the list, because1178 * this is what all call sites need.1179 *1180 * So given:1181 *1182 * A : B {};1183 * B : C {};1184 * C {};1185 *1186 * then A.get_reverse_inheritance_stack() returns [C, B, A],1187 * and B.get_reverse_inheritance_stack() returns [C, B].1188 *1189 * Note: as dictionary inheritance is expressed identically by the AST,1190 * this works just as well for getting a stack of inherited dictionaries.1191 */1192IdlInterface.prototype.get_reverse_inheritance_stack = function() {1193 const stack = [this];1194 let idl_interface = this;1195 while (idl_interface.base) {1196 const base = this.array.members[idl_interface.base];1197 if (!base) {1198 throw new Error(idl_interface.type + " " + idl_interface.base + " not found (inherited by " + idl_interface.name + ")");1199 } else if (stack.indexOf(base) > -1) {1200 stack.unshift(base);1201 const dep_chain = stack.map(i => i.name).join(',');1202 throw new IdlHarnessError(`${this.name} has a circular dependency: ${dep_chain}`);1203 }1204 idl_interface = base;1205 stack.unshift(idl_interface);1206 }1207 return stack;1208};1209/**1210 * Implementation of1211 * https://heycam.github.io/webidl/#default-tojson-operation1212 * for testing purposes.1213 *1214 * Collects the IDL types of the attributes that meet the criteria1215 * for inclusion in the default toJSON operation for easy1216 * comparison with actual value1217 */1218IdlInterface.prototype.default_to_json_operation = function() {1219 const map = new Map()1220 let isDefault = false;1221 for (const I of this.get_reverse_inheritance_stack()) {1222 if (I.has_default_to_json_regular_operation()) {1223 isDefault = true;1224 for (const m of I.members) {1225 if (m.special !== "static" && m.type == "attribute" && I.array.is_json_type(m.idlType)) {1226 map.set(m.name, m.idlType);1227 }1228 }1229 } else if (I.has_to_json_regular_operation()) {1230 isDefault = false;1231 }1232 }1233 return isDefault ? map : null;1234};1235IdlInterface.prototype.test = function()1236{1237 if (this.has_extended_attribute("LegacyNoInterfaceObject") || this.is_mixin())1238 {1239 // No tests to do without an instance. TODO: We should still be able1240 // to run tests on the prototype object, if we obtain one through some1241 // other means.1242 return;1243 }1244 // If the interface object is not exposed, only test that. Members can't be1245 // tested either, but objects could still be tested in |test_object|.1246 if (!this.exposed)1247 {1248 if (!this.untested)1249 {1250 subsetTestByKey(this.name, test, function() {1251 assert_false(this.name in self);1252 }.bind(this), this.name + " interface: existence and properties of interface object");1253 }1254 return;1255 }1256 if (!this.untested)1257 {1258 // First test things to do with the exception/interface object and1259 // exception/interface prototype object.1260 this.test_self();1261 }1262 // Then test things to do with its members (constants, fields, attributes,1263 // operations, . . .). These are run even if .untested is true, because1264 // members might themselves be marked as .untested. This might happen to1265 // interfaces if the interface itself is untested but a partial interface1266 // that extends it is tested -- then the interface itself and its initial1267 // members will be marked as untested, but the members added by the partial1268 // interface are still tested.1269 this.test_members();1270};1271IdlInterface.prototype.constructors = function()1272{1273 return this.members1274 .filter(function(m) { return m.type == "constructor"; });1275}1276IdlInterface.prototype.test_self = function()1277{1278 subsetTestByKey(this.name, test, function()1279 {1280 if (!this.should_have_interface_object()) {1281 return;1282 }1283 // The name of the property is the identifier of the interface, and its1284 // value is an object called the interface object.1285 // The property has the attributes { [[Writable]]: true,1286 // [[Enumerable]]: false, [[Configurable]]: true }."1287 // TODO: Should we test here that the property is actually writable1288 // etc., or trust getOwnPropertyDescriptor?1289 this.assert_interface_object_exists();1290 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object_owner(), this.name);1291 assert_false("get" in desc, "self's property " + format_value(this.name) + " should not have a getter");1292 assert_false("set" in desc, "self's property " + format_value(this.name) + " should not have a setter");1293 assert_true(desc.writable, "self's property " + format_value(this.name) + " should be writable");1294 assert_false(desc.enumerable, "self's property " + format_value(this.name) + " should not be enumerable");1295 assert_true(desc.configurable, "self's property " + format_value(this.name) + " should be configurable");1296 if (this.is_callback()) {1297 // "The internal [[Prototype]] property of an interface object for1298 // a callback interface must be the Function.prototype object."1299 assert_equals(Object.getPrototypeOf(this.get_interface_object()), Function.prototype,1300 "prototype of self's property " + format_value(this.name) + " is not Object.prototype");1301 return;1302 }1303 // "The interface object for a given non-callback interface is a1304 // function object."1305 // "If an object is defined to be a function object, then it has1306 // characteristics as follows:"1307 // Its [[Prototype]] internal property is otherwise specified (see1308 // below).1309 // "* Its [[Get]] internal property is set as described in ECMA-2621310 // section 9.1.8."1311 // Not much to test for this.1312 // "* Its [[Construct]] internal property is set as described in1313 // ECMA-262 section 19.2.2.3."1314 // "* Its @@hasInstance property is set as described in ECMA-2621315 // section 19.2.3.8, unless otherwise specified."1316 // TODO1317 // ES6 (rev 30) 19.1.3.6:1318 // "Else, if O has a [[Call]] internal method, then let builtinTag be1319 // "Function"."1320 assert_class_string(this.get_interface_object(), "Function", "class string of " + this.name);1321 // "The [[Prototype]] internal property of an interface object for a1322 // non-callback interface is determined as follows:"1323 var prototype = Object.getPrototypeOf(this.get_interface_object());1324 if (this.base) {1325 // "* If the interface inherits from some other interface, the1326 // value of [[Prototype]] is the interface object for that other1327 // interface."1328 var inherited_interface = this.array.members[this.base];1329 if (!inherited_interface.has_extended_attribute("LegacyNoInterfaceObject")) {1330 inherited_interface.assert_interface_object_exists();1331 assert_equals(prototype, inherited_interface.get_interface_object(),1332 'prototype of ' + this.name + ' is not ' +1333 this.base);1334 }1335 } else {1336 // "If the interface doesn't inherit from any other interface, the1337 // value of [[Prototype]] is %FunctionPrototype% ([ECMA-262],1338 // section 6.1.7.4)."1339 assert_equals(prototype, Function.prototype,1340 "prototype of self's property " + format_value(this.name) + " is not Function.prototype");1341 }1342 // Always test for [[Construct]]:1343 // https://github.com/heycam/webidl/issues/6981344 assert_true(isConstructor(this.get_interface_object()), "interface object must pass IsConstructor check");1345 if (!this.constructors().length) {1346 // "If I was not declared with a constructor operation, then throw a TypeError."1347 var interface_object = this.get_interface_object();1348 assert_throws_js(globalOf(interface_object).TypeError, function() {1349 interface_object();1350 }, "interface object didn't throw TypeError when called as a function");1351 assert_throws_js(globalOf(interface_object).TypeError, function() {1352 new interface_object();1353 }, "interface object didn't throw TypeError when called as a constructor");1354 }1355 }.bind(this), this.name + " interface: existence and properties of interface object");1356 if (this.should_have_interface_object() && !this.is_callback()) {1357 subsetTestByKey(this.name, test, function() {1358 // This function tests WebIDL as of 2014-10-25.1359 // https://heycam.github.io/webidl/#es-interface-call1360 this.assert_interface_object_exists();1361 // "Interface objects for non-callback interfaces MUST have a1362 // property named âlengthâ with attributes { [[Writable]]: false,1363 // [[Enumerable]]: false, [[Configurable]]: true } whose value is1364 // a Number."1365 assert_own_property(this.get_interface_object(), "length");1366 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "length");1367 assert_false("get" in desc, this.name + ".length should not have a getter");1368 assert_false("set" in desc, this.name + ".length should not have a setter");1369 assert_false(desc.writable, this.name + ".length should not be writable");1370 assert_false(desc.enumerable, this.name + ".length should not be enumerable");1371 assert_true(desc.configurable, this.name + ".length should be configurable");1372 var constructors = this.constructors();1373 var expected_length = minOverloadLength(constructors);1374 assert_equals(this.get_interface_object().length, expected_length, "wrong value for " + this.name + ".length");1375 }.bind(this), this.name + " interface object length");1376 }1377 if (this.should_have_interface_object()) {1378 subsetTestByKey(this.name, test, function() {1379 // This function tests WebIDL as of 2015-11-17.1380 // https://heycam.github.io/webidl/#interface-object1381 this.assert_interface_object_exists();1382 // "All interface objects must have a property named ânameâ with1383 // attributes { [[Writable]]: false, [[Enumerable]]: false,1384 // [[Configurable]]: true } whose value is the identifier of the1385 // corresponding interface."1386 assert_own_property(this.get_interface_object(), "name");1387 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "name");1388 assert_false("get" in desc, this.name + ".name should not have a getter");1389 assert_false("set" in desc, this.name + ".name should not have a setter");1390 assert_false(desc.writable, this.name + ".name should not be writable");1391 assert_false(desc.enumerable, this.name + ".name should not be enumerable");1392 assert_true(desc.configurable, this.name + ".name should be configurable");1393 assert_equals(this.get_interface_object().name, this.name, "wrong value for " + this.name + ".name");1394 }.bind(this), this.name + " interface object name");1395 }1396 if (this.has_extended_attribute("LegacyWindowAlias")) {1397 subsetTestByKey(this.name, test, function()1398 {1399 var aliasAttrs = this.extAttrs.filter(function(o) { return o.name === "LegacyWindowAlias"; });1400 if (aliasAttrs.length > 1) {1401 throw new IdlHarnessError("Invalid IDL: multiple LegacyWindowAlias extended attributes on " + this.name);1402 }1403 if (this.is_callback()) {1404 throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on non-interface " + this.name);1405 }1406 if (!this.exposureSet.has("Window")) {1407 throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " which is not exposed in Window");1408 }1409 // TODO: when testing of [LegacyNoInterfaceObject] interfaces is supported,1410 // check that it's not specified together with LegacyWindowAlias.1411 // TODO: maybe check that [LegacyWindowAlias] is not specified on a partial interface.1412 var rhs = aliasAttrs[0].rhs;1413 if (!rhs) {1414 throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " without identifier");1415 }1416 var aliases;1417 if (rhs.type === "identifier-list") {1418 aliases = rhs.value.map(id => id.value);1419 } else { // rhs.type === identifier1420 aliases = [ rhs.value ];1421 }1422 // OK now actually check the aliases...1423 var alias;1424 if (exposed_in(exposure_set(this, this.exposureSet)) && 'document' in self) {1425 for (alias of aliases) {1426 assert_true(alias in self, alias + " should exist");1427 assert_equals(self[alias], this.get_interface_object(), "self." + alias + " should be the same value as self." + this.get_qualified_name());1428 var desc = Object.getOwnPropertyDescriptor(self, alias);1429 assert_equals(desc.value, this.get_interface_object(), "wrong value in " + alias + " property descriptor");1430 assert_true(desc.writable, alias + " should be writable");1431 assert_false(desc.enumerable, alias + " should not be enumerable");1432 assert_true(desc.configurable, alias + " should be configurable");1433 assert_false('get' in desc, alias + " should not have a getter");1434 assert_false('set' in desc, alias + " should not have a setter");1435 }1436 } else {1437 for (alias of aliases) {1438 assert_false(alias in self, alias + " should not exist");1439 }1440 }1441 }.bind(this), this.name + " interface: legacy window alias");1442 }1443 if (this.has_extended_attribute("LegacyFactoryFunction")) {1444 var constructors = this.extAttrs1445 .filter(function(attr) { return attr.name == "LegacyFactoryFunction"; });1446 if (constructors.length !== 1) {1447 throw new IdlHarnessError("Internal error: missing support for multiple LegacyFactoryFunction extended attributes");1448 }1449 var constructor = constructors[0];1450 var min_length = minOverloadLength([constructor]);1451 subsetTestByKey(this.name, test, function()1452 {1453 // This function tests WebIDL as of 2019-01-14.1454 // "for every [LegacyFactoryFunction] extended attribute on an exposed1455 // interface, a corresponding property must exist on the ECMAScript1456 // global object. The name of the property is the1457 // [LegacyFactoryFunction]'s identifier, and its value is an object1458 // called a named constructor, ... . The property has the attributes1459 // { [[Writable]]: true, [[Enumerable]]: false,1460 // [[Configurable]]: true }."1461 var name = constructor.rhs.value;1462 assert_own_property(self, name);1463 var desc = Object.getOwnPropertyDescriptor(self, name);1464 assert_equals(desc.value, self[name], "wrong value in " + name + " property descriptor");1465 assert_true(desc.writable, name + " should be writable");1466 assert_false(desc.enumerable, name + " should not be enumerable");1467 assert_true(desc.configurable, name + " should be configurable");1468 assert_false("get" in desc, name + " should not have a getter");1469 assert_false("set" in desc, name + " should not have a setter");1470 }.bind(this), this.name + " interface: named constructor");1471 subsetTestByKey(this.name, test, function()1472 {1473 // This function tests WebIDL as of 2019-01-14.1474 // "2. Let F be ! CreateBuiltinFunction(realm, steps,1475 // realm.[[Intrinsics]].[[%FunctionPrototype%]])."1476 var name = constructor.rhs.value;1477 var value = self[name];1478 assert_equals(typeof value, "function", "type of value in " + name + " property descriptor");1479 assert_not_equals(value, this.get_interface_object(), "wrong value in " + name + " property descriptor");1480 assert_equals(Object.getPrototypeOf(value), Function.prototype, "wrong value for " + name + "'s prototype");1481 }.bind(this), this.name + " interface: named constructor object");1482 subsetTestByKey(this.name, test, function()1483 {1484 // This function tests WebIDL as of 2019-01-14.1485 // "7. Let proto be the interface prototype object of interface I1486 // in realm.1487 // "8. Perform ! DefinePropertyOrThrow(F, "prototype",1488 // PropertyDescriptor{1489 // [[Value]]: proto, [[Writable]]: false,1490 // [[Enumerable]]: false, [[Configurable]]: false1491 // })."1492 var name = constructor.rhs.value;1493 var expected = this.get_interface_object().prototype;1494 var desc = Object.getOwnPropertyDescriptor(self[name], "prototype");1495 assert_equals(desc.value, expected, "wrong value for " + name + ".prototype");1496 assert_false(desc.writable, "prototype should not be writable");1497 assert_false(desc.enumerable, "prototype should not be enumerable");1498 assert_false(desc.configurable, "prototype should not be configurable");1499 assert_false("get" in desc, "prototype should not have a getter");1500 assert_false("set" in desc, "prototype should not have a setter");1501 }.bind(this), this.name + " interface: named constructor prototype property");1502 subsetTestByKey(this.name, test, function()1503 {1504 // This function tests WebIDL as of 2019-01-14.1505 // "3. Perform ! SetFunctionName(F, id)."1506 var name = constructor.rhs.value;1507 var desc = Object.getOwnPropertyDescriptor(self[name], "name");1508 assert_equals(desc.value, name, "wrong value for " + name + ".name");1509 assert_false(desc.writable, "name should not be writable");1510 assert_false(desc.enumerable, "name should not be enumerable");1511 assert_true(desc.configurable, "name should be configurable");1512 assert_false("get" in desc, "name should not have a getter");1513 assert_false("set" in desc, "name should not have a setter");1514 }.bind(this), this.name + " interface: named constructor name");1515 subsetTestByKey(this.name, test, function()1516 {1517 // This function tests WebIDL as of 2019-01-14.1518 // "4. Initialize S to the effective overload set for constructors1519 // with identifier id on interface I and with argument count 0.1520 // "5. Let length be the length of the shortest argument list of1521 // the entries in S.1522 // "6. Perform ! SetFunctionLength(F, length)."1523 var name = constructor.rhs.value;1524 var desc = Object.getOwnPropertyDescriptor(self[name], "length");1525 assert_equals(desc.value, min_length, "wrong value for " + name + ".length");1526 assert_false(desc.writable, "length should not be writable");1527 assert_false(desc.enumerable, "length should not be enumerable");1528 assert_true(desc.configurable, "length should be configurable");1529 assert_false("get" in desc, "length should not have a getter");1530 assert_false("set" in desc, "length should not have a setter");1531 }.bind(this), this.name + " interface: named constructor length");1532 subsetTestByKey(this.name, test, function()1533 {1534 // This function tests WebIDL as of 2019-01-14.1535 // "1. Let steps be the following steps:1536 // " 1. If NewTarget is undefined, then throw a TypeError."1537 var name = constructor.rhs.value;1538 var args = constructor.arguments.map(function(arg) {1539 return create_suitable_object(arg.idlType);1540 });1541 assert_throws_js(globalOf(self[name]).TypeError, function() {1542 self[name](...args);1543 }.bind(this));1544 }.bind(this), this.name + " interface: named constructor without 'new'");1545 }1546 subsetTestByKey(this.name, test, function()1547 {1548 // This function tests WebIDL as of 2015-01-21.1549 // https://heycam.github.io/webidl/#interface-object1550 if (!this.should_have_interface_object()) {1551 return;1552 }1553 this.assert_interface_object_exists();1554 if (this.is_callback()) {1555 assert_false("prototype" in this.get_interface_object(),1556 this.name + ' should not have a "prototype" property');1557 return;1558 }1559 // "An interface object for a non-callback interface must have a1560 // property named âprototypeâ with attributes { [[Writable]]: false,1561 // [[Enumerable]]: false, [[Configurable]]: false } whose value is an1562 // object called the interface prototype object. This object has1563 // properties that correspond to the regular attributes and regular1564 // operations defined on the interface, and is described in more detail1565 // in section 4.5.4 below."1566 assert_own_property(this.get_interface_object(), "prototype",1567 'interface "' + this.name + '" does not have own property "prototype"');1568 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "prototype");1569 assert_false("get" in desc, this.name + ".prototype should not have a getter");1570 assert_false("set" in desc, this.name + ".prototype should not have a setter");1571 assert_false(desc.writable, this.name + ".prototype should not be writable");1572 assert_false(desc.enumerable, this.name + ".prototype should not be enumerable");1573 assert_false(desc.configurable, this.name + ".prototype should not be configurable");1574 // Next, test that the [[Prototype]] of the interface prototype object1575 // is correct. (This is made somewhat difficult by the existence of1576 // [LegacyNoInterfaceObject].)1577 // TODO: Aryeh thinks there's at least other place in this file where1578 // we try to figure out if an interface prototype object is1579 // correct. Consolidate that code.1580 // "The interface prototype object for a given interface A must have an1581 // internal [[Prototype]] property whose value is returned from the1582 // following steps:1583 // "If A is declared with the [Global] extended1584 // attribute, and A supports named properties, then return the named1585 // properties object for A, as defined in §3.6.4 Named properties1586 // object.1587 // "Otherwise, if A is declared to inherit from another interface, then1588 // return the interface prototype object for the inherited interface.1589 // "Otherwise, return %ObjectPrototype%.1590 //1591 // "In the ECMAScript binding, the DOMException type has some additional1592 // requirements:1593 //1594 // "Unlike normal interface types, the interface prototype object1595 // for DOMException must have as its [[Prototype]] the intrinsic1596 // object %ErrorPrototype%."1597 //1598 if (this.name === "Window") {1599 assert_class_string(Object.getPrototypeOf(this.get_interface_object().prototype),1600 'WindowProperties',1601 'Class name for prototype of Window' +1602 '.prototype is not "WindowProperties"');1603 } else {1604 var inherit_interface, inherit_interface_interface_object;1605 if (this.base) {1606 inherit_interface = this.base;1607 var parent = this.array.members[inherit_interface];1608 if (!parent.has_extended_attribute("LegacyNoInterfaceObject")) {1609 parent.assert_interface_object_exists();1610 inherit_interface_interface_object = parent.get_interface_object();1611 }1612 } else if (this.name === "DOMException") {1613 inherit_interface = 'Error';1614 inherit_interface_interface_object = self.Error;1615 } else {1616 inherit_interface = 'Object';1617 inherit_interface_interface_object = self.Object;1618 }1619 if (inherit_interface_interface_object) {1620 assert_not_equals(inherit_interface_interface_object, undefined,1621 'should inherit from ' + inherit_interface + ', but there is no such property');1622 assert_own_property(inherit_interface_interface_object, 'prototype',1623 'should inherit from ' + inherit_interface + ', but that object has no "prototype" property');1624 assert_equals(Object.getPrototypeOf(this.get_interface_object().prototype),1625 inherit_interface_interface_object.prototype,1626 'prototype of ' + this.name + '.prototype is not ' + inherit_interface + '.prototype');1627 } else {1628 // We can't test that we get the correct object, because this is the1629 // only way to get our hands on it. We only test that its class1630 // string, at least, is correct.1631 assert_class_string(Object.getPrototypeOf(this.get_interface_object().prototype),1632 inherit_interface + 'Prototype',1633 'Class name for prototype of ' + this.name +1634 '.prototype is not "' + inherit_interface + 'Prototype"');1635 }1636 }1637 // "The class string of an interface prototype object is the1638 // concatenation of the interfaceâs qualified identifier and the string1639 // âPrototypeâ."1640 // Skip these tests for now due to a specification issue about1641 // prototype name.1642 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=282441643 // assert_class_string(this.get_interface_object().prototype, this.get_qualified_name() + "Prototype",1644 // "class string of " + this.name + ".prototype");1645 // String() should end up calling {}.toString if nothing defines a1646 // stringifier.1647 if (!this.has_stringifier()) {1648 // assert_equals(String(this.get_interface_object().prototype), "[object " + this.get_qualified_name() + "Prototype]",1649 // "String(" + this.name + ".prototype)");1650 }1651 }.bind(this), this.name + " interface: existence and properties of interface prototype object");1652 // "If the interface is declared with the [Global]1653 // extended attribute, or the interface is in the set of inherited1654 // interfaces for any other interface that is declared with one of these1655 // attributes, then the interface prototype object must be an immutable1656 // prototype exotic object."1657 // https://heycam.github.io/webidl/#interface-prototype-object1658 if (this.is_global()) {1659 this.test_immutable_prototype("interface prototype object", this.get_interface_object().prototype);1660 }1661 subsetTestByKey(this.name, test, function()1662 {1663 if (!this.should_have_interface_object()) {1664 return;1665 }1666 this.assert_interface_object_exists();1667 if (this.is_callback()) {1668 assert_false("prototype" in this.get_interface_object(),1669 this.name + ' should not have a "prototype" property');1670 return;1671 }1672 assert_own_property(this.get_interface_object(), "prototype",1673 'interface "' + this.name + '" does not have own property "prototype"');1674 // "If the [LegacyNoInterfaceObject] extended attribute was not specified1675 // on the interface, then the interface prototype object must also have a1676 // property named âconstructorâ with attributes { [[Writable]]: true,1677 // [[Enumerable]]: false, [[Configurable]]: true } whose value is a1678 // reference to the interface object for the interface."1679 assert_own_property(this.get_interface_object().prototype, "constructor",1680 this.name + '.prototype does not have own property "constructor"');1681 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object().prototype, "constructor");1682 assert_false("get" in desc, this.name + ".prototype.constructor should not have a getter");1683 assert_false("set" in desc, this.name + ".prototype.constructor should not have a setter");1684 assert_true(desc.writable, this.name + ".prototype.constructor should be writable");1685 assert_false(desc.enumerable, this.name + ".prototype.constructor should not be enumerable");1686 assert_true(desc.configurable, this.name + ".prototype.constructor should be configurable");1687 assert_equals(this.get_interface_object().prototype.constructor, this.get_interface_object(),1688 this.name + '.prototype.constructor is not the same object as ' + this.name);1689 }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property');1690 subsetTestByKey(this.name, test, function()1691 {1692 if (!this.should_have_interface_object()) {1693 return;1694 }1695 this.assert_interface_object_exists();1696 if (this.is_callback()) {1697 assert_false("prototype" in this.get_interface_object(),1698 this.name + ' should not have a "prototype" property');1699 return;1700 }1701 assert_own_property(this.get_interface_object(), "prototype",1702 'interface "' + this.name + '" does not have own property "prototype"');1703 // If the interface has any member declared with the [Unscopable] extended1704 // attribute, then there must be a property on the interface prototype object1705 // whose name is the @@unscopables symbol, which has the attributes1706 // { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true },1707 // and whose value is an object created as follows...1708 var unscopables = this.get_unscopables().map(m => m.name);1709 var proto = this.get_interface_object().prototype;1710 if (unscopables.length != 0) {1711 assert_own_property(1712 proto, Symbol.unscopables,1713 this.name + '.prototype should have an @@unscopables property');1714 var desc = Object.getOwnPropertyDescriptor(proto, Symbol.unscopables);1715 assert_false("get" in desc,1716 this.name + ".prototype[Symbol.unscopables] should not have a getter");1717 assert_false("set" in desc, this.name + ".prototype[Symbol.unscopables] should not have a setter");1718 assert_false(desc.writable, this.name + ".prototype[Symbol.unscopables] should not be writable");1719 assert_false(desc.enumerable, this.name + ".prototype[Symbol.unscopables] should not be enumerable");1720 assert_true(desc.configurable, this.name + ".prototype[Symbol.unscopables] should be configurable");1721 assert_equals(desc.value, proto[Symbol.unscopables],1722 this.name + '.prototype[Symbol.unscopables] should be in the descriptor');1723 assert_equals(typeof desc.value, "object",1724 this.name + '.prototype[Symbol.unscopables] should be an object');1725 assert_equals(Object.getPrototypeOf(desc.value), null,1726 this.name + '.prototype[Symbol.unscopables] should have a null prototype');1727 assert_equals(Object.getOwnPropertySymbols(desc.value).length,1728 0,1729 this.name + '.prototype[Symbol.unscopables] should have the right number of symbol-named properties');1730 // Check that we do not have _extra_ unscopables. Checking that we1731 // have all the ones we should will happen in the per-member tests.1732 var observed = Object.getOwnPropertyNames(desc.value);1733 for (var prop of observed) {1734 assert_not_equals(unscopables.indexOf(prop),1735 -1,1736 this.name + '.prototype[Symbol.unscopables] has unexpected property "' + prop + '"');1737 }1738 } else {1739 assert_equals(Object.getOwnPropertyDescriptor(this.get_interface_object().prototype, Symbol.unscopables),1740 undefined,1741 this.name + '.prototype should not have @@unscopables');1742 }1743 }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s @@unscopables property');1744};1745IdlInterface.prototype.test_immutable_prototype = function(type, obj)1746{1747 if (typeof Object.setPrototypeOf !== "function") {1748 return;1749 }1750 subsetTestByKey(this.name, test, function(t) {1751 var originalValue = Object.getPrototypeOf(obj);1752 var newValue = Object.create(null);1753 t.add_cleanup(function() {1754 try {1755 Object.setPrototypeOf(obj, originalValue);1756 } catch (err) {}1757 });1758 assert_throws_js(TypeError, function() {1759 Object.setPrototypeOf(obj, newValue);1760 });1761 assert_equals(1762 Object.getPrototypeOf(obj),1763 originalValue,1764 "original value not modified"1765 );1766 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1767 "of " + type + " - setting to a new value via Object.setPrototypeOf " +1768 "should throw a TypeError");1769 subsetTestByKey(this.name, test, function(t) {1770 var originalValue = Object.getPrototypeOf(obj);1771 var newValue = Object.create(null);1772 t.add_cleanup(function() {1773 let setter = Object.getOwnPropertyDescriptor(1774 Object.prototype, '__proto__'1775 ).set;1776 try {1777 setter.call(obj, originalValue);1778 } catch (err) {}1779 });1780 // We need to find the actual setter for the '__proto__' property, so we1781 // can determine the right global for it. Walk up the prototype chain1782 // looking for that property until we find it.1783 let setter;1784 {1785 let cur = obj;1786 while (cur) {1787 const desc = Object.getOwnPropertyDescriptor(cur, "__proto__");1788 if (desc) {1789 setter = desc.set;1790 break;1791 }1792 cur = Object.getPrototypeOf(cur);1793 }1794 }1795 assert_throws_js(globalOf(setter).TypeError, function() {1796 obj.__proto__ = newValue;1797 });1798 assert_equals(1799 Object.getPrototypeOf(obj),1800 originalValue,1801 "original value not modified"1802 );1803 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1804 "of " + type + " - setting to a new value via __proto__ " +1805 "should throw a TypeError");1806 subsetTestByKey(this.name, test, function(t) {1807 var originalValue = Object.getPrototypeOf(obj);1808 var newValue = Object.create(null);1809 t.add_cleanup(function() {1810 try {1811 Reflect.setPrototypeOf(obj, originalValue);1812 } catch (err) {}1813 });1814 assert_false(Reflect.setPrototypeOf(obj, newValue));1815 assert_equals(1816 Object.getPrototypeOf(obj),1817 originalValue,1818 "original value not modified"1819 );1820 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1821 "of " + type + " - setting to a new value via Reflect.setPrototypeOf " +1822 "should return false");1823 subsetTestByKey(this.name, test, function() {1824 var originalValue = Object.getPrototypeOf(obj);1825 Object.setPrototypeOf(obj, originalValue);1826 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1827 "of " + type + " - setting to its original value via Object.setPrototypeOf " +1828 "should not throw");1829 subsetTestByKey(this.name, test, function() {1830 var originalValue = Object.getPrototypeOf(obj);1831 obj.__proto__ = originalValue;1832 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1833 "of " + type + " - setting to its original value via __proto__ " +1834 "should not throw");1835 subsetTestByKey(this.name, test, function() {1836 var originalValue = Object.getPrototypeOf(obj);1837 assert_true(Reflect.setPrototypeOf(obj, originalValue));1838 }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " +1839 "of " + type + " - setting to its original value via Reflect.setPrototypeOf " +1840 "should return true");1841};1842IdlInterface.prototype.test_member_const = function(member)1843{1844 if (!this.has_constants()) {1845 throw new IdlHarnessError("Internal error: test_member_const called without any constants");1846 }1847 subsetTestByKey(this.name, test, function()1848 {1849 this.assert_interface_object_exists();1850 // "For each constant defined on an interface A, there must be1851 // a corresponding property on the interface object, if it1852 // exists."1853 assert_own_property(this.get_interface_object(), member.name);1854 // "The value of the property is that which is obtained by1855 // converting the constantâs IDL value to an ECMAScript1856 // value."1857 assert_equals(this.get_interface_object()[member.name], constValue(member.value),1858 "property has wrong value");1859 // "The property has attributes { [[Writable]]: false,1860 // [[Enumerable]]: true, [[Configurable]]: false }."1861 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), member.name);1862 assert_false("get" in desc, "property should not have a getter");1863 assert_false("set" in desc, "property should not have a setter");1864 assert_false(desc.writable, "property should not be writable");1865 assert_true(desc.enumerable, "property should be enumerable");1866 assert_false(desc.configurable, "property should not be configurable");1867 }.bind(this), this.name + " interface: constant " + member.name + " on interface object");1868 // "In addition, a property with the same characteristics must1869 // exist on the interface prototype object."1870 subsetTestByKey(this.name, test, function()1871 {1872 this.assert_interface_object_exists();1873 if (this.is_callback()) {1874 assert_false("prototype" in this.get_interface_object(),1875 this.name + ' should not have a "prototype" property');1876 return;1877 }1878 assert_own_property(this.get_interface_object(), "prototype",1879 'interface "' + this.name + '" does not have own property "prototype"');1880 assert_own_property(this.get_interface_object().prototype, member.name);1881 assert_equals(this.get_interface_object().prototype[member.name], constValue(member.value),1882 "property has wrong value");1883 var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), member.name);1884 assert_false("get" in desc, "property should not have a getter");1885 assert_false("set" in desc, "property should not have a setter");1886 assert_false(desc.writable, "property should not be writable");1887 assert_true(desc.enumerable, "property should be enumerable");1888 assert_false(desc.configurable, "property should not be configurable");1889 }.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object");1890};1891IdlInterface.prototype.test_member_attribute = function(member)1892 {1893 if (!shouldRunSubTest(this.name)) {1894 return;1895 }1896 var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: attribute " + member.name);1897 a_test.step(function()1898 {1899 if (!this.should_have_interface_object()) {1900 a_test.done();1901 return;1902 }1903 this.assert_interface_object_exists();1904 assert_own_property(this.get_interface_object(), "prototype",1905 'interface "' + this.name + '" does not have own property "prototype"');1906 if (member.special === "static") {1907 assert_own_property(this.get_interface_object(), member.name,1908 "The interface object must have a property " +1909 format_value(member.name));1910 a_test.done();1911 return;1912 }1913 this.do_member_unscopable_asserts(member);1914 if (this.is_global()) {1915 assert_own_property(self, member.name,1916 "The global object must have a property " +1917 format_value(member.name));1918 assert_false(member.name in this.get_interface_object().prototype,1919 "The prototype object should not have a property " +1920 format_value(member.name));1921 var getter = Object.getOwnPropertyDescriptor(self, member.name).get;1922 assert_equals(typeof(getter), "function",1923 format_value(member.name) + " must have a getter");1924 // Try/catch around the get here, since it can legitimately throw.1925 // If it does, we obviously can't check for equality with direct1926 // invocation of the getter.1927 var gotValue;1928 var propVal;1929 try {1930 propVal = self[member.name];1931 gotValue = true;1932 } catch (e) {1933 gotValue = false;1934 }1935 if (gotValue) {1936 assert_equals(propVal, getter.call(undefined),1937 "Gets on a global should not require an explicit this");1938 }1939 // do_interface_attribute_asserts must be the last thing we do,1940 // since it will call done() on a_test.1941 this.do_interface_attribute_asserts(self, member, a_test);1942 } else {1943 assert_true(member.name in this.get_interface_object().prototype,1944 "The prototype object must have a property " +1945 format_value(member.name));1946 if (!member.has_extended_attribute("LegacyLenientThis")) {1947 if (member.idlType.generic !== "Promise") {1948 // this.get_interface_object() returns a thing in our global1949 assert_throws_js(TypeError, function() {1950 this.get_interface_object().prototype[member.name];1951 }.bind(this), "getting property on prototype object must throw TypeError");1952 // do_interface_attribute_asserts must be the last thing we1953 // do, since it will call done() on a_test.1954 this.do_interface_attribute_asserts(this.get_interface_object().prototype, member, a_test);1955 } else {1956 promise_rejects_js(a_test, TypeError,1957 this.get_interface_object().prototype[member.name])1958 .then(a_test.step_func(function() {1959 // do_interface_attribute_asserts must be the last1960 // thing we do, since it will call done() on a_test.1961 this.do_interface_attribute_asserts(this.get_interface_object().prototype,1962 member, a_test);1963 }.bind(this)));1964 }1965 } else {1966 assert_equals(this.get_interface_object().prototype[member.name], undefined,1967 "getting property on prototype object must return undefined");1968 // do_interface_attribute_asserts must be the last thing we do,1969 // since it will call done() on a_test.1970 this.do_interface_attribute_asserts(this.get_interface_object().prototype, member, a_test);1971 }1972 }1973 }.bind(this));1974};1975IdlInterface.prototype.test_member_operation = function(member)1976{1977 if (!shouldRunSubTest(this.name)) {1978 return;1979 }1980 var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: operation " + member);1981 a_test.step(function()1982 {1983 // This function tests WebIDL as of 2015-12-29.1984 // https://heycam.github.io/webidl/#es-operations1985 if (!this.should_have_interface_object()) {1986 a_test.done();1987 return;1988 }1989 this.assert_interface_object_exists();1990 if (this.is_callback()) {1991 assert_false("prototype" in this.get_interface_object(),1992 this.name + ' should not have a "prototype" property');1993 a_test.done();1994 return;1995 }1996 assert_own_property(this.get_interface_object(), "prototype",1997 'interface "' + this.name + '" does not have own property "prototype"');1998 // "For each unique identifier of an exposed operation defined on the1999 // interface, there must exist a corresponding property, unless the2000 // effective overload set for that identifier and operation and with an2001 // argument count of 0 has no entries."2002 // TODO: Consider [Exposed].2003 // "The location of the property is determined as follows:"2004 var memberHolderObject;2005 // "* If the operation is static, then the property exists on the2006 // interface object."2007 if (member.special === "static") {2008 assert_own_property(this.get_interface_object(), member.name,2009 "interface object missing static operation");2010 memberHolderObject = this.get_interface_object();2011 // "* Otherwise, [...] if the interface was declared with the [Global]2012 // extended attribute, then the property exists2013 // on every object that implements the interface."2014 } else if (this.is_global()) {2015 assert_own_property(self, member.name,2016 "global object missing non-static operation");2017 memberHolderObject = self;2018 // "* Otherwise, the property exists solely on the interfaceâs2019 // interface prototype object."2020 } else {2021 assert_own_property(this.get_interface_object().prototype, member.name,2022 "interface prototype object missing non-static operation");2023 memberHolderObject = this.get_interface_object().prototype;2024 }2025 this.do_member_unscopable_asserts(member);2026 this.do_member_operation_asserts(memberHolderObject, member, a_test);2027 }.bind(this));2028};2029IdlInterface.prototype.do_member_unscopable_asserts = function(member)2030{2031 // Check that if the member is unscopable then it's in the2032 // @@unscopables object properly.2033 if (!member.isUnscopable) {2034 return;2035 }2036 var unscopables = this.get_interface_object().prototype[Symbol.unscopables];2037 var prop = member.name;2038 var propDesc = Object.getOwnPropertyDescriptor(unscopables, prop);2039 assert_equals(typeof propDesc, "object",2040 this.name + '.prototype[Symbol.unscopables].' + prop + ' must exist')2041 assert_false("get" in propDesc,2042 this.name + '.prototype[Symbol.unscopables].' + prop + ' must have no getter');2043 assert_false("set" in propDesc,2044 this.name + '.prototype[Symbol.unscopables].' + prop + ' must have no setter');2045 assert_true(propDesc.writable,2046 this.name + '.prototype[Symbol.unscopables].' + prop + ' must be writable');2047 assert_true(propDesc.enumerable,2048 this.name + '.prototype[Symbol.unscopables].' + prop + ' must be enumerable');2049 assert_true(propDesc.configurable,2050 this.name + '.prototype[Symbol.unscopables].' + prop + ' must be configurable');2051 assert_equals(propDesc.value, true,2052 this.name + '.prototype[Symbol.unscopables].' + prop + ' must have the value `true`');2053};2054IdlInterface.prototype.do_member_operation_asserts = function(memberHolderObject, member, a_test)2055{2056 var done = a_test.done.bind(a_test);2057 var operationUnforgeable = member.isUnforgeable;2058 var desc = Object.getOwnPropertyDescriptor(memberHolderObject, member.name);2059 // "The property has attributes { [[Writable]]: B,2060 // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the2061 // operation is unforgeable on the interface, and true otherwise".2062 assert_false("get" in desc, "property should not have a getter");2063 assert_false("set" in desc, "property should not have a setter");2064 assert_equals(desc.writable, !operationUnforgeable,2065 "property should be writable if and only if not unforgeable");2066 assert_true(desc.enumerable, "property should be enumerable");2067 assert_equals(desc.configurable, !operationUnforgeable,2068 "property should be configurable if and only if not unforgeable");2069 // "The value of the property is a Function object whose2070 // behavior is as follows . . ."2071 assert_equals(typeof memberHolderObject[member.name], "function",2072 "property must be a function");2073 const ctors = this.members.filter(function(m) {2074 return m.type == "operation" && m.name == member.name;2075 });2076 assert_equals(2077 memberHolderObject[member.name].length,2078 minOverloadLength(ctors),2079 "property has wrong .length");2080 assert_equals(2081 memberHolderObject[member.name].name,2082 member.name,2083 "property has wrong .name");2084 // Make some suitable arguments2085 var args = member.arguments.map(function(arg) {2086 return create_suitable_object(arg.idlType);2087 });2088 // "Let O be a value determined as follows:2089 // ". . .2090 // "Otherwise, throw a TypeError."2091 // This should be hit if the operation is not static, there is2092 // no [ImplicitThis] attribute, and the this value is null.2093 //2094 // TODO: We currently ignore the [ImplicitThis] case. Except we manually2095 // check for globals, since otherwise we'll invoke window.close(). And we2096 // have to skip this test for anything that on the proto chain of "self",2097 // since that does in fact have implicit-this behavior.2098 if (member.special !== "static") {2099 var cb;2100 if (!this.is_global() &&2101 memberHolderObject[member.name] != self[member.name])2102 {2103 cb = awaitNCallbacks(2, done);2104 throwOrReject(a_test, member, memberHolderObject[member.name], null, args,2105 "calling operation with this = null didn't throw TypeError", cb);2106 } else {2107 cb = awaitNCallbacks(1, done);2108 }2109 // ". . . If O is not null and is also not a platform object2110 // that implements interface I, throw a TypeError."2111 //2112 // TODO: Test a platform object that implements some other2113 // interface. (Have to be sure to get inheritance right.)2114 throwOrReject(a_test, member, memberHolderObject[member.name], {}, args,2115 "calling operation with this = {} didn't throw TypeError", cb);2116 } else {2117 done();2118 }2119}2120IdlInterface.prototype.test_to_json_operation = function(desc, memberHolderObject, member) {2121 var instanceName = memberHolderObject && memberHolderObject.constructor.name2122 || member.name + " object";2123 if (member.has_extended_attribute("Default")) {2124 subsetTestByKey(this.name, test, function() {2125 var map = this.default_to_json_operation();2126 var json = memberHolderObject.toJSON();2127 map.forEach(function(type, k) {2128 assert_true(k in json, "property " + JSON.stringify(k) + " should be present in the output of " + this.name + ".prototype.toJSON()");2129 var descriptor = Object.getOwnPropertyDescriptor(json, k);2130 assert_true(descriptor.writable, "property " + k + " should be writable");2131 assert_true(descriptor.configurable, "property " + k + " should be configurable");2132 assert_true(descriptor.enumerable, "property " + k + " should be enumerable");2133 this.array.assert_type_is(json[k], type);2134 delete json[k];2135 }, this);2136 }.bind(this), this.name + " interface: default toJSON operation on " + desc);2137 } else {2138 subsetTestByKey(this.name, test, function() {2139 assert_true(this.array.is_json_type(member.idlType), JSON.stringify(member.idlType) + " is not an appropriate return value for the toJSON operation of " + instanceName);2140 this.array.assert_type_is(memberHolderObject.toJSON(), member.idlType);2141 }.bind(this), this.name + " interface: toJSON operation on " + desc);2142 }2143};2144IdlInterface.prototype.test_member_iterable = function(member)2145{2146 subsetTestByKey(this.name, test, function()2147 {2148 var isPairIterator = member.idlType.length === 2;2149 var proto = this.get_interface_object().prototype;2150 var iteratorDesc = Object.getOwnPropertyDescriptor(proto, Symbol.iterator);2151 assert_true(iteratorDesc.writable, "@@iterator property should be writable");2152 assert_true(iteratorDesc.configurable, "@@iterator property should be configurable");2153 assert_false(iteratorDesc.enumerable, "@@iterator property should not be enumerable");2154 assert_equals(typeof iteratorDesc.value, "function", "@@iterator property should be a function");2155 assert_equals(iteratorDesc.value.length, 0, "@@iterator function object length should be 0");2156 assert_equals(iteratorDesc.value.name, isPairIterator ? "entries" : "values", "@@iterator function object should have the right name");2157 if (isPairIterator) {2158 assert_equals(proto["entries"], proto[Symbol.iterator], "entries method should be the same as @@iterator method");2159 [2160 ["entries", 0],2161 ["keys", 0],2162 ["values", 0],2163 ["forEach", 1]2164 ].forEach(([property, length]) => {2165 var desc = Object.getOwnPropertyDescriptor(proto, property);2166 assert_equals(typeof desc.value, "function", property + " property should be a function");2167 assert_equals(desc.value.length, length, property + " function object length should be " + length);2168 assert_equals(desc.value.name, property, property + " function object should have the right name");2169 });2170 } else {2171 assert_equals(proto[Symbol.iterator], Array.prototype[Symbol.iterator], "@@iterator method should be the same as Array prototype's");2172 ["entries", "keys", "values", "forEach", Symbol.iterator].forEach(property => {2173 var propertyName = property === Symbol.iterator ? "@@iterator" : property;2174 assert_equals(proto[property], Array.prototype[property], propertyName + " method should be the same as Array prototype's");2175 });2176 }2177 }.bind(this), this.name + " interface: iterable<" + member.idlType.map(function(t) { return t.idlType; }).join(", ") + ">");2178};2179IdlInterface.prototype.test_member_async_iterable = function(member)2180{2181 subsetTestByKey(this.name, test, function()2182 {2183 var isPairIterator = member.idlType.length === 2;2184 var proto = this.get_interface_object().prototype;2185 var iteratorDesc = Object.getOwnPropertyDescriptor(proto, Symbol.asyncIterator);2186 assert_true(iteratorDesc.writable, "@@asyncIterator property should be writable");2187 assert_true(iteratorDesc.configurable, "@@asyncIterator property should be configurable");2188 assert_false(iteratorDesc.enumerable, "@@asyncIterator property should not be enumerable");2189 assert_equals(typeof iteratorDesc.value, "function", "@@asyncIterator property should be a function");2190 assert_equals(iteratorDesc.value.length, 0, "@@asyncIterator function object length should be 0");2191 assert_equals(iteratorDesc.value.name, isPairIterator ? "entries" : "values", "@@asyncIterator function object should have the right name");2192 if (isPairIterator) {2193 assert_equals(proto["entries"], proto[Symbol.asyncIterator], "entries method should be the same as @@asyncIterator method");2194 ["entries", "keys", "values"].forEach(property => {2195 var desc = Object.getOwnPropertyDescriptor(proto, property);2196 assert_equals(typeof desc.value, "function", property + " property should be a function");2197 assert_equals(desc.value.length, 0, property + " function object length should be 0");2198 assert_equals(desc.value.name, property, property + " function object should have the right name");2199 });2200 } else {2201 assert_equals(proto["values"], proto[Symbol.asyncIterator], "values method should be the same as @@asyncIterator method");2202 assert_false("entries" in proto, "should not have an entries method");2203 assert_false("keys" in proto, "should not have a keys method");2204 }2205 }.bind(this), this.name + " interface: async iterable<" + member.idlType.map(function(t) { return t.idlType; }).join(", ") + ">");2206};2207IdlInterface.prototype.test_member_stringifier = function(member)2208{2209 subsetTestByKey(this.name, test, function()2210 {2211 if (!this.should_have_interface_object()) {2212 return;2213 }2214 this.assert_interface_object_exists();2215 if (this.is_callback()) {2216 assert_false("prototype" in this.get_interface_object(),2217 this.name + ' should not have a "prototype" property');2218 return;2219 }2220 assert_own_property(this.get_interface_object(), "prototype",2221 'interface "' + this.name + '" does not have own property "prototype"');2222 // ". . . the property exists on the interface prototype object."2223 var interfacePrototypeObject = this.get_interface_object().prototype;2224 assert_own_property(interfacePrototypeObject, "toString",2225 "interface prototype object missing non-static operation");2226 var stringifierUnforgeable = member.isUnforgeable;2227 var desc = Object.getOwnPropertyDescriptor(interfacePrototypeObject, "toString");2228 // "The property has attributes { [[Writable]]: B,2229 // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the2230 // stringifier is unforgeable on the interface, and true otherwise."2231 assert_false("get" in desc, "property should not have a getter");2232 assert_false("set" in desc, "property should not have a setter");2233 assert_equals(desc.writable, !stringifierUnforgeable,2234 "property should be writable if and only if not unforgeable");2235 assert_true(desc.enumerable, "property should be enumerable");2236 assert_equals(desc.configurable, !stringifierUnforgeable,2237 "property should be configurable if and only if not unforgeable");2238 // "The value of the property is a Function object, which behaves as2239 // follows . . ."2240 assert_equals(typeof interfacePrototypeObject.toString, "function",2241 "property must be a function");2242 // "The value of the Function objectâs âlengthâ property is the Number2243 // value 0."2244 assert_equals(interfacePrototypeObject.toString.length, 0,2245 "property has wrong .length");2246 // "Let O be the result of calling ToObject on the this value."2247 assert_throws_js(globalOf(interfacePrototypeObject.toString).TypeError, function() {2248 interfacePrototypeObject.toString.apply(null, []);2249 }, "calling stringifier with this = null didn't throw TypeError");2250 // "If O is not an object that implements the interface on which the2251 // stringifier was declared, then throw a TypeError."2252 //2253 // TODO: Test a platform object that implements some other2254 // interface. (Have to be sure to get inheritance right.)2255 assert_throws_js(globalOf(interfacePrototypeObject.toString).TypeError, function() {2256 interfacePrototypeObject.toString.apply({}, []);2257 }, "calling stringifier with this = {} didn't throw TypeError");2258 }.bind(this), this.name + " interface: stringifier");2259};2260IdlInterface.prototype.test_members = function()2261{2262 for (var i = 0; i < this.members.length; i++)2263 {2264 var member = this.members[i];2265 if (member.untested) {2266 continue;2267 }2268 if (!exposed_in(exposure_set(member, this.exposureSet))) {2269 subsetTestByKey(this.name, test, function() {2270 // It's not exposed, so we shouldn't find it anywhere.2271 assert_false(member.name in this.get_interface_object(),2272 "The interface object must not have a property " +2273 format_value(member.name));2274 assert_false(member.name in this.get_interface_object().prototype,2275 "The prototype object must not have a property " +2276 format_value(member.name));2277 }.bind(this), this.name + " interface: member " + member.name);2278 continue;2279 }2280 switch (member.type) {2281 case "const":2282 this.test_member_const(member);2283 break;2284 case "attribute":2285 // For unforgeable attributes, we do the checks in2286 // test_interface_of instead.2287 if (!member.isUnforgeable)2288 {2289 this.test_member_attribute(member);2290 }2291 if (member.special === "stringifier") {2292 this.test_member_stringifier(member);2293 }2294 break;2295 case "operation":2296 // TODO: Need to correctly handle multiple operations with the same2297 // identifier.2298 // For unforgeable operations, we do the checks in2299 // test_interface_of instead.2300 if (member.name) {2301 if (!member.isUnforgeable)2302 {2303 this.test_member_operation(member);2304 }2305 } else if (member.special === "stringifier") {2306 this.test_member_stringifier(member);2307 }2308 break;2309 case "iterable":2310 if (member.async) {2311 this.test_member_async_iterable(member);2312 } else {2313 this.test_member_iterable(member);2314 }2315 break;2316 default:2317 // TODO: check more member types.2318 break;2319 }2320 }2321};2322IdlInterface.prototype.test_object = function(desc)2323{2324 var obj, exception = null;2325 try2326 {2327 obj = eval(desc);2328 }2329 catch(e)2330 {2331 exception = e;2332 }2333 var expected_typeof;2334 if (this.name == "HTMLAllCollection")2335 {2336 // Result of [[IsHTMLDDA]] slot2337 expected_typeof = "undefined";2338 }2339 else2340 {2341 expected_typeof = "object";2342 }2343 this.test_primary_interface_of(desc, obj, exception, expected_typeof);2344 var current_interface = this;2345 while (current_interface)2346 {2347 if (!(current_interface.name in this.array.members))2348 {2349 throw new IdlHarnessError("Interface " + current_interface.name + " not found (inherited by " + this.name + ")");2350 }2351 if (current_interface.prevent_multiple_testing && current_interface.already_tested)2352 {2353 return;2354 }2355 current_interface.test_interface_of(desc, obj, exception, expected_typeof);2356 current_interface = this.array.members[current_interface.base];2357 }2358};2359IdlInterface.prototype.test_primary_interface_of = function(desc, obj, exception, expected_typeof)2360{2361 // Only the object itself, not its members, are tested here, so if the2362 // interface is untested, there is nothing to do.2363 if (this.untested)2364 {2365 return;2366 }2367 // "The internal [[SetPrototypeOf]] method of every platform object that2368 // implements an interface with the [Global] extended2369 // attribute must execute the same algorithm as is defined for the2370 // [[SetPrototypeOf]] internal method of an immutable prototype exotic2371 // object."2372 // https://heycam.github.io/webidl/#platform-object-setprototypeof2373 if (this.is_global())2374 {2375 this.test_immutable_prototype("global platform object", obj);2376 }2377 // We can't easily test that its prototype is correct if there's no2378 // interface object, or the object is from a different global environment2379 // (not instanceof Object). TODO: test in this case that its prototype at2380 // least looks correct, even if we can't test that it's actually correct.2381 if (this.should_have_interface_object()2382 && (typeof obj != expected_typeof || obj instanceof Object))2383 {2384 subsetTestByKey(this.name, test, function()2385 {2386 assert_equals(exception, null, "Unexpected exception when evaluating object");2387 assert_equals(typeof obj, expected_typeof, "wrong typeof object");2388 this.assert_interface_object_exists();2389 assert_own_property(this.get_interface_object(), "prototype",2390 'interface "' + this.name + '" does not have own property "prototype"');2391 // "The value of the internal [[Prototype]] property of the2392 // platform object is the interface prototype object of the primary2393 // interface from the platform objectâs associated global2394 // environment."2395 assert_equals(Object.getPrototypeOf(obj),2396 this.get_interface_object().prototype,2397 desc + "'s prototype is not " + this.name + ".prototype");2398 }.bind(this), this.name + " must be primary interface of " + desc);2399 }2400 // "The class string of a platform object that implements one or more2401 // interfaces must be the qualified name of the primary interface of the2402 // platform object."2403 subsetTestByKey(this.name, test, function()2404 {2405 assert_equals(exception, null, "Unexpected exception when evaluating object");2406 assert_equals(typeof obj, expected_typeof, "wrong typeof object");2407 assert_class_string(obj, this.get_qualified_name(), "class string of " + desc);2408 if (!this.has_stringifier())2409 {2410 assert_equals(String(obj), "[object " + this.get_qualified_name() + "]", "String(" + desc + ")");2411 }2412 }.bind(this), "Stringification of " + desc);2413};2414IdlInterface.prototype.test_interface_of = function(desc, obj, exception, expected_typeof)2415{2416 // TODO: Indexed and named properties, more checks on interface members2417 this.already_tested = true;2418 if (!shouldRunSubTest(this.name)) {2419 return;2420 }2421 for (var i = 0; i < this.members.length; i++)2422 {2423 var member = this.members[i];2424 if (member.untested) {2425 continue;2426 }2427 if (!exposed_in(exposure_set(member, this.exposureSet))) {2428 subsetTestByKey(this.name, test, function() {2429 assert_equals(exception, null, "Unexpected exception when evaluating object");2430 assert_false(member.name in obj);2431 }.bind(this), this.name + " interface: " + desc + ' must not have property "' + member.name + '"');2432 continue;2433 }2434 if (member.type == "attribute" && member.isUnforgeable)2435 {2436 var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: " + desc + ' must have own property "' + member.name + '"');2437 a_test.step(function() {2438 assert_equals(exception, null, "Unexpected exception when evaluating object");2439 assert_equals(typeof obj, expected_typeof, "wrong typeof object");2440 // Call do_interface_attribute_asserts last, since it will call a_test.done()2441 this.do_interface_attribute_asserts(obj, member, a_test);2442 }.bind(this));2443 }2444 else if (member.type == "operation" &&2445 member.name &&2446 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 {2451 assert_equals(exception, null, "Unexpected exception when evaluating object");2452 assert_equals(typeof obj, expected_typeof, "wrong typeof object");2453 assert_own_property(obj, member.name,2454 "Doesn't have the unforgeable operation property");2455 this.do_member_operation_asserts(obj, member, a_test);2456 }.bind(this));2457 }2458 else if ((member.type == "const"2459 || member.type == "attribute"2460 || member.type == "operation")2461 && member.name)2462 {2463 subsetTestByKey(this.name, test, function()2464 {2465 assert_equals(exception, null, "Unexpected exception when evaluating object");2466 assert_equals(typeof obj, expected_typeof, "wrong typeof object");2467 if (member.special !== "static") {2468 if (!this.is_global()) {2469 assert_inherits(obj, member.name);2470 } else {2471 assert_own_property(obj, member.name);2472 }2473 if (member.type == "const")2474 {2475 assert_equals(obj[member.name], constValue(member.value));2476 }2477 if (member.type == "attribute")2478 {2479 // Attributes are accessor properties, so they might2480 // legitimately throw an exception rather than returning2481 // anything.2482 var property, thrown = false;2483 try2484 {2485 property = obj[member.name];2486 }2487 catch (e)2488 {2489 thrown = true;2490 }2491 if (!thrown)2492 {2493 if (this.name == "Document" && member.name == "all")2494 {2495 // Result of [[IsHTMLDDA]] slot2496 assert_equals(typeof property, "undefined");2497 }2498 else2499 {2500 this.array.assert_type_is(property, member.idlType);2501 }2502 }2503 }2504 if (member.type == "operation")2505 {2506 assert_equals(typeof obj[member.name], "function");2507 }2508 }2509 }.bind(this), this.name + " interface: " + desc + ' must inherit property "' + member + '" with the proper type');2510 }2511 // TODO: This is wrong if there are multiple operations with the same2512 // identifier.2513 // TODO: Test passing arguments of the wrong type.2514 if (member.type == "operation" && member.name && member.arguments.length)2515 {2516 var description =2517 this.name + " interface: calling " + member + " on " + desc +2518 " with too few arguments must throw TypeError";2519 var a_test = subsetTestByKey(this.name, async_test, description);2520 a_test.step(function()2521 {2522 assert_equals(exception, null, "Unexpected exception when evaluating object");2523 assert_equals(typeof obj, expected_typeof, "wrong typeof object");2524 var fn;2525 if (member.special !== "static") {2526 if (!this.is_global() && !member.isUnforgeable) {2527 assert_inherits(obj, member.name);2528 } else {2529 assert_own_property(obj, member.name);2530 }2531 fn = obj[member.name];2532 }2533 else2534 {2535 assert_own_property(obj.constructor, member.name, "interface object must have static operation as own property");2536 fn = obj.constructor[member.name];2537 }2538 var minLength = minOverloadLength(this.members.filter(function(m) {2539 return m.type == "operation" && m.name == member.name;2540 }));2541 var args = [];2542 var cb = awaitNCallbacks(minLength, a_test.done.bind(a_test));2543 for (var i = 0; i < minLength; i++) {2544 throwOrReject(a_test, member, fn, obj, args, "Called with " + i + " arguments", cb);2545 args.push(create_suitable_object(member.arguments[i].idlType));2546 }2547 if (minLength === 0) {2548 cb();2549 }2550 }.bind(this));2551 }2552 if (member.is_to_json_regular_operation()) {2553 this.test_to_json_operation(desc, obj, member);2554 }2555 }2556};2557IdlInterface.prototype.has_stringifier = function()2558{2559 if (this.name === "DOMException") {2560 // toString is inherited from Error, so don't assume we have the2561 // default stringifer2562 return true;2563 }2564 if (this.members.some(function(member) { return member.special === "stringifier"; })) {2565 return true;2566 }2567 if (this.base &&2568 this.array.members[this.base].has_stringifier()) {2569 return true;2570 }2571 return false;2572};2573IdlInterface.prototype.do_interface_attribute_asserts = function(obj, member, a_test)2574{2575 // This function tests WebIDL as of 2015-01-27.2576 // TODO: Consider [Exposed].2577 // This is called by test_member_attribute() with the prototype as obj if2578 // it is not a global, and the global otherwise, and by test_interface_of()2579 // with the object as obj.2580 var pendingPromises = [];2581 // "The name of the property is the identifier of the attribute."2582 assert_own_property(obj, member.name);2583 // "The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:2584 // true, [[Configurable]]: configurable }, where:2585 // "configurable is false if the attribute was declared with the2586 // [LegacyUnforgeable] extended attribute and true otherwise;2587 // "G is the attribute getter, defined below; and2588 // "S is the attribute setter, also defined below."2589 var desc = Object.getOwnPropertyDescriptor(obj, member.name);2590 assert_false("value" in desc, 'property descriptor should not have a "value" field');2591 assert_false("writable" in desc, 'property descriptor should not have a "writable" field');2592 assert_true(desc.enumerable, "property should be enumerable");2593 if (member.isUnforgeable)2594 {2595 assert_false(desc.configurable, "[LegacyUnforgeable] property must not be configurable");2596 }2597 else2598 {2599 assert_true(desc.configurable, "property must be configurable");2600 }2601 // "The attribute getter is a Function object whose behavior when invoked2602 // is as follows:"2603 assert_equals(typeof desc.get, "function", "getter must be Function");2604 // "If the attribute is a regular attribute, then:"2605 if (member.special !== "static") {2606 // "If O is not a platform object that implements I, then:2607 // "If the attribute was specified with the [LegacyLenientThis] extended2608 // attribute, then return undefined.2609 // "Otherwise, throw a TypeError."2610 if (!member.has_extended_attribute("LegacyLenientThis")) {2611 if (member.idlType.generic !== "Promise") {2612 assert_throws_js(globalOf(desc.get).TypeError, function() {2613 desc.get.call({});2614 }.bind(this), "calling getter on wrong object type must throw TypeError");2615 } else {2616 pendingPromises.push(2617 promise_rejects_js(a_test, TypeError, desc.get.call({}),2618 "calling getter on wrong object type must reject the return promise with TypeError"));2619 }2620 } else {2621 assert_equals(desc.get.call({}), undefined,2622 "calling getter on wrong object type must return undefined");2623 }2624 }2625 // "The value of the Function objectâs âlengthâ property is the Number2626 // value 0."2627 assert_equals(desc.get.length, 0, "getter length must be 0");2628 // "Let name be the string "get " prepended to attributeâs identifier."2629 // "Perform ! SetFunctionName(F, name)."2630 assert_equals(desc.get.name, "get " + member.name,2631 "getter must have the name 'get " + member.name + "'");2632 // TODO: Test calling setter on the interface prototype (should throw2633 // TypeError in most cases).2634 if (member.readonly2635 && !member.has_extended_attribute("LegacyLenientSetter")2636 && !member.has_extended_attribute("PutForwards")2637 && !member.has_extended_attribute("Replaceable"))2638 {2639 // "The attribute setter is undefined if the attribute is declared2640 // readonly and has neither a [PutForwards] nor a [Replaceable]2641 // extended attribute declared on it."2642 assert_equals(desc.set, undefined, "setter must be undefined for readonly attributes");2643 }2644 else2645 {2646 // "Otherwise, it is a Function object whose behavior when2647 // invoked is as follows:"2648 assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes");2649 // "If the attribute is a regular attribute, then:"2650 if (member.special !== "static") {2651 // "If /validThis/ is false and the attribute was not specified2652 // with the [LegacyLenientThis] extended attribute, then throw a2653 // TypeError."2654 // "If the attribute is declared with a [Replaceable] extended2655 // attribute, then: ..."2656 // "If validThis is false, then return."2657 if (!member.has_extended_attribute("LegacyLenientThis")) {2658 assert_throws_js(globalOf(desc.set).TypeError, function() {2659 desc.set.call({});2660 }.bind(this), "calling setter on wrong object type must throw TypeError");2661 } else {2662 assert_equals(desc.set.call({}), undefined,2663 "calling setter on wrong object type must return undefined");2664 }2665 }2666 // "The value of the Function objectâs âlengthâ property is the Number2667 // value 1."2668 assert_equals(desc.set.length, 1, "setter length must be 1");2669 // "Let name be the string "set " prepended to id."2670 // "Perform ! SetFunctionName(F, name)."2671 assert_equals(desc.set.name, "set " + member.name,2672 "The attribute setter must have the name 'set " + member.name + "'");2673 }2674 Promise.all(pendingPromises).then(a_test.done.bind(a_test));2675}2676/// IdlInterfaceMember ///2677function IdlInterfaceMember(obj)2678{2679 /**2680 * obj is an object produced by the WebIDLParser.js "ifMember" production.2681 * We just forward all properties to this object without modification,2682 * except for special extAttrs handling.2683 */2684 for (var k in obj.toJSON())2685 {2686 this[k] = obj[k];2687 }2688 if (!("extAttrs" in this))2689 {2690 this.extAttrs = [];2691 }2692 this.isUnforgeable = this.has_extended_attribute("LegacyUnforgeable");2693 this.isUnscopable = this.has_extended_attribute("Unscopable");2694}2695IdlInterfaceMember.prototype = Object.create(IdlObject.prototype);2696IdlInterfaceMember.prototype.toJSON = function() {2697 return this;2698};2699IdlInterfaceMember.prototype.is_to_json_regular_operation = function() {2700 return this.type == "operation" && this.special !== "static" && this.name == "toJSON";2701};2702IdlInterfaceMember.prototype.toString = function() {2703 function formatType(type) {2704 var result;2705 if (type.generic) {2706 result = type.generic + "<" + type.idlType.map(formatType).join(", ") + ">";2707 } else if (type.union) {2708 result = "(" + type.subtype.map(formatType).join(" or ") + ")";2709 } else {2710 result = type.idlType;2711 }2712 if (type.nullable) {2713 result += "?"2714 }2715 return result;2716 }2717 if (this.type === "operation") {2718 var args = this.arguments.map(function(m) {2719 return [2720 m.optional ? "optional " : "",2721 formatType(m.idlType),2722 m.variadic ? "..." : "",2723 ].join("");2724 }).join(", ");2725 return this.name + "(" + args + ")";2726 }2727 return this.name;2728}2729/// Internal helper functions ///2730function create_suitable_object(type)2731{2732 /**2733 * type is an object produced by the WebIDLParser.js "type" production. We2734 * return a JavaScript value that matches the type, if we can figure out2735 * how.2736 */2737 if (type.nullable)2738 {2739 return null;2740 }2741 switch (type.idlType)2742 {2743 case "any":2744 case "boolean":2745 return true;2746 case "byte": case "octet": case "short": case "unsigned short":2747 case "long": case "unsigned long": case "long long":2748 case "unsigned long long": case "float": case "double":2749 case "unrestricted float": case "unrestricted double":2750 return 7;2751 case "DOMString":2752 case "ByteString":2753 case "USVString":2754 return "foo";2755 case "object":2756 return {a: "b"};2757 case "Node":2758 return document.createTextNode("abc");2759 }2760 return null;2761}2762/// IdlEnum ///2763// Used for IdlArray.prototype.assert_type_is2764function IdlEnum(obj)2765{2766 /**2767 * obj is an object produced by the WebIDLParser.js "dictionary"2768 * production.2769 */2770 /** Self-explanatory. */2771 this.name = obj.name;2772 /** An array of values produced by the "enum" production. */2773 this.values = obj.values;2774}2775IdlEnum.prototype = Object.create(IdlObject.prototype);2776/// IdlCallback ///2777// Used for IdlArray.prototype.assert_type_is2778function IdlCallback(obj)2779{2780 /**2781 * obj is an object produced by the WebIDLParser.js "callback"2782 * production.2783 */2784 /** Self-explanatory. */2785 this.name = obj.name;2786 /** Arguments for the callback. */2787 this.arguments = obj.arguments;2788}2789IdlCallback.prototype = Object.create(IdlObject.prototype);2790/// IdlTypedef ///2791// Used for IdlArray.prototype.assert_type_is2792function IdlTypedef(obj)2793{2794 /**2795 * obj is an object produced by the WebIDLParser.js "typedef"2796 * production.2797 */2798 /** Self-explanatory. */2799 this.name = obj.name;2800 /** The idlType that we are supposed to be typedeffing to. */2801 this.idlType = obj.idlType;2802}2803IdlTypedef.prototype = Object.create(IdlObject.prototype);2804/// IdlNamespace ///2805function IdlNamespace(obj)2806{2807 this.name = obj.name;2808 this.extAttrs = obj.extAttrs;2809 this.untested = obj.untested;2810 /** A back-reference to our IdlArray. */2811 this.array = obj.array;2812 /** An array of IdlInterfaceMembers. */2813 this.members = obj.members.map(m => new IdlInterfaceMember(m));2814}2815IdlNamespace.prototype = Object.create(IdlObject.prototype);2816IdlNamespace.prototype.do_member_operation_asserts = function (memberHolderObject, member, a_test)2817{2818 var desc = Object.getOwnPropertyDescriptor(memberHolderObject, member.name);2819 assert_false("get" in desc, "property should not have a getter");2820 assert_false("set" in desc, "property should not have a setter");2821 assert_equals(2822 desc.writable,2823 !member.isUnforgeable,2824 "property should be writable if and only if not unforgeable");2825 assert_true(desc.enumerable, "property should be enumerable");2826 assert_equals(2827 desc.configurable,2828 !member.isUnforgeable,2829 "property should be configurable if and only if not unforgeable");2830 assert_equals(2831 typeof memberHolderObject[member.name],2832 "function",2833 "property must be a function");2834 assert_equals(2835 memberHolderObject[member.name].length,2836 minOverloadLength(this.members.filter(function(m) {2837 return m.type == "operation" && m.name == member.name;2838 })),2839 "operation has wrong .length");2840 a_test.done();2841}2842IdlNamespace.prototype.test_member_operation = function(member)2843{2844 if (!shouldRunSubTest(this.name)) {2845 return;2846 }2847 var a_test = subsetTestByKey(2848 this.name,2849 async_test,2850 this.name + ' namespace: operation ' + member);2851 a_test.step(function() {2852 assert_own_property(2853 self[this.name],2854 member.name,2855 'namespace object missing operation ' + format_value(member.name));2856 this.do_member_operation_asserts(self[this.name], member, a_test);2857 }.bind(this));2858};2859IdlNamespace.prototype.test_member_attribute = function (member)2860{2861 if (!shouldRunSubTest(this.name)) {2862 return;2863 }2864 var a_test = subsetTestByKey(2865 this.name,2866 async_test,2867 this.name + ' namespace: attribute ' + member.name);2868 a_test.step(function()2869 {2870 assert_own_property(2871 self[this.name],2872 member.name,2873 this.name + ' does not have property ' + format_value(member.name));2874 var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name);2875 assert_equals(desc.set, undefined, "setter must be undefined for namespace members");2876 a_test.done();2877 }.bind(this));2878};2879IdlNamespace.prototype.test_self = function ()2880{2881 /**2882 * TODO(lukebjerring): Assert:2883 * - "Note that unlike interfaces or dictionaries, namespaces do not create types."2884 */2885 subsetTestByKey(this.name, test, () => {2886 assert_true(this.extAttrs.every(o => o.name === "Exposed" || o.name === "SecureContext"),2887 "Only the [Exposed] and [SecureContext] extended attributes are applicable to namespaces");2888 assert_true(this.has_extended_attribute("Exposed"),2889 "Namespaces must be annotated with the [Exposed] extended attribute");2890 }, `${this.name} namespace: extended attributes`);2891 const namespaceObject = self[this.name];2892 subsetTestByKey(this.name, test, () => {2893 const desc = Object.getOwnPropertyDescriptor(self, this.name);2894 assert_equals(desc.value, namespaceObject, `wrong value for ${this.name} namespace object`);2895 assert_true(desc.writable, "namespace object should be writable");2896 assert_false(desc.enumerable, "namespace object should not be enumerable");2897 assert_true(desc.configurable, "namespace object should be configurable");2898 assert_false("get" in desc, "namespace object should not have a getter");2899 assert_false("set" in desc, "namespace object should not have a setter");2900 }, `${this.name} namespace: property descriptor`);2901 subsetTestByKey(this.name, test, () => {2902 assert_true(Object.isExtensible(namespaceObject));2903 }, `${this.name} namespace: [[Extensible]] is true`);2904 subsetTestByKey(this.name, test, () => {2905 assert_true(namespaceObject instanceof Object);2906 if (this.name === "console") {2907 // https://console.spec.whatwg.org/#console-namespace2908 const namespacePrototype = Object.getPrototypeOf(namespaceObject);2909 assert_equals(Reflect.ownKeys(namespacePrototype).length, 0);2910 assert_equals(Object.getPrototypeOf(namespacePrototype), Object.prototype);2911 } else {2912 assert_equals(Object.getPrototypeOf(namespaceObject), Object.prototype);2913 }2914 }, `${this.name} namespace: [[Prototype]] is Object.prototype`);2915 subsetTestByKey(this.name, test, () => {2916 assert_equals(typeof namespaceObject, "object");2917 }, `${this.name} namespace: typeof is "object"`);2918 subsetTestByKey(this.name, test, () => {2919 assert_equals(2920 Object.getOwnPropertyDescriptor(namespaceObject, "length"),2921 undefined,2922 "length property must be undefined"2923 );2924 }, `${this.name} namespace: has no length property`);2925 subsetTestByKey(this.name, test, () => {2926 assert_equals(2927 Object.getOwnPropertyDescriptor(namespaceObject, "name"),2928 undefined,2929 "name property must be undefined"2930 );2931 }, `${this.name} namespace: has no name property`);2932};2933IdlNamespace.prototype.test = function ()2934{2935 if (!this.untested) {2936 this.test_self();2937 }2938 for (const v of Object.values(this.members)) {2939 switch (v.type) {2940 case 'operation':2941 this.test_member_operation(v);2942 break;2943 case 'attribute':2944 this.test_member_attribute(v);2945 break;2946 default:2947 throw 'Invalid namespace member ' + v.name + ': ' + v.type + ' not supported';2948 }2949 };2950};2951}());2952/**2953 * idl_test is a promise_test wrapper that handles the fetching of the IDL,2954 * avoiding repetitive boilerplate.2955 *2956 * @param {String|String[]} srcs Spec name(s) for source idl files (fetched from2957 * /interfaces/{name}.idl).2958 * @param {String|String[]} deps Spec name(s) for dependency idl files (fetched2959 * from /interfaces/{name}.idl). Order is important - dependencies from2960 * each source will only be included if they're already know to be a2961 * dependency (i.e. have already been seen).2962 * @param {Function} setup_func Function for extra setup of the idl_array, such2963 * as adding objects. Do not call idl_array.test() in the setup; it is2964 * called by this function (idl_test).2965 */2966function idl_test(srcs, deps, idl_setup_func) {2967 return promise_test(function (t) {2968 var idl_array = new IdlArray();2969 var setup_error = null;2970 const validationIgnored = [2971 "constructor-member",2972 "dict-arg-default",2973 "require-exposed"2974 ];2975 return Promise.all(2976 srcs.concat(deps).map(fetch_spec))2977 .then(function(results) {2978 const astArray = results.map(result =>2979 WebIDL2.parse(result.idl, { sourceName: result.spec })2980 );2981 test(() => {2982 const validations = WebIDL2.validate(astArray)2983 .filter(v => !validationIgnored.includes(v.ruleName));2984 if (validations.length) {2985 const message = validations.map(v => v.message).join("\n\n");2986 throw new Error(message);2987 }2988 }, "idl_test validation");2989 for (var i = 0; i < srcs.length; i++) {2990 idl_array.internal_add_idls(astArray[i]);2991 }2992 for (var i = srcs.length; i < srcs.length + deps.length; i++) {2993 idl_array.internal_add_dependency_idls(astArray[i]);2994 }2995 })2996 .then(function() {2997 if (idl_setup_func) {2998 return idl_setup_func(idl_array, t);2999 }3000 })3001 .catch(function(e) { setup_error = e || 'IDL setup failed.'; })3002 .then(function () {3003 var error = setup_error;3004 try {3005 idl_array.test(); // Test what we can.3006 } catch (e) {3007 // If testing fails hard here, the original setup error3008 // is more likely to be the real cause.3009 error = error || e;3010 }3011 if (error) {3012 throw error;3013 }3014 });3015 }, 'idl_test setup');3016}3017/**3018 * fetch_spec is a shorthand for a Promise that fetches the spec's content.3019 */3020function fetch_spec(spec) {3021 var url = '/interfaces/' + spec + '.idl';3022 return fetch(url).then(function (r) {3023 if (!r.ok) {3024 throw new IdlHarnessError("Error fetching " + url + ".");3025 }3026 return r.text();3027 }).then(idl => ({ spec, idl }));3028}...
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('webpagetest');2var wpt = new WebPageTest('www.webpagetest.org');3 if(err) {4 } else {5 }6});7### getLocations(callback)8var wpt = require('webpagetest');9var wpt = new WebPageTest('www.webpagetest.org');10wpt.getLocations(function(err, data) {11 if(err) {12 } else {13 }14});15### getLocations(callback)16var wpt = require('webpagetest');17var wpt = new WebPageTest('www.webpagetest.org');18wpt.getLocations(function(err, data) {19 if(err) {20 } else {21 }22});23### getTesters(callback)24var wpt = require('webpagetest');25var wpt = new WebPageTest('www.webpagetest.org');26wpt.getTesters(function(err, data) {27 if(err) {28 } else {29 }30});31### getTestStatus(testId, callback)32var wpt = require('webpagetest');33var wpt = new WebPageTest('www.webpagetest.org');34wpt.getTestStatus('140416_1C_1', function(err, data) {35 if(err) {36 } else {37 }38});39### getTestResults(testId, callback)
Using AI Code Generation
1var wpt = require('webpagetest');2var webpagetest = new wpt('www.webpagetest.org');3 lighthouseConfig: {4 "settings": {5 }6 },7}, function (err, data) {8 if (err) {9 console.log(err);10 } else {11 console.log('Test Status: ' + data.statusCode);12 console.log('Test Results: ' + data.statusText);13 if (data.statusCode == 200) {14 console.log('Test ID: ' + data.data.testId);15 console.log('Test URL: ' + data.data.summary);16 console.log('Lighthouse Score: ' + data.data.lighthouseResult.categories.performance.score * 100);17 }18 }19});
Using AI Code Generation
1var wpt = require('webpagetest');2var client = wpt('www.webpagetest.org');3 console.log(data);4});5 { firstView:6 { loadTime: 1141,7 score_gfonts: 100 },8 { loadTime: 0,9 score_gfonts: 0 } },10 { firstView
Using AI Code Generation
1var wpt = require('webpagetest');2var wpt = new WebPageTest('www.webpagetest.org');3 lighthouseConfig: {4 "settings": {5 },6 "categories": {7 "performance": {8 {9 },10 {11 },12 {13 },14 {15 },16 {17 },18 {19 },20 {21 },22 {23 },24 {25 },26 {27 },28 {29 },30 {31 },32 {33 },34 {35 },36 {37 },38 {39 },40 {
Using AI Code Generation
1var wptAgent = require('wptAgent');2wptAgent.testedPartials('test', function(err, result) {3 console.log(result);4});5var wptAgent = require('wptAgent');6wptAgent.testedPartials('test', function(err, result) {7 console.log(result);8});9var wptAgent = require('wptAgent');10wptAgent.testedPartials('test', function(err, result) {11 console.log(result);12});13var wptAgent = require('wptAgent');14wptAgent.testedPartials('test', function(err, result) {15 console.log(result);16});17var wptAgent = require('wptAgent');18wptAgent.testedPartials('test', function(err, result) {19 console.log(result);20});21var wptAgent = require('wptAgent');22wptAgent.testedPartials('test', function(err, result) {23 console.log(result);24});25var wptAgent = require('wptAgent');26wptAgent.testedPartials('test', function(err, result) {27 console.log(result);28});29var wptAgent = require('wptAgent');30wptAgent.testedPartials('test', function(err, result) {31 console.log(result);32});33var wptAgent = require('wptAgent');34wptAgent.testedPartials('test', function(err, result) {35 console.log(result);36});37var wptAgent = require('wptAgent');38wptAgent.testedPartials('test', function(err, result) {
Using AI Code Generation
1var wpt = require('./index.js');2var fs = require('fs');3var partialsFile = process.argv[2];4var testFile = process.argv[3];5var dataFile = process.argv[4];6var partials = fs.readFileSync(partialsFile, 'utf8');7var test = fs.readFileSync(testFile, 'utf8');8var data = fs.readFileSync(dataFile, 'utf8');9wpt.testedPartials(partials, test, data, function(err, res) {10 if (err) {11 console.log(err);12 } else {13 console.log(res);14 }15});
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!!