How to use memberExposure method in wpt

Best JavaScript code snippet using wpt

idlharness.js

Source:idlharness.js Github

copy

Full Screen

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

Full Screen

Full Screen

aflprep_idlharness.js

Source:aflprep_idlharness.js Github

copy

Full Screen

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 }));...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1var wpt = require('webpagetest');2var wpt = new WebPageTest('www.webpagetest.org');3var options = {4 videoParams: {5 }6};7 if (err) return console.error(err);8 console.log('Test submitted to WebPagetest for %s', data.data.testUrl);9 console.log('Test ID: %s', data.data.testId);10 wpt.getTestResults(data.data.testId, function(err, data) {11 if (err) return console.error(err);12 console.log('Test results for %s', data.data.testUrl);13 console.log('First View: %s', data.data.average.firstView.loadTime);14 console.log('Repeat View: %s', data.data.average.repeatView.loadTime);15 });16});17var wpt = require('webpagetest');18var wpt = new WebPageTest('www.webpagetest.org');19var options = {20 videoParams: {21 }22};23 if (err) return console.error(err);24 console.log('Test submitted to WebPagetest for %s', data.data.testUrl);25 console.log('Test ID: %s', data.data.testId);26 wpt.getTestResults(data.data.testId, function(err, data) {27 if (err) return console.error(err);28 console.log('Test results for %s', data.data.testUrl);29 console.log('First View: %s', data.data.average.firstView.loadTime);30 console.log('Repeat View: %s', data.data.average.repeatView.loadTime);31 });32});

Full Screen

Using AI Code Generation

copy

Full Screen

1var Wpt = require('wpt-api');2var wpt = new Wpt(process.env.WPT_API_KEY);3wpt.memberExposure(url, function (err, data) {4 if (err) {5 console.log(err);6 }7 else {8 console.log(data);9 }10});11var Wpt = require('wpt-api');12var wpt = new Wpt(process.env.WPT_API_KEY);13wpt.memberExposureSummary(url, function (err, data) {14 if (err) {15 console.log(err);16 }17 else {18 console.log(data);19 }20});21var Wpt = require('wpt-api');22var wpt = new Wpt(process.env.WPT_API_KEY);23wpt.runTest(url, function (err, data) {24 if (err) {25 console.log(err);26 }27 else {28 console.log(data);29 }30});31var Wpt = require('wpt-api');32var wpt = new Wpt(process.env.WPT_API_KEY);33var testId = "160221_0J_1f1";34wpt.testResults(testId, function (err, data) {35 if (err) {36 console.log(err);37 }38 else {39 console.log(data);40 }41});42var Wpt = require('wpt-api');43var wpt = new Wpt(process.env.WPT_API_KEY);

Full Screen

Using AI Code Generation

copy

Full Screen

1var wpt = require('./wpt.js');2var wptObj = new wpt();3wptObj.memberExposure(url, function(err, data){4 if(err){5 console.log(err);6 }7 else{8 console.log(data);9 }10});11{ 12 { 13 { 14 {

Full Screen

Automation Testing Tutorials

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.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run wpt automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful