| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 Distributed under both the W3C Test Suite License [1] and the W3C | |
| 3 3-clause BSD License [2]. To contribute to a W3C Test Suite, see the | |
| 4 policies and contribution forms [3]. | |
| 5 | |
| 6 [1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license | |
| 7 [2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license | |
| 8 [3] http://www.w3.org/2004/10/27-testcases | |
| 9 */ | |
| 10 | |
| 11 /* For user documentation see docs/idlharness.md */ | |
| 12 | |
| 13 /** | |
| 14 * Notes for people who want to edit this file (not just use it as a library): | |
| 15 * | |
| 16 * Most of the interesting stuff happens in the derived classes of IdlObject, | |
| 17 * especially IdlInterface. The entry point for all IdlObjects is .test(), | |
| 18 * which is called by IdlArray.test(). An IdlObject is conceptually just | |
| 19 * "thing we want to run tests on", and an IdlArray is an array of IdlObjects | |
| 20 * with some additional data thrown in. | |
| 21 * | |
| 22 * The object model is based on what WebIDLParser.js produces, which is in turn | |
| 23 * based on its pegjs grammar. If you want to figure out what properties an | |
| 24 * object will have from WebIDLParser.js, the best way is to look at the | |
| 25 * grammar: | |
| 26 * | |
| 27 * https://github.com/darobin/webidl.js/blob/master/lib/grammar.peg | |
| 28 * | |
| 29 * So for instance: | |
| 30 * | |
| 31 * // interface definition | |
| 32 * interface | |
| 33 * = extAttrs:extendedAttributeList? S? "interface" S name:identifier w
herit:ifInheritance? w "{" w mem:ifMember* w "}" w ";" w | |
| 34 * { return { type: "interface", name: name, inheritance: herit, membe
rs: mem, extAttrs: extAttrs }; } | |
| 35 * | |
| 36 * This means that an "interface" object will have a .type property equal to | |
| 37 * the string "interface", a .name property equal to the identifier that the | |
| 38 * parser found, an .inheritance property equal to either null or the result of | |
| 39 * the "ifInheritance" production found elsewhere in the grammar, and so on. | |
| 40 * After each grammatical production is a JavaScript function in curly braces | |
| 41 * that gets called with suitable arguments and returns some JavaScript value. | |
| 42 * | |
| 43 * (Note that the version of WebIDLParser.js we use might sometimes be | |
| 44 * out-of-date or forked.) | |
| 45 * | |
| 46 * The members and methods of the classes defined by this file are all at least | |
| 47 * briefly documented, hopefully. | |
| 48 */ | |
| 49 (function(){ | |
| 50 "use strict"; | |
| 51 /// Helpers /// | |
| 52 function constValue (cnt) { | |
| 53 if (cnt.type === "null") return null; | |
| 54 if (cnt.type === "NaN") return NaN; | |
| 55 if (cnt.type === "Infinity") return cnt.negative ? -Infinity : Infinity; | |
| 56 return cnt.value; | |
| 57 } | |
| 58 | |
| 59 function minOverloadLength(overloads) { | |
| 60 if (!overloads.length) { | |
| 61 return 0; | |
| 62 } | |
| 63 | |
| 64 return overloads.map(function(attr) { | |
| 65 return attr.arguments ? attr.arguments.filter(function(arg) { | |
| 66 return !arg.optional && !arg.variadic; | |
| 67 }).length : 0; | |
| 68 }) | |
| 69 .reduce(function(m, n) { return Math.min(m, n); }); | |
| 70 } | |
| 71 | |
| 72 function throwOrReject(a_test, operation, fn, obj, args, message, cb) { | |
| 73 if (operation.idlType.generic !== "Promise") { | |
| 74 assert_throws(new TypeError(), function() { | |
| 75 fn.apply(obj, args); | |
| 76 }, message); | |
| 77 cb(); | |
| 78 } else { | |
| 79 try { | |
| 80 promise_rejects(a_test, new TypeError(), fn.apply(obj, args)).then(c
b, cb); | |
| 81 } catch (e){ | |
| 82 a_test.step(function() { | |
| 83 assert_unreached("Throws \"" + e + "\" instead of rejecting prom
ise"); | |
| 84 cb(); | |
| 85 }); | |
| 86 } | |
| 87 } | |
| 88 } | |
| 89 | |
| 90 function awaitNCallbacks(n, cb, ctx) { | |
| 91 var counter = 0; | |
| 92 return function() { | |
| 93 counter++; | |
| 94 if (counter >= n) { | |
| 95 cb(); | |
| 96 } | |
| 97 }; | |
| 98 } | |
| 99 | |
| 100 var fround = (function(){ | |
| 101 if (Math.fround) return Math.fround; | |
| 102 | |
| 103 var arr = new Float32Array(1); | |
| 104 return function fround(n) { | |
| 105 arr[0] = n; | |
| 106 return arr[0]; | |
| 107 }; | |
| 108 })(); | |
| 109 | |
| 110 /// IdlArray /// | |
| 111 // Entry point | |
| 112 self.IdlArray = function() | |
| 113 //@{ | |
| 114 { | |
| 115 /** | |
| 116 * A map from strings to the corresponding named IdlObject, such as | |
| 117 * IdlInterface or IdlException. These are the things that test() will run | |
| 118 * tests on. | |
| 119 */ | |
| 120 this.members = {}; | |
| 121 | |
| 122 /** | |
| 123 * A map from strings to arrays of strings. The keys are interface or | |
| 124 * exception names, and are expected to also exist as keys in this.members | |
| 125 * (otherwise they'll be ignored). This is populated by add_objects() -- | |
| 126 * see documentation at the start of the file. The actual tests will be | |
| 127 * run by calling this.members[name].test_object(obj) for each obj in | |
| 128 * this.objects[name]. obj is a string that will be eval'd to produce a | |
| 129 * JavaScript value, which is supposed to be an object implementing the | |
| 130 * given IdlObject (interface, exception, etc.). | |
| 131 */ | |
| 132 this.objects = {}; | |
| 133 | |
| 134 /** | |
| 135 * When adding multiple collections of IDLs one at a time, an earlier one | |
| 136 * might contain a partial interface or implements statement that depends | |
| 137 * on a later one. Save these up and handle them right before we run | |
| 138 * tests. | |
| 139 * | |
| 140 * .partials is simply an array of objects from WebIDLParser.js' | |
| 141 * "partialinterface" production. .implements maps strings to arrays of | |
| 142 * strings, such that | |
| 143 * | |
| 144 * A implements B; | |
| 145 * A implements C; | |
| 146 * D implements E; | |
| 147 * | |
| 148 * results in { A: ["B", "C"], D: ["E"] }. | |
| 149 */ | |
| 150 this.partials = []; | |
| 151 this["implements"] = {}; | |
| 152 }; | |
| 153 | |
| 154 //@} | |
| 155 IdlArray.prototype.add_idls = function(raw_idls) | |
| 156 //@{ | |
| 157 { | |
| 158 /** Entry point. See documentation at beginning of file. */ | |
| 159 this.internal_add_idls(WebIDL2.parse(raw_idls)); | |
| 160 }; | |
| 161 | |
| 162 //@} | |
| 163 IdlArray.prototype.add_untested_idls = function(raw_idls) | |
| 164 //@{ | |
| 165 { | |
| 166 /** Entry point. See documentation at beginning of file. */ | |
| 167 var parsed_idls = WebIDL2.parse(raw_idls); | |
| 168 for (var i = 0; i < parsed_idls.length; i++) | |
| 169 { | |
| 170 parsed_idls[i].untested = true; | |
| 171 if ("members" in parsed_idls[i]) | |
| 172 { | |
| 173 for (var j = 0; j < parsed_idls[i].members.length; j++) | |
| 174 { | |
| 175 parsed_idls[i].members[j].untested = true; | |
| 176 } | |
| 177 } | |
| 178 } | |
| 179 this.internal_add_idls(parsed_idls); | |
| 180 }; | |
| 181 | |
| 182 //@} | |
| 183 IdlArray.prototype.internal_add_idls = function(parsed_idls) | |
| 184 //@{ | |
| 185 { | |
| 186 /** | |
| 187 * Internal helper called by add_idls() and add_untested_idls(). | |
| 188 * parsed_idls is an array of objects that come from WebIDLParser.js's | |
| 189 * "definitions" production. The add_untested_idls() entry point | |
| 190 * additionally sets an .untested property on each object (and its | |
| 191 * .members) so that they'll be skipped by test() -- they'll only be | |
| 192 * used for base interfaces of tested interfaces, return types, etc. | |
| 193 */ | |
| 194 parsed_idls.forEach(function(parsed_idl) | |
| 195 { | |
| 196 if (parsed_idl.type == "interface" && parsed_idl.partial) | |
| 197 { | |
| 198 this.partials.push(parsed_idl); | |
| 199 return; | |
| 200 } | |
| 201 | |
| 202 if (parsed_idl.type == "implements") | |
| 203 { | |
| 204 if (!(parsed_idl.target in this["implements"])) | |
| 205 { | |
| 206 this["implements"][parsed_idl.target] = []; | |
| 207 } | |
| 208 this["implements"][parsed_idl.target].push(parsed_idl["implements"])
; | |
| 209 return; | |
| 210 } | |
| 211 | |
| 212 parsed_idl.array = this; | |
| 213 if (parsed_idl.name in this.members) | |
| 214 { | |
| 215 throw "Duplicate identifier " + parsed_idl.name; | |
| 216 } | |
| 217 switch(parsed_idl.type) | |
| 218 { | |
| 219 case "interface": | |
| 220 this.members[parsed_idl.name] = | |
| 221 new IdlInterface(parsed_idl, /* is_callback = */ false); | |
| 222 break; | |
| 223 | |
| 224 case "dictionary": | |
| 225 // Nothing to test, but we need the dictionary info around for type | |
| 226 // checks | |
| 227 this.members[parsed_idl.name] = new IdlDictionary(parsed_idl); | |
| 228 break; | |
| 229 | |
| 230 case "typedef": | |
| 231 this.members[parsed_idl.name] = new IdlTypedef(parsed_idl); | |
| 232 break; | |
| 233 | |
| 234 case "callback": | |
| 235 // TODO | |
| 236 console.log("callback not yet supported"); | |
| 237 break; | |
| 238 | |
| 239 case "enum": | |
| 240 this.members[parsed_idl.name] = new IdlEnum(parsed_idl); | |
| 241 break; | |
| 242 | |
| 243 case "callback interface": | |
| 244 this.members[parsed_idl.name] = | |
| 245 new IdlInterface(parsed_idl, /* is_callback = */ true); | |
| 246 break; | |
| 247 | |
| 248 default: | |
| 249 throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported
"; | |
| 250 } | |
| 251 }.bind(this)); | |
| 252 }; | |
| 253 | |
| 254 //@} | |
| 255 IdlArray.prototype.add_objects = function(dict) | |
| 256 //@{ | |
| 257 { | |
| 258 /** Entry point. See documentation at beginning of file. */ | |
| 259 for (var k in dict) | |
| 260 { | |
| 261 if (k in this.objects) | |
| 262 { | |
| 263 this.objects[k] = this.objects[k].concat(dict[k]); | |
| 264 } | |
| 265 else | |
| 266 { | |
| 267 this.objects[k] = dict[k]; | |
| 268 } | |
| 269 } | |
| 270 }; | |
| 271 | |
| 272 //@} | |
| 273 IdlArray.prototype.prevent_multiple_testing = function(name) | |
| 274 //@{ | |
| 275 { | |
| 276 /** Entry point. See documentation at beginning of file. */ | |
| 277 this.members[name].prevent_multiple_testing = true; | |
| 278 }; | |
| 279 | |
| 280 //@} | |
| 281 IdlArray.prototype.recursively_get_implements = function(interface_name) | |
| 282 //@{ | |
| 283 { | |
| 284 /** | |
| 285 * Helper function for test(). Returns an array of things that implement | |
| 286 * interface_name, so if the IDL contains | |
| 287 * | |
| 288 * A implements B; | |
| 289 * B implements C; | |
| 290 * B implements D; | |
| 291 * | |
| 292 * then recursively_get_implements("A") should return ["B", "C", "D"]. | |
| 293 */ | |
| 294 var ret = this["implements"][interface_name]; | |
| 295 if (ret === undefined) | |
| 296 { | |
| 297 return []; | |
| 298 } | |
| 299 for (var i = 0; i < this["implements"][interface_name].length; i++) | |
| 300 { | |
| 301 ret = ret.concat(this.recursively_get_implements(ret[i])); | |
| 302 if (ret.indexOf(ret[i]) != ret.lastIndexOf(ret[i])) | |
| 303 { | |
| 304 throw "Circular implements statements involving " + ret[i]; | |
| 305 } | |
| 306 } | |
| 307 return ret; | |
| 308 }; | |
| 309 | |
| 310 //@} | |
| 311 IdlArray.prototype.test = function() | |
| 312 //@{ | |
| 313 { | |
| 314 /** Entry point. See documentation at beginning of file. */ | |
| 315 | |
| 316 // First merge in all the partial interfaces and implements statements we | |
| 317 // encountered. | |
| 318 this.partials.forEach(function(parsed_idl) | |
| 319 { | |
| 320 if (!(parsed_idl.name in this.members) | |
| 321 || !(this.members[parsed_idl.name] instanceof IdlInterface)) | |
| 322 { | |
| 323 throw "Partial interface " + parsed_idl.name + " with no original in
terface"; | |
| 324 } | |
| 325 if (parsed_idl.extAttrs) | |
| 326 { | |
| 327 parsed_idl.extAttrs.forEach(function(extAttr) | |
| 328 { | |
| 329 this.members[parsed_idl.name].extAttrs.push(extAttr); | |
| 330 }.bind(this)); | |
| 331 } | |
| 332 parsed_idl.members.forEach(function(member) | |
| 333 { | |
| 334 this.members[parsed_idl.name].members.push(new IdlInterfaceMember(me
mber)); | |
| 335 }.bind(this)); | |
| 336 }.bind(this)); | |
| 337 this.partials = []; | |
| 338 | |
| 339 for (var lhs in this["implements"]) | |
| 340 { | |
| 341 this.recursively_get_implements(lhs).forEach(function(rhs) | |
| 342 { | |
| 343 var errStr = lhs + " implements " + rhs + ", but "; | |
| 344 if (!(lhs in this.members)) throw errStr + lhs + " is undefined."; | |
| 345 if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs
+ " is not an interface."; | |
| 346 if (!(rhs in this.members)) throw errStr + rhs + " is undefined."; | |
| 347 if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs
+ " is not an interface."; | |
| 348 this.members[rhs].members.forEach(function(member) | |
| 349 { | |
| 350 this.members[lhs].members.push(new IdlInterfaceMember(member)); | |
| 351 }.bind(this)); | |
| 352 }.bind(this)); | |
| 353 } | |
| 354 this["implements"] = {}; | |
| 355 | |
| 356 // Now run test() on every member, and test_object() for every object. | |
| 357 for (var name in this.members) | |
| 358 { | |
| 359 this.members[name].test(); | |
| 360 if (name in this.objects) | |
| 361 { | |
| 362 this.objects[name].forEach(function(str) | |
| 363 { | |
| 364 this.members[name].test_object(str); | |
| 365 }.bind(this)); | |
| 366 } | |
| 367 } | |
| 368 }; | |
| 369 | |
| 370 //@} | |
| 371 IdlArray.prototype.assert_type_is = function(value, type) | |
| 372 //@{ | |
| 373 { | |
| 374 /** | |
| 375 * Helper function that tests that value is an instance of type according | |
| 376 * to the rules of WebIDL. value is any JavaScript value, and type is an | |
| 377 * object produced by WebIDLParser.js' "type" production. That production | |
| 378 * is fairly elaborate due to the complexity of WebIDL's types, so it's | |
| 379 * best to look at the grammar to figure out what properties it might have. | |
| 380 */ | |
| 381 if (type.idlType == "any") | |
| 382 { | |
| 383 // No assertions to make | |
| 384 return; | |
| 385 } | |
| 386 | |
| 387 if (type.nullable && value === null) | |
| 388 { | |
| 389 // This is fine | |
| 390 return; | |
| 391 } | |
| 392 | |
| 393 if (type.array) | |
| 394 { | |
| 395 // TODO: not supported yet | |
| 396 return; | |
| 397 } | |
| 398 | |
| 399 if (type.sequence) | |
| 400 { | |
| 401 assert_true(Array.isArray(value), "is not array"); | |
| 402 if (!value.length) | |
| 403 { | |
| 404 // Nothing we can do. | |
| 405 return; | |
| 406 } | |
| 407 this.assert_type_is(value[0], type.idlType.idlType); | |
| 408 return; | |
| 409 } | |
| 410 | |
| 411 type = type.idlType; | |
| 412 | |
| 413 switch(type) | |
| 414 { | |
| 415 case "void": | |
| 416 assert_equals(value, undefined); | |
| 417 return; | |
| 418 | |
| 419 case "boolean": | |
| 420 assert_equals(typeof value, "boolean"); | |
| 421 return; | |
| 422 | |
| 423 case "byte": | |
| 424 assert_equals(typeof value, "number"); | |
| 425 assert_equals(value, Math.floor(value), "not an integer"); | |
| 426 assert_true(-128 <= value && value <= 127, "byte " + value + " not i
n range [-128, 127]"); | |
| 427 return; | |
| 428 | |
| 429 case "octet": | |
| 430 assert_equals(typeof value, "number"); | |
| 431 assert_equals(value, Math.floor(value), "not an integer"); | |
| 432 assert_true(0 <= value && value <= 255, "octet " + value + " not in
range [0, 255]"); | |
| 433 return; | |
| 434 | |
| 435 case "short": | |
| 436 assert_equals(typeof value, "number"); | |
| 437 assert_equals(value, Math.floor(value), "not an integer"); | |
| 438 assert_true(-32768 <= value && value <= 32767, "short " + value + "
not in range [-32768, 32767]"); | |
| 439 return; | |
| 440 | |
| 441 case "unsigned short": | |
| 442 assert_equals(typeof value, "number"); | |
| 443 assert_equals(value, Math.floor(value), "not an integer"); | |
| 444 assert_true(0 <= value && value <= 65535, "unsigned short " + value
+ " not in range [0, 65535]"); | |
| 445 return; | |
| 446 | |
| 447 case "long": | |
| 448 assert_equals(typeof value, "number"); | |
| 449 assert_equals(value, Math.floor(value), "not an integer"); | |
| 450 assert_true(-2147483648 <= value && value <= 2147483647, "long " + v
alue + " not in range [-2147483648, 2147483647]"); | |
| 451 return; | |
| 452 | |
| 453 case "unsigned long": | |
| 454 assert_equals(typeof value, "number"); | |
| 455 assert_equals(value, Math.floor(value), "not an integer"); | |
| 456 assert_true(0 <= value && value <= 4294967295, "unsigned long " + va
lue + " not in range [0, 4294967295]"); | |
| 457 return; | |
| 458 | |
| 459 case "long long": | |
| 460 assert_equals(typeof value, "number"); | |
| 461 return; | |
| 462 | |
| 463 case "unsigned long long": | |
| 464 case "DOMTimeStamp": | |
| 465 assert_equals(typeof value, "number"); | |
| 466 assert_true(0 <= value, "unsigned long long is negative"); | |
| 467 return; | |
| 468 | |
| 469 case "float": | |
| 470 assert_equals(typeof value, "number"); | |
| 471 assert_equals(value, fround(value), "float rounded to 32-bit float s
hould be itself"); | |
| 472 assert_not_equals(value, Infinity); | |
| 473 assert_not_equals(value, -Infinity); | |
| 474 assert_not_equals(value, NaN); | |
| 475 return; | |
| 476 | |
| 477 case "DOMHighResTimeStamp": | |
| 478 case "double": | |
| 479 assert_equals(typeof value, "number"); | |
| 480 assert_not_equals(value, Infinity); | |
| 481 assert_not_equals(value, -Infinity); | |
| 482 assert_not_equals(value, NaN); | |
| 483 return; | |
| 484 | |
| 485 case "unrestricted float": | |
| 486 assert_equals(typeof value, "number"); | |
| 487 assert_equals(value, fround(value), "unrestricted float rounded to 3
2-bit float should be itself"); | |
| 488 return; | |
| 489 | |
| 490 case "unrestricted double": | |
| 491 assert_equals(typeof value, "number"); | |
| 492 return; | |
| 493 | |
| 494 case "DOMString": | |
| 495 assert_equals(typeof value, "string"); | |
| 496 return; | |
| 497 | |
| 498 case "ByteString": | |
| 499 assert_equals(typeof value, "string"); | |
| 500 assert_regexp_match(value, /^[\x00-\x7F]*$/); | |
| 501 return; | |
| 502 | |
| 503 case "USVString": | |
| 504 assert_equals(typeof value, "string"); | |
| 505 assert_regexp_match(value, /^([\x00-\ud7ff\ue000-\uffff]|[\ud800-\ud
bff][\udc00-\udfff])*$/); | |
| 506 return; | |
| 507 | |
| 508 case "object": | |
| 509 assert_true(typeof value == "object" || typeof value == "function",
"wrong type: not object or function"); | |
| 510 return; | |
| 511 } | |
| 512 | |
| 513 if (!(type in this.members)) | |
| 514 { | |
| 515 throw "Unrecognized type " + type; | |
| 516 } | |
| 517 | |
| 518 if (this.members[type] instanceof IdlInterface) | |
| 519 { | |
| 520 // We don't want to run the full | |
| 521 // IdlInterface.prototype.test_instance_of, because that could result | |
| 522 // in an infinite loop. TODO: This means we don't have tests for | |
| 523 // NoInterfaceObject interfaces, and we also can't test objects that | |
| 524 // come from another self. | |
| 525 assert_true(typeof value == "object" || typeof value == "function", "wro
ng type: not object or function"); | |
| 526 if (value instanceof Object | |
| 527 && !this.members[type].has_extended_attribute("NoInterfaceObject") | |
| 528 && type in self) | |
| 529 { | |
| 530 assert_true(value instanceof self[type], "not instanceof " + type); | |
| 531 } | |
| 532 } | |
| 533 else if (this.members[type] instanceof IdlEnum) | |
| 534 { | |
| 535 assert_equals(typeof value, "string"); | |
| 536 } | |
| 537 else if (this.members[type] instanceof IdlDictionary) | |
| 538 { | |
| 539 // TODO: Test when we actually have something to test this on | |
| 540 } | |
| 541 else if (this.members[type] instanceof IdlTypedef) | |
| 542 { | |
| 543 // TODO: Test when we actually have something to test this on | |
| 544 } | |
| 545 else | |
| 546 { | |
| 547 throw "Type " + type + " isn't an interface or dictionary"; | |
| 548 } | |
| 549 }; | |
| 550 //@} | |
| 551 | |
| 552 /// IdlObject /// | |
| 553 function IdlObject() {} | |
| 554 IdlObject.prototype.test = function() | |
| 555 //@{ | |
| 556 { | |
| 557 /** | |
| 558 * By default, this does nothing, so no actual tests are run for IdlObjects | |
| 559 * that don't define any (e.g., IdlDictionary at the time of this writing). | |
| 560 */ | |
| 561 }; | |
| 562 | |
| 563 //@} | |
| 564 IdlObject.prototype.has_extended_attribute = function(name) | |
| 565 //@{ | |
| 566 { | |
| 567 /** | |
| 568 * This is only meaningful for things that support extended attributes, | |
| 569 * such as interfaces, exceptions, and members. | |
| 570 */ | |
| 571 return this.extAttrs.some(function(o) | |
| 572 { | |
| 573 return o.name == name; | |
| 574 }); | |
| 575 }; | |
| 576 | |
| 577 //@} | |
| 578 | |
| 579 /// IdlDictionary /// | |
| 580 // Used for IdlArray.prototype.assert_type_is | |
| 581 function IdlDictionary(obj) | |
| 582 //@{ | |
| 583 { | |
| 584 /** | |
| 585 * obj is an object produced by the WebIDLParser.js "dictionary" | |
| 586 * production. | |
| 587 */ | |
| 588 | |
| 589 /** Self-explanatory. */ | |
| 590 this.name = obj.name; | |
| 591 | |
| 592 /** An array of objects produced by the "dictionaryMember" production. */ | |
| 593 this.members = obj.members; | |
| 594 | |
| 595 /** | |
| 596 * The name (as a string) of the dictionary type we inherit from, or null | |
| 597 * if there is none. | |
| 598 */ | |
| 599 this.base = obj.inheritance; | |
| 600 } | |
| 601 | |
| 602 //@} | |
| 603 IdlDictionary.prototype = Object.create(IdlObject.prototype); | |
| 604 | |
| 605 /// IdlInterface /// | |
| 606 function IdlInterface(obj, is_callback) { | |
| 607 /** | |
| 608 * obj is an object produced by the WebIDLParser.js "interface" production. | |
| 609 */ | |
| 610 | |
| 611 /** Self-explanatory. */ | |
| 612 this.name = obj.name; | |
| 613 | |
| 614 /** A back-reference to our IdlArray. */ | |
| 615 this.array = obj.array; | |
| 616 | |
| 617 /** | |
| 618 * An indicator of whether we should run tests on the interface object and | |
| 619 * interface prototype object. Tests on members are controlled by .untested | |
| 620 * on each member, not this. | |
| 621 */ | |
| 622 this.untested = obj.untested; | |
| 623 | |
| 624 /** An array of objects produced by the "ExtAttr" production. */ | |
| 625 this.extAttrs = obj.extAttrs; | |
| 626 | |
| 627 /** An array of IdlInterfaceMembers. */ | |
| 628 this.members = obj.members.map(function(m){return new IdlInterfaceMember(m);
}); | |
| 629 if (this.has_extended_attribute("Unforgeable")) { | |
| 630 this.members | |
| 631 .filter(function(m) { return !m["static"] && (m.type == "attribute"
|| m.type == "operation"); }) | |
| 632 .forEach(function(m) { return m.isUnforgeable = true; }); | |
| 633 } | |
| 634 | |
| 635 /** | |
| 636 * The name (as a string) of the type we inherit from, or null if there is | |
| 637 * none. | |
| 638 */ | |
| 639 this.base = obj.inheritance; | |
| 640 | |
| 641 this._is_callback = is_callback; | |
| 642 } | |
| 643 IdlInterface.prototype = Object.create(IdlObject.prototype); | |
| 644 IdlInterface.prototype.is_callback = function() | |
| 645 //@{ | |
| 646 { | |
| 647 return this._is_callback; | |
| 648 }; | |
| 649 //@} | |
| 650 | |
| 651 IdlInterface.prototype.has_constants = function() | |
| 652 //@{ | |
| 653 { | |
| 654 return this.members.some(function(member) { | |
| 655 return member.type === "const"; | |
| 656 }); | |
| 657 }; | |
| 658 //@} | |
| 659 | |
| 660 IdlInterface.prototype.is_global = function() | |
| 661 //@{ | |
| 662 { | |
| 663 return this.extAttrs.some(function(attribute) { | |
| 664 return attribute.name === "Global" || | |
| 665 attribute.name === "PrimaryGlobal"; | |
| 666 }); | |
| 667 }; | |
| 668 //@} | |
| 669 | |
| 670 IdlInterface.prototype.test = function() | |
| 671 //@{ | |
| 672 { | |
| 673 if (this.has_extended_attribute("NoInterfaceObject")) | |
| 674 { | |
| 675 // No tests to do without an instance. TODO: We should still be able | |
| 676 // to run tests on the prototype object, if we obtain one through some | |
| 677 // other means. | |
| 678 return; | |
| 679 } | |
| 680 | |
| 681 if (!this.untested) | |
| 682 { | |
| 683 // First test things to do with the exception/interface object and | |
| 684 // exception/interface prototype object. | |
| 685 this.test_self(); | |
| 686 } | |
| 687 // Then test things to do with its members (constants, fields, attributes, | |
| 688 // operations, . . .). These are run even if .untested is true, because | |
| 689 // members might themselves be marked as .untested. This might happen to | |
| 690 // interfaces if the interface itself is untested but a partial interface | |
| 691 // that extends it is tested -- then the interface itself and its initial | |
| 692 // members will be marked as untested, but the members added by the partial | |
| 693 // interface are still tested. | |
| 694 this.test_members(); | |
| 695 }; | |
| 696 //@} | |
| 697 | |
| 698 IdlInterface.prototype.test_self = function() | |
| 699 //@{ | |
| 700 { | |
| 701 test(function() | |
| 702 { | |
| 703 // This function tests WebIDL as of 2015-01-13. | |
| 704 // TODO: Consider [Exposed]. | |
| 705 | |
| 706 // "For every interface that is exposed in a given ECMAScript global | |
| 707 // environment and: | |
| 708 // * is a callback interface that has constants declared on it, or | |
| 709 // * is a non-callback interface that is not declared with the | |
| 710 // [NoInterfaceObject] extended attribute, | |
| 711 // a corresponding property MUST exist on the ECMAScript global object. | |
| 712 // The name of the property is the identifier of the interface, and its | |
| 713 // value is an object called the interface object. | |
| 714 // The property has the attributes { [[Writable]]: true, | |
| 715 // [[Enumerable]]: false, [[Configurable]]: true }." | |
| 716 if (this.is_callback() && !this.has_constants()) { | |
| 717 return; | |
| 718 } | |
| 719 | |
| 720 // TODO: Should we test here that the property is actually writable | |
| 721 // etc., or trust getOwnPropertyDescriptor? | |
| 722 assert_own_property(self, this.name, | |
| 723 "self does not have own property " + format_value(th
is.name)); | |
| 724 var desc = Object.getOwnPropertyDescriptor(self, this.name); | |
| 725 assert_false("get" in desc, "self's property " + format_value(this.name)
+ " has getter"); | |
| 726 assert_false("set" in desc, "self's property " + format_value(this.name)
+ " has setter"); | |
| 727 assert_true(desc.writable, "self's property " + format_value(this.name)
+ " is not writable"); | |
| 728 assert_false(desc.enumerable, "self's property " + format_value(this.nam
e) + " is enumerable"); | |
| 729 assert_true(desc.configurable, "self's property " + format_value(this.na
me) + " is not configurable"); | |
| 730 | |
| 731 if (this.is_callback()) { | |
| 732 // "The internal [[Prototype]] property of an interface object for | |
| 733 // a callback interface MUST be the Object.prototype object." | |
| 734 assert_equals(Object.getPrototypeOf(self[this.name]), Object.prototy
pe, | |
| 735 "prototype of self's property " + format_value(this.na
me) + " is not Object.prototype"); | |
| 736 | |
| 737 return; | |
| 738 } | |
| 739 | |
| 740 // "The interface object for a given non-callback interface is a | |
| 741 // function object." | |
| 742 // "If an object is defined to be a function object, then it has | |
| 743 // characteristics as follows:" | |
| 744 | |
| 745 // Its [[Prototype]] internal property is otherwise specified (see | |
| 746 // below). | |
| 747 | |
| 748 // "* Its [[Get]] internal property is set as described in ECMA-262 | |
| 749 // section 9.1.8." | |
| 750 // Not much to test for this. | |
| 751 | |
| 752 // "* Its [[Construct]] internal property is set as described in | |
| 753 // ECMA-262 section 19.2.2.3." | |
| 754 // Tested below if no constructor is defined. TODO: test constructors | |
| 755 // if defined. | |
| 756 | |
| 757 // "* Its @@hasInstance property is set as described in ECMA-262 | |
| 758 // section 19.2.3.8, unless otherwise specified." | |
| 759 // TODO | |
| 760 | |
| 761 // ES6 (rev 30) 19.1.3.6: | |
| 762 // "Else, if O has a [[Call]] internal method, then let builtinTag be | |
| 763 // "Function"." | |
| 764 assert_class_string(self[this.name], "Function", "class string of " + th
is.name); | |
| 765 | |
| 766 // "The [[Prototype]] internal property of an interface object for a | |
| 767 // non-callback interface is determined as follows:" | |
| 768 var prototype = Object.getPrototypeOf(self[this.name]); | |
| 769 if (this.base) { | |
| 770 // "* If the interface inherits from some other interface, the | |
| 771 // value of [[Prototype]] is the interface object for that other | |
| 772 // interface." | |
| 773 var has_interface_object = | |
| 774 !this.array | |
| 775 .members[this.base] | |
| 776 .has_extended_attribute("NoInterfaceObject"); | |
| 777 if (has_interface_object) { | |
| 778 assert_own_property(self, this.base, | |
| 779 'should inherit from ' + this.base + | |
| 780 ', but self has no such property'); | |
| 781 assert_equals(prototype, self[this.base], | |
| 782 'prototype of ' + this.name + ' is not ' + | |
| 783 this.base); | |
| 784 } | |
| 785 } else { | |
| 786 // "If the interface doesn't inherit from any other interface, the | |
| 787 // value of [[Prototype]] is %FunctionPrototype% ([ECMA-262], | |
| 788 // section 6.1.7.4)." | |
| 789 assert_equals(prototype, Function.prototype, | |
| 790 "prototype of self's property " + format_value(this.na
me) + " is not Function.prototype"); | |
| 791 } | |
| 792 | |
| 793 if (!this.has_extended_attribute("Constructor")) { | |
| 794 // "The internal [[Call]] method of the interface object behaves as | |
| 795 // follows . . . | |
| 796 // | |
| 797 // "If I was not declared with a [Constructor] extended attribute, | |
| 798 // then throw a TypeError." | |
| 799 assert_throws(new TypeError(), function() { | |
| 800 self[this.name](); | |
| 801 }.bind(this), "interface object didn't throw TypeError when called a
s a function"); | |
| 802 assert_throws(new TypeError(), function() { | |
| 803 new self[this.name](); | |
| 804 }.bind(this), "interface object didn't throw TypeError when called a
s a constructor"); | |
| 805 } | |
| 806 }.bind(this), this.name + " interface: existence and properties of interface
object"); | |
| 807 | |
| 808 if (!this.is_callback()) { | |
| 809 test(function() { | |
| 810 // This function tests WebIDL as of 2014-10-25. | |
| 811 // https://heycam.github.io/webidl/#es-interface-call | |
| 812 | |
| 813 assert_own_property(self, this.name, | |
| 814 "self does not have own property " + format_valu
e(this.name)); | |
| 815 | |
| 816 // "Interface objects for non-callback interfaces MUST have a | |
| 817 // property named “length” with attributes { [[Writable]]: false, | |
| 818 // [[Enumerable]]: false, [[Configurable]]: true } whose value is | |
| 819 // a Number." | |
| 820 assert_own_property(self[this.name], "length"); | |
| 821 var desc = Object.getOwnPropertyDescriptor(self[this.name], "length"
); | |
| 822 assert_false("get" in desc, this.name + ".length has getter"); | |
| 823 assert_false("set" in desc, this.name + ".length has setter"); | |
| 824 assert_false(desc.writable, this.name + ".length is writable"); | |
| 825 assert_false(desc.enumerable, this.name + ".length is enumerable"); | |
| 826 assert_true(desc.configurable, this.name + ".length is not configura
ble"); | |
| 827 | |
| 828 var constructors = this.extAttrs | |
| 829 .filter(function(attr) { return attr.name == "Constructor"; }); | |
| 830 var expected_length = minOverloadLength(constructors); | |
| 831 assert_equals(self[this.name].length, expected_length, "wrong value
for " + this.name + ".length"); | |
| 832 }.bind(this), this.name + " interface object length"); | |
| 833 } | |
| 834 | |
| 835 if (!this.is_callback() || this.has_constants()) { | |
| 836 test(function() { | |
| 837 // This function tests WebIDL as of 2015-11-17. | |
| 838 // https://heycam.github.io/webidl/#interface-object | |
| 839 | |
| 840 assert_own_property(self, this.name, | |
| 841 "self does not have own property " + format_valu
e(this.name)); | |
| 842 | |
| 843 // "All interface objects must have a property named “name” with | |
| 844 // attributes { [[Writable]]: false, [[Enumerable]]: false, | |
| 845 // [[Configurable]]: true } whose value is the identifier of the | |
| 846 // corresponding interface." | |
| 847 | |
| 848 assert_own_property(self[this.name], "name"); | |
| 849 var desc = Object.getOwnPropertyDescriptor(self[this.name], "name"); | |
| 850 assert_false("get" in desc, this.name + ".name has getter"); | |
| 851 assert_false("set" in desc, this.name + ".name has setter"); | |
| 852 assert_false(desc.writable, this.name + ".name is writable"); | |
| 853 assert_false(desc.enumerable, this.name + ".name is enumerable"); | |
| 854 assert_true(desc.configurable, this.name + ".name is not configurabl
e"); | |
| 855 assert_equals(self[this.name].name, this.name, "wrong value for " +
this.name + ".name"); | |
| 856 }.bind(this), this.name + " interface object name"); | |
| 857 } | |
| 858 | |
| 859 // TODO: Test named constructors if I find any interfaces that have them. | |
| 860 | |
| 861 test(function() | |
| 862 { | |
| 863 // This function tests WebIDL as of 2015-01-21. | |
| 864 // https://heycam.github.io/webidl/#interface-object | |
| 865 | |
| 866 if (this.is_callback() && !this.has_constants()) { | |
| 867 return; | |
| 868 } | |
| 869 | |
| 870 assert_own_property(self, this.name, | |
| 871 "self does not have own property " + format_value(th
is.name)); | |
| 872 | |
| 873 if (this.is_callback()) { | |
| 874 assert_false("prototype" in self[this.name], | |
| 875 this.name + ' should not have a "prototype" property'); | |
| 876 return; | |
| 877 } | |
| 878 | |
| 879 // "An interface object for a non-callback interface must have a | |
| 880 // property named “prototype” with attributes { [[Writable]]: false, | |
| 881 // [[Enumerable]]: false, [[Configurable]]: false } whose value is an | |
| 882 // object called the interface prototype object. This object has | |
| 883 // properties that correspond to the regular attributes and regular | |
| 884 // operations defined on the interface, and is described in more detail | |
| 885 // in section 4.5.4 below." | |
| 886 assert_own_property(self[this.name], "prototype", | |
| 887 'interface "' + this.name + '" does not have own pro
perty "prototype"'); | |
| 888 var desc = Object.getOwnPropertyDescriptor(self[this.name], "prototype")
; | |
| 889 assert_false("get" in desc, this.name + ".prototype has getter"); | |
| 890 assert_false("set" in desc, this.name + ".prototype has setter"); | |
| 891 assert_false(desc.writable, this.name + ".prototype is writable"); | |
| 892 assert_false(desc.enumerable, this.name + ".prototype is enumerable"); | |
| 893 assert_false(desc.configurable, this.name + ".prototype is configurable"
); | |
| 894 | |
| 895 // Next, test that the [[Prototype]] of the interface prototype object | |
| 896 // is correct. (This is made somewhat difficult by the existence of | |
| 897 // [NoInterfaceObject].) | |
| 898 // TODO: Aryeh thinks there's at least other place in this file where | |
| 899 // we try to figure out if an interface prototype object is | |
| 900 // correct. Consolidate that code. | |
| 901 | |
| 902 // "The interface prototype object for a given interface A must have an | |
| 903 // internal [[Prototype]] property whose value is returned from the | |
| 904 // following steps: | |
| 905 // "If A is declared with the [Global] or [PrimaryGlobal] extended | |
| 906 // attribute, and A supports named properties, then return the named | |
| 907 // properties object for A, as defined in section 4.5.5 below. | |
| 908 // "Otherwise, if A is declared to inherit from another interface, then | |
| 909 // return the interface prototype object for the inherited interface. | |
| 910 // "Otherwise, if A is declared with the [ArrayClass] extended | |
| 911 // attribute, then return %ArrayPrototype% ([ECMA-262], section | |
| 912 // 6.1.7.4). | |
| 913 // "Otherwise, return %ObjectPrototype% ([ECMA-262], section 6.1.7.4). | |
| 914 // ([ECMA-262], section 15.2.4). | |
| 915 if (this.name === "Window") { | |
| 916 assert_class_string(Object.getPrototypeOf(self[this.name].prototype)
, | |
| 917 'WindowProperties', | |
| 918 'Class name for prototype of Window' + | |
| 919 '.prototype is not "WindowProperties"'); | |
| 920 } else { | |
| 921 var inherit_interface, inherit_interface_has_interface_object; | |
| 922 if (this.base) { | |
| 923 inherit_interface = this.base; | |
| 924 inherit_interface_has_interface_object = | |
| 925 !this.array | |
| 926 .members[inherit_interface] | |
| 927 .has_extended_attribute("NoInterfaceObject"); | |
| 928 } else if (this.has_extended_attribute('ArrayClass')) { | |
| 929 inherit_interface = 'Array'; | |
| 930 inherit_interface_has_interface_object = true; | |
| 931 } else { | |
| 932 inherit_interface = 'Object'; | |
| 933 inherit_interface_has_interface_object = true; | |
| 934 } | |
| 935 if (inherit_interface_has_interface_object) { | |
| 936 assert_own_property(self, inherit_interface, | |
| 937 'should inherit from ' + inherit_interface +
', but self has no such property'); | |
| 938 assert_own_property(self[inherit_interface], 'prototype', | |
| 939 'should inherit from ' + inherit_interface +
', but that object has no "prototype" property'); | |
| 940 assert_equals(Object.getPrototypeOf(self[this.name].prototype), | |
| 941 self[inherit_interface].prototype, | |
| 942 'prototype of ' + this.name + '.prototype is not '
+ inherit_interface + '.prototype'); | |
| 943 } else { | |
| 944 // We can't test that we get the correct object, because this is
the | |
| 945 // only way to get our hands on it. We only test that its class | |
| 946 // string, at least, is correct. | |
| 947 assert_class_string(Object.getPrototypeOf(self[this.name].protot
ype), | |
| 948 inherit_interface + 'Prototype', | |
| 949 'Class name for prototype of ' + this.name + | |
| 950 '.prototype is not "' + inherit_interface +
'Prototype"'); | |
| 951 } | |
| 952 } | |
| 953 | |
| 954 // "The class string of an interface prototype object is the | |
| 955 // concatenation of the interface’s identifier and the string | |
| 956 // “Prototype”." | |
| 957 assert_class_string(self[this.name].prototype, this.name + "Prototype", | |
| 958 "class string of " + this.name + ".prototype"); | |
| 959 // String() should end up calling {}.toString if nothing defines a | |
| 960 // stringifier. | |
| 961 if (!this.has_stringifier()) { | |
| 962 assert_equals(String(self[this.name].prototype), "[object " + this.n
ame + "Prototype]", | |
| 963 "String(" + this.name + ".prototype)"); | |
| 964 } | |
| 965 }.bind(this), this.name + " interface: existence and properties of interface
prototype object"); | |
| 966 | |
| 967 test(function() | |
| 968 { | |
| 969 if (this.is_callback() && !this.has_constants()) { | |
| 970 return; | |
| 971 } | |
| 972 | |
| 973 assert_own_property(self, this.name, | |
| 974 "self does not have own property " + format_value(th
is.name)); | |
| 975 | |
| 976 if (this.is_callback()) { | |
| 977 assert_false("prototype" in self[this.name], | |
| 978 this.name + ' should not have a "prototype" property'); | |
| 979 return; | |
| 980 } | |
| 981 | |
| 982 assert_own_property(self[this.name], "prototype", | |
| 983 'interface "' + this.name + '" does not have own pro
perty "prototype"'); | |
| 984 | |
| 985 // "If the [NoInterfaceObject] extended attribute was not specified on | |
| 986 // the interface, then the interface prototype object must also have a | |
| 987 // property named “constructor” with attributes { [[Writable]]: true, | |
| 988 // [[Enumerable]]: false, [[Configurable]]: true } whose value is a | |
| 989 // reference to the interface object for the interface." | |
| 990 assert_own_property(self[this.name].prototype, "constructor", | |
| 991 this.name + '.prototype does not have own property "
constructor"'); | |
| 992 var desc = Object.getOwnPropertyDescriptor(self[this.name].prototype, "c
onstructor"); | |
| 993 assert_false("get" in desc, this.name + ".prototype.constructor has gett
er"); | |
| 994 assert_false("set" in desc, this.name + ".prototype.constructor has sett
er"); | |
| 995 assert_true(desc.writable, this.name + ".prototype.constructor is not wr
itable"); | |
| 996 assert_false(desc.enumerable, this.name + ".prototype.constructor is enu
merable"); | |
| 997 assert_true(desc.configurable, this.name + ".prototype.constructor in no
t configurable"); | |
| 998 assert_equals(self[this.name].prototype.constructor, self[this.name], | |
| 999 this.name + '.prototype.constructor is not the same object
as ' + this.name); | |
| 1000 }.bind(this), this.name + ' interface: existence and properties of interface
prototype object\'s "constructor" property'); | |
| 1001 }; | |
| 1002 | |
| 1003 //@} | |
| 1004 IdlInterface.prototype.test_member_const = function(member) | |
| 1005 //@{ | |
| 1006 { | |
| 1007 if (!this.has_constants()) { | |
| 1008 throw "Internal error: test_member_const called without any constants"; | |
| 1009 } | |
| 1010 | |
| 1011 test(function() | |
| 1012 { | |
| 1013 assert_own_property(self, this.name, | |
| 1014 "self does not have own property " + format_value(th
is.name)); | |
| 1015 | |
| 1016 // "For each constant defined on an interface A, there must be | |
| 1017 // a corresponding property on the interface object, if it | |
| 1018 // exists." | |
| 1019 assert_own_property(self[this.name], member.name); | |
| 1020 // "The value of the property is that which is obtained by | |
| 1021 // converting the constant’s IDL value to an ECMAScript | |
| 1022 // value." | |
| 1023 assert_equals(self[this.name][member.name], constValue(member.value), | |
| 1024 "property has wrong value"); | |
| 1025 // "The property has attributes { [[Writable]]: false, | |
| 1026 // [[Enumerable]]: true, [[Configurable]]: false }." | |
| 1027 var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name)
; | |
| 1028 assert_false("get" in desc, "property has getter"); | |
| 1029 assert_false("set" in desc, "property has setter"); | |
| 1030 assert_false(desc.writable, "property is writable"); | |
| 1031 assert_true(desc.enumerable, "property is not enumerable"); | |
| 1032 assert_false(desc.configurable, "property is configurable"); | |
| 1033 }.bind(this), this.name + " interface: constant " + member.name + " on inter
face object"); | |
| 1034 | |
| 1035 // "In addition, a property with the same characteristics must | |
| 1036 // exist on the interface prototype object." | |
| 1037 test(function() | |
| 1038 { | |
| 1039 assert_own_property(self, this.name, | |
| 1040 "self does not have own property " + format_value(th
is.name)); | |
| 1041 | |
| 1042 if (this.is_callback()) { | |
| 1043 assert_false("prototype" in self[this.name], | |
| 1044 this.name + ' should not have a "prototype" property'); | |
| 1045 return; | |
| 1046 } | |
| 1047 | |
| 1048 assert_own_property(self[this.name], "prototype", | |
| 1049 'interface "' + this.name + '" does not have own pro
perty "prototype"'); | |
| 1050 | |
| 1051 assert_own_property(self[this.name].prototype, member.name); | |
| 1052 assert_equals(self[this.name].prototype[member.name], constValue(member.
value), | |
| 1053 "property has wrong value"); | |
| 1054 var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name)
; | |
| 1055 assert_false("get" in desc, "property has getter"); | |
| 1056 assert_false("set" in desc, "property has setter"); | |
| 1057 assert_false(desc.writable, "property is writable"); | |
| 1058 assert_true(desc.enumerable, "property is not enumerable"); | |
| 1059 assert_false(desc.configurable, "property is configurable"); | |
| 1060 }.bind(this), this.name + " interface: constant " + member.name + " on inter
face prototype object"); | |
| 1061 }; | |
| 1062 | |
| 1063 | |
| 1064 //@} | |
| 1065 IdlInterface.prototype.test_member_attribute = function(member) | |
| 1066 //@{ | |
| 1067 { | |
| 1068 test(function() | |
| 1069 { | |
| 1070 if (this.is_callback() && !this.has_constants()) { | |
| 1071 return; | |
| 1072 } | |
| 1073 | |
| 1074 assert_own_property(self, this.name, | |
| 1075 "self does not have own property " + format_value(th
is.name)); | |
| 1076 assert_own_property(self[this.name], "prototype", | |
| 1077 'interface "' + this.name + '" does not have own pro
perty "prototype"'); | |
| 1078 | |
| 1079 if (member["static"]) { | |
| 1080 assert_own_property(self[this.name], member.name, | |
| 1081 "The interface object must have a property " + | |
| 1082 format_value(member.name)); | |
| 1083 } else if (this.is_global()) { | |
| 1084 assert_own_property(self, member.name, | |
| 1085 "The global object must have a property " + | |
| 1086 format_value(member.name)); | |
| 1087 assert_false(member.name in self[this.name].prototype, | |
| 1088 "The prototype object must not have a property " + | |
| 1089 format_value(member.name)); | |
| 1090 | |
| 1091 var getter = Object.getOwnPropertyDescriptor(self, member.name).get; | |
| 1092 assert_equals(typeof(getter), "function", | |
| 1093 format_value(member.name) + " must have a getter"); | |
| 1094 | |
| 1095 // Try/catch around the get here, since it can legitimately throw. | |
| 1096 // If it does, we obviously can't check for equality with direct | |
| 1097 // invocation of the getter. | |
| 1098 var gotValue; | |
| 1099 var propVal; | |
| 1100 try { | |
| 1101 propVal = self[member.name]; | |
| 1102 gotValue = true; | |
| 1103 } catch (e) { | |
| 1104 gotValue = false; | |
| 1105 } | |
| 1106 if (gotValue) { | |
| 1107 assert_equals(propVal, getter.call(undefined), | |
| 1108 "Gets on a global should not require an explicit t
his"); | |
| 1109 } | |
| 1110 | |
| 1111 this.do_interface_attribute_asserts(self, member); | |
| 1112 } else { | |
| 1113 assert_true(member.name in self[this.name].prototype, | |
| 1114 "The prototype object must have a property " + | |
| 1115 format_value(member.name)); | |
| 1116 | |
| 1117 if (!member.has_extended_attribute("LenientThis")) { | |
| 1118 assert_throws(new TypeError(), function() { | |
| 1119 self[this.name].prototype[member.name]; | |
| 1120 }.bind(this), "getting property on prototype object must throw T
ypeError"); | |
| 1121 } else { | |
| 1122 assert_equals(self[this.name].prototype[member.name], undefined, | |
| 1123 "getting property on prototype object must return
undefined"); | |
| 1124 } | |
| 1125 this.do_interface_attribute_asserts(self[this.name].prototype, membe
r); | |
| 1126 } | |
| 1127 }.bind(this), this.name + " interface: attribute " + member.name); | |
| 1128 }; | |
| 1129 | |
| 1130 //@} | |
| 1131 IdlInterface.prototype.test_member_operation = function(member) | |
| 1132 //@{ | |
| 1133 { | |
| 1134 var a_test = async_test(this.name + " interface: operation " + member.name + | |
| 1135 "(" + member.arguments.map( | |
| 1136 function(m) {return m.idlType.idlType; } ) | |
| 1137 +")"); | |
| 1138 a_test.step(function() | |
| 1139 { | |
| 1140 // This function tests WebIDL as of 2015-12-29. | |
| 1141 // https://heycam.github.io/webidl/#es-operations | |
| 1142 | |
| 1143 if (this.is_callback() && !this.has_constants()) { | |
| 1144 a_test.done(); | |
| 1145 return; | |
| 1146 } | |
| 1147 | |
| 1148 assert_own_property(self, this.name, | |
| 1149 "self does not have own property " + format_value(th
is.name)); | |
| 1150 | |
| 1151 if (this.is_callback()) { | |
| 1152 assert_false("prototype" in self[this.name], | |
| 1153 this.name + ' should not have a "prototype" property'); | |
| 1154 a_test.done(); | |
| 1155 return; | |
| 1156 } | |
| 1157 | |
| 1158 assert_own_property(self[this.name], "prototype", | |
| 1159 'interface "' + this.name + '" does not have own pro
perty "prototype"'); | |
| 1160 | |
| 1161 // "For each unique identifier of an exposed operation defined on the | |
| 1162 // interface, there must exist a corresponding property, unless the | |
| 1163 // effective overload set for that identifier and operation and with an | |
| 1164 // argument count of 0 has no entries." | |
| 1165 | |
| 1166 // TODO: Consider [Exposed]. | |
| 1167 | |
| 1168 // "The location of the property is determined as follows:" | |
| 1169 var memberHolderObject; | |
| 1170 // "* If the operation is static, then the property exists on the | |
| 1171 // interface object." | |
| 1172 if (member["static"]) { | |
| 1173 assert_own_property(self[this.name], member.name, | |
| 1174 "interface object missing static operation"); | |
| 1175 memberHolderObject = self[this.name]; | |
| 1176 // "* Otherwise, [...] if the interface was declared with the [Global] | |
| 1177 // or [PrimaryGlobal] extended attribute, then the property exists | |
| 1178 // on every object that implements the interface." | |
| 1179 } else if (this.is_global()) { | |
| 1180 assert_own_property(self, member.name, | |
| 1181 "global object missing non-static operation"); | |
| 1182 memberHolderObject = self; | |
| 1183 // "* Otherwise, the property exists solely on the interface’s | |
| 1184 // interface prototype object." | |
| 1185 } else { | |
| 1186 assert_own_property(self[this.name].prototype, member.name, | |
| 1187 "interface prototype object missing non-static operation"); | |
| 1188 memberHolderObject = self[this.name].prototype; | |
| 1189 } | |
| 1190 this.do_member_operation_asserts(memberHolderObject, member, a_test); | |
| 1191 }.bind(this)); | |
| 1192 }; | |
| 1193 | |
| 1194 //@} | |
| 1195 IdlInterface.prototype.do_member_operation_asserts = function(memberHolderObject
, member, a_test) | |
| 1196 //@{ | |
| 1197 { | |
| 1198 var done = a_test.done.bind(a_test); | |
| 1199 var operationUnforgeable = member.isUnforgeable; | |
| 1200 var desc = Object.getOwnPropertyDescriptor(memberHolderObject, member.name); | |
| 1201 // "The property has attributes { [[Writable]]: B, | |
| 1202 // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the | |
| 1203 // operation is unforgeable on the interface, and true otherwise". | |
| 1204 assert_false("get" in desc, "property has getter"); | |
| 1205 assert_false("set" in desc, "property has setter"); | |
| 1206 assert_equals(desc.writable, !operationUnforgeable, | |
| 1207 "property should be writable if and only if not unforgeable"); | |
| 1208 assert_true(desc.enumerable, "property is not enumerable"); | |
| 1209 assert_equals(desc.configurable, !operationUnforgeable, | |
| 1210 "property should be configurable if and only if not unforgeabl
e"); | |
| 1211 // "The value of the property is a Function object whose | |
| 1212 // behavior is as follows . . ." | |
| 1213 assert_equals(typeof memberHolderObject[member.name], "function", | |
| 1214 "property must be a function"); | |
| 1215 // "The value of the Function object’s “length” property is | |
| 1216 // a Number determined as follows: | |
| 1217 // ". . . | |
| 1218 // "Return the length of the shortest argument list of the | |
| 1219 // entries in S." | |
| 1220 assert_equals(memberHolderObject[member.name].length, | |
| 1221 minOverloadLength(this.members.filter(function(m) { | |
| 1222 return m.type == "operation" && m.name == member.name; | |
| 1223 })), | |
| 1224 "property has wrong .length"); | |
| 1225 | |
| 1226 // Make some suitable arguments | |
| 1227 var args = member.arguments.map(function(arg) { | |
| 1228 return create_suitable_object(arg.idlType); | |
| 1229 }); | |
| 1230 | |
| 1231 // "Let O be a value determined as follows: | |
| 1232 // ". . . | |
| 1233 // "Otherwise, throw a TypeError." | |
| 1234 // This should be hit if the operation is not static, there is | |
| 1235 // no [ImplicitThis] attribute, and the this value is null. | |
| 1236 // | |
| 1237 // TODO: We currently ignore the [ImplicitThis] case. Except we manually | |
| 1238 // check for globals, since otherwise we'll invoke window.close(). And we | |
| 1239 // have to skip this test for anything that on the proto chain of "self", | |
| 1240 // since that does in fact have implicit-this behavior. | |
| 1241 if (!member["static"]) { | |
| 1242 var cb; | |
| 1243 if (!this.is_global() && | |
| 1244 memberHolderObject[member.name] != self[member.name]) | |
| 1245 { | |
| 1246 cb = awaitNCallbacks(2, done); | |
| 1247 throwOrReject(a_test, member, memberHolderObject[member.name], null,
args, | |
| 1248 "calling operation with this = null didn't throw TypeE
rror", cb); | |
| 1249 } else { | |
| 1250 cb = awaitNCallbacks(1, done); | |
| 1251 } | |
| 1252 | |
| 1253 // ". . . If O is not null and is also not a platform object | |
| 1254 // that implements interface I, throw a TypeError." | |
| 1255 // | |
| 1256 // TODO: Test a platform object that implements some other | |
| 1257 // interface. (Have to be sure to get inheritance right.) | |
| 1258 throwOrReject(a_test, member, memberHolderObject[member.name], {}, args, | |
| 1259 "calling operation with this = {} didn't throw TypeError",
cb); | |
| 1260 } else { | |
| 1261 done(); | |
| 1262 } | |
| 1263 } | |
| 1264 | |
| 1265 //@} | |
| 1266 IdlInterface.prototype.test_member_stringifier = function(member) | |
| 1267 //@{ | |
| 1268 { | |
| 1269 test(function() | |
| 1270 { | |
| 1271 if (this.is_callback() && !this.has_constants()) { | |
| 1272 return; | |
| 1273 } | |
| 1274 | |
| 1275 assert_own_property(self, this.name, | |
| 1276 "self does not have own property " + format_value(th
is.name)); | |
| 1277 | |
| 1278 if (this.is_callback()) { | |
| 1279 assert_false("prototype" in self[this.name], | |
| 1280 this.name + ' should not have a "prototype" property'); | |
| 1281 return; | |
| 1282 } | |
| 1283 | |
| 1284 assert_own_property(self[this.name], "prototype", | |
| 1285 'interface "' + this.name + '" does not have own pro
perty "prototype"'); | |
| 1286 | |
| 1287 // ". . . the property exists on the interface prototype object." | |
| 1288 var interfacePrototypeObject = self[this.name].prototype; | |
| 1289 assert_own_property(self[this.name].prototype, "toString", | |
| 1290 "interface prototype object missing non-static operation"); | |
| 1291 | |
| 1292 var stringifierUnforgeable = member.isUnforgeable; | |
| 1293 var desc = Object.getOwnPropertyDescriptor(interfacePrototypeObject, "to
String"); | |
| 1294 // "The property has attributes { [[Writable]]: B, | |
| 1295 // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the | |
| 1296 // stringifier is unforgeable on the interface, and true otherwise." | |
| 1297 assert_false("get" in desc, "property has getter"); | |
| 1298 assert_false("set" in desc, "property has setter"); | |
| 1299 assert_equals(desc.writable, !stringifierUnforgeable, | |
| 1300 "property should be writable if and only if not unforgeabl
e"); | |
| 1301 assert_true(desc.enumerable, "property is not enumerable"); | |
| 1302 assert_equals(desc.configurable, !stringifierUnforgeable, | |
| 1303 "property should be configurable if and only if not unforg
eable"); | |
| 1304 // "The value of the property is a Function object, which behaves as | |
| 1305 // follows . . ." | |
| 1306 assert_equals(typeof interfacePrototypeObject.toString, "function", | |
| 1307 "property must be a function"); | |
| 1308 // "The value of the Function object’s “length” property is the Number | |
| 1309 // value 0." | |
| 1310 assert_equals(interfacePrototypeObject.toString.length, 0, | |
| 1311 "property has wrong .length"); | |
| 1312 | |
| 1313 // "Let O be the result of calling ToObject on the this value." | |
| 1314 assert_throws(new TypeError(), function() { | |
| 1315 self[this.name].prototype.toString.apply(null, []); | |
| 1316 }, "calling stringifier with this = null didn't throw TypeError"); | |
| 1317 | |
| 1318 // "If O is not an object that implements the interface on which the | |
| 1319 // stringifier was declared, then throw a TypeError." | |
| 1320 // | |
| 1321 // TODO: Test a platform object that implements some other | |
| 1322 // interface. (Have to be sure to get inheritance right.) | |
| 1323 assert_throws(new TypeError(), function() { | |
| 1324 self[this.name].prototype.toString.apply({}, []); | |
| 1325 }, "calling stringifier with this = {} didn't throw TypeError"); | |
| 1326 }.bind(this), this.name + " interface: stringifier"); | |
| 1327 }; | |
| 1328 | |
| 1329 //@} | |
| 1330 IdlInterface.prototype.test_members = function() | |
| 1331 //@{ | |
| 1332 { | |
| 1333 for (var i = 0; i < this.members.length; i++) | |
| 1334 { | |
| 1335 var member = this.members[i]; | |
| 1336 if (member.untested) { | |
| 1337 continue; | |
| 1338 } | |
| 1339 | |
| 1340 switch (member.type) { | |
| 1341 case "const": | |
| 1342 this.test_member_const(member); | |
| 1343 break; | |
| 1344 | |
| 1345 case "attribute": | |
| 1346 // For unforgeable attributes, we do the checks in | |
| 1347 // test_interface_of instead. | |
| 1348 if (!member.isUnforgeable) | |
| 1349 { | |
| 1350 this.test_member_attribute(member); | |
| 1351 } | |
| 1352 break; | |
| 1353 | |
| 1354 case "operation": | |
| 1355 // TODO: Need to correctly handle multiple operations with the same | |
| 1356 // identifier. | |
| 1357 // For unforgeable operations, we do the checks in | |
| 1358 // test_interface_of instead. | |
| 1359 if (member.name) { | |
| 1360 if (!member.isUnforgeable) | |
| 1361 { | |
| 1362 this.test_member_operation(member); | |
| 1363 } | |
| 1364 } else if (member.stringifier) { | |
| 1365 this.test_member_stringifier(member); | |
| 1366 } | |
| 1367 break; | |
| 1368 | |
| 1369 default: | |
| 1370 // TODO: check more member types. | |
| 1371 break; | |
| 1372 } | |
| 1373 } | |
| 1374 }; | |
| 1375 | |
| 1376 //@} | |
| 1377 IdlInterface.prototype.test_object = function(desc) | |
| 1378 //@{ | |
| 1379 { | |
| 1380 var obj, exception = null; | |
| 1381 try | |
| 1382 { | |
| 1383 obj = eval(desc); | |
| 1384 } | |
| 1385 catch(e) | |
| 1386 { | |
| 1387 exception = e; | |
| 1388 } | |
| 1389 | |
| 1390 var expected_typeof = | |
| 1391 this.members.some(function(member) { return member.legacycaller; }) | |
| 1392 ? "function" | |
| 1393 : "object"; | |
| 1394 | |
| 1395 this.test_primary_interface_of(desc, obj, exception, expected_typeof); | |
| 1396 var current_interface = this; | |
| 1397 while (current_interface) | |
| 1398 { | |
| 1399 if (!(current_interface.name in this.array.members)) | |
| 1400 { | |
| 1401 throw "Interface " + current_interface.name + " not found (inherited
by " + this.name + ")"; | |
| 1402 } | |
| 1403 if (current_interface.prevent_multiple_testing && current_interface.alre
ady_tested) | |
| 1404 { | |
| 1405 return; | |
| 1406 } | |
| 1407 current_interface.test_interface_of(desc, obj, exception, expected_typeo
f); | |
| 1408 current_interface = this.array.members[current_interface.base]; | |
| 1409 } | |
| 1410 }; | |
| 1411 | |
| 1412 //@} | |
| 1413 IdlInterface.prototype.test_primary_interface_of = function(desc, obj, exception
, expected_typeof) | |
| 1414 //@{ | |
| 1415 { | |
| 1416 // We can't easily test that its prototype is correct if there's no | |
| 1417 // interface object, or the object is from a different global environment | |
| 1418 // (not instanceof Object). TODO: test in this case that its prototype at | |
| 1419 // least looks correct, even if we can't test that it's actually correct. | |
| 1420 if (!this.has_extended_attribute("NoInterfaceObject") | |
| 1421 && (typeof obj != expected_typeof || obj instanceof Object)) | |
| 1422 { | |
| 1423 test(function() | |
| 1424 { | |
| 1425 assert_equals(exception, null, "Unexpected exception when evaluating
object"); | |
| 1426 assert_equals(typeof obj, expected_typeof, "wrong typeof object"); | |
| 1427 assert_own_property(self, this.name, | |
| 1428 "self does not have own property " + format_valu
e(this.name)); | |
| 1429 assert_own_property(self[this.name], "prototype", | |
| 1430 'interface "' + this.name + '" does not have own
property "prototype"'); | |
| 1431 | |
| 1432 // "The value of the internal [[Prototype]] property of the | |
| 1433 // platform object is the interface prototype object of the primary | |
| 1434 // interface from the platform object’s associated global | |
| 1435 // environment." | |
| 1436 assert_equals(Object.getPrototypeOf(obj), | |
| 1437 self[this.name].prototype, | |
| 1438 desc + "'s prototype is not " + this.name + ".prototyp
e"); | |
| 1439 }.bind(this), this.name + " must be primary interface of " + desc); | |
| 1440 } | |
| 1441 | |
| 1442 // "The class string of a platform object that implements one or more | |
| 1443 // interfaces must be the identifier of the primary interface of the | |
| 1444 // platform object." | |
| 1445 test(function() | |
| 1446 { | |
| 1447 assert_equals(exception, null, "Unexpected exception when evaluating obj
ect"); | |
| 1448 assert_equals(typeof obj, expected_typeof, "wrong typeof object"); | |
| 1449 assert_class_string(obj, this.name, "class string of " + desc); | |
| 1450 if (!this.has_stringifier()) | |
| 1451 { | |
| 1452 assert_equals(String(obj), "[object " + this.name + "]", "String(" +
desc + ")"); | |
| 1453 } | |
| 1454 }.bind(this), "Stringification of " + desc); | |
| 1455 }; | |
| 1456 | |
| 1457 //@} | |
| 1458 IdlInterface.prototype.test_interface_of = function(desc, obj, exception, expect
ed_typeof) | |
| 1459 //@{ | |
| 1460 { | |
| 1461 // TODO: Indexed and named properties, more checks on interface members | |
| 1462 this.already_tested = true; | |
| 1463 | |
| 1464 for (var i = 0; i < this.members.length; i++) | |
| 1465 { | |
| 1466 var member = this.members[i]; | |
| 1467 if (member.type == "attribute" && member.isUnforgeable) | |
| 1468 { | |
| 1469 test(function() | |
| 1470 { | |
| 1471 assert_equals(exception, null, "Unexpected exception when evalua
ting object"); | |
| 1472 assert_equals(typeof obj, expected_typeof, "wrong typeof object"
); | |
| 1473 this.do_interface_attribute_asserts(obj, member); | |
| 1474 }.bind(this), this.name + " interface: " + desc + ' must have own pr
operty "' + member.name + '"'); | |
| 1475 } | |
| 1476 else if (member.type == "operation" && | |
| 1477 member.name && | |
| 1478 member.isUnforgeable) | |
| 1479 { | |
| 1480 var a_test = async_test(this.name + " interface: " + desc + ' must h
ave own property "' + member.name + '"'); | |
| 1481 a_test.step(function() | |
| 1482 { | |
| 1483 assert_equals(exception, null, "Unexpected exception when evalua
ting object"); | |
| 1484 assert_equals(typeof obj, expected_typeof, "wrong typeof object"
); | |
| 1485 assert_own_property(obj, member.name, | |
| 1486 "Doesn't have the unforgeable operation prop
erty"); | |
| 1487 this.do_member_operation_asserts(obj, member, a_test); | |
| 1488 }.bind(this)); | |
| 1489 } | |
| 1490 else if ((member.type == "const" | |
| 1491 || member.type == "attribute" | |
| 1492 || member.type == "operation") | |
| 1493 && member.name) | |
| 1494 { | |
| 1495 test(function() | |
| 1496 { | |
| 1497 assert_equals(exception, null, "Unexpected exception when evalua
ting object"); | |
| 1498 assert_equals(typeof obj, expected_typeof, "wrong typeof object"
); | |
| 1499 if (!member["static"]) { | |
| 1500 if (!this.is_global()) { | |
| 1501 assert_inherits(obj, member.name); | |
| 1502 } else { | |
| 1503 assert_own_property(obj, member.name); | |
| 1504 } | |
| 1505 | |
| 1506 if (member.type == "const") | |
| 1507 { | |
| 1508 assert_equals(obj[member.name], constValue(member.value)
); | |
| 1509 } | |
| 1510 if (member.type == "attribute") | |
| 1511 { | |
| 1512 // Attributes are accessor properties, so they might | |
| 1513 // legitimately throw an exception rather than returning | |
| 1514 // anything. | |
| 1515 var property, thrown = false; | |
| 1516 try | |
| 1517 { | |
| 1518 property = obj[member.name]; | |
| 1519 } | |
| 1520 catch (e) | |
| 1521 { | |
| 1522 thrown = true; | |
| 1523 } | |
| 1524 if (!thrown) | |
| 1525 { | |
| 1526 this.array.assert_type_is(property, member.idlType); | |
| 1527 } | |
| 1528 } | |
| 1529 if (member.type == "operation") | |
| 1530 { | |
| 1531 assert_equals(typeof obj[member.name], "function"); | |
| 1532 } | |
| 1533 } | |
| 1534 }.bind(this), this.name + " interface: " + desc + ' must inherit pro
perty "' + member.name + '" with the proper type (' + i + ')'); | |
| 1535 } | |
| 1536 // TODO: This is wrong if there are multiple operations with the same | |
| 1537 // identifier. | |
| 1538 // TODO: Test passing arguments of the wrong type. | |
| 1539 if (member.type == "operation" && member.name && member.arguments.length
) | |
| 1540 { | |
| 1541 var a_test = async_test( this.name + " interface: calling " + member
.name + | |
| 1542 "(" + member.arguments.map(function(m) { return m.idlType.idlType; }
) + | |
| 1543 ") on " + desc + " with too few arguments must throw TypeError"); | |
| 1544 a_test.step(function() | |
| 1545 { | |
| 1546 assert_equals(exception, null, "Unexpected exception when evalua
ting object"); | |
| 1547 assert_equals(typeof obj, expected_typeof, "wrong typeof object"
); | |
| 1548 if (!member["static"]) { | |
| 1549 if (!this.is_global() && !member.isUnforgeable) { | |
| 1550 assert_inherits(obj, member.name); | |
| 1551 } else { | |
| 1552 assert_own_property(obj, member.name); | |
| 1553 } | |
| 1554 } | |
| 1555 else | |
| 1556 { | |
| 1557 assert_false(member.name in obj); | |
| 1558 } | |
| 1559 | |
| 1560 var minLength = minOverloadLength(this.members.filter(function(m
) { | |
| 1561 return m.type == "operation" && m.name == member.name; | |
| 1562 })); | |
| 1563 var args = []; | |
| 1564 var cb = awaitNCallbacks(minLength, a_test.done.bind(a_test)); | |
| 1565 for (var i = 0; i < minLength; i++) { | |
| 1566 throwOrReject(a_test, member, obj[member.name], obj, args,
"Called with " + i + " arguments", cb); | |
| 1567 | |
| 1568 args.push(create_suitable_object(member.arguments[i].idlType
)); | |
| 1569 } | |
| 1570 if (minLength === 0) { | |
| 1571 cb(); | |
| 1572 } | |
| 1573 }.bind(this)); | |
| 1574 } | |
| 1575 } | |
| 1576 }; | |
| 1577 | |
| 1578 //@} | |
| 1579 IdlInterface.prototype.has_stringifier = function() | |
| 1580 //@{ | |
| 1581 { | |
| 1582 if (this.members.some(function(member) { return member.stringifier; })) { | |
| 1583 return true; | |
| 1584 } | |
| 1585 if (this.base && | |
| 1586 this.array.members[this.base].has_stringifier()) { | |
| 1587 return true; | |
| 1588 } | |
| 1589 return false; | |
| 1590 }; | |
| 1591 | |
| 1592 //@} | |
| 1593 IdlInterface.prototype.do_interface_attribute_asserts = function(obj, member) | |
| 1594 //@{ | |
| 1595 { | |
| 1596 // This function tests WebIDL as of 2015-01-27. | |
| 1597 // TODO: Consider [Exposed]. | |
| 1598 | |
| 1599 // This is called by test_member_attribute() with the prototype as obj if | |
| 1600 // it is not a global, and the global otherwise, and by test_interface_of() | |
| 1601 // with the object as obj. | |
| 1602 | |
| 1603 // "For each exposed attribute of the interface, whether it was declared on | |
| 1604 // the interface itself or one of its consequential interfaces, there MUST | |
| 1605 // exist a corresponding property. The characteristics of this property are | |
| 1606 // as follows:" | |
| 1607 | |
| 1608 // "The name of the property is the identifier of the attribute." | |
| 1609 assert_own_property(obj, member.name); | |
| 1610 | |
| 1611 // "The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]: | |
| 1612 // true, [[Configurable]]: configurable }, where: | |
| 1613 // "configurable is false if the attribute was declared with the | |
| 1614 // [Unforgeable] extended attribute and true otherwise; | |
| 1615 // "G is the attribute getter, defined below; and | |
| 1616 // "S is the attribute setter, also defined below." | |
| 1617 var desc = Object.getOwnPropertyDescriptor(obj, member.name); | |
| 1618 assert_false("value" in desc, 'property descriptor has value but is supposed
to be accessor'); | |
| 1619 assert_false("writable" in desc, 'property descriptor has "writable" field b
ut is supposed to be accessor'); | |
| 1620 assert_true(desc.enumerable, "property is not enumerable"); | |
| 1621 if (member.isUnforgeable) | |
| 1622 { | |
| 1623 assert_false(desc.configurable, "[Unforgeable] property must not be conf
igurable"); | |
| 1624 } | |
| 1625 else | |
| 1626 { | |
| 1627 assert_true(desc.configurable, "property must be configurable"); | |
| 1628 } | |
| 1629 | |
| 1630 | |
| 1631 // "The attribute getter is a Function object whose behavior when invoked | |
| 1632 // is as follows:" | |
| 1633 assert_equals(typeof desc.get, "function", "getter must be Function"); | |
| 1634 | |
| 1635 // "If the attribute is a regular attribute, then:" | |
| 1636 if (!member["static"]) { | |
| 1637 // "If O is not a platform object that implements I, then: | |
| 1638 // "If the attribute was specified with the [LenientThis] extended | |
| 1639 // attribute, then return undefined. | |
| 1640 // "Otherwise, throw a TypeError." | |
| 1641 if (!member.has_extended_attribute("LenientThis")) { | |
| 1642 assert_throws(new TypeError(), function() { | |
| 1643 desc.get.call({}); | |
| 1644 }.bind(this), "calling getter on wrong object type must throw TypeEr
ror"); | |
| 1645 } else { | |
| 1646 assert_equals(desc.get.call({}), undefined, | |
| 1647 "calling getter on wrong object type must return undef
ined"); | |
| 1648 } | |
| 1649 } | |
| 1650 | |
| 1651 // "The value of the Function object’s “length” property is the Number | |
| 1652 // value 0." | |
| 1653 assert_equals(desc.get.length, 0, "getter length must be 0"); | |
| 1654 | |
| 1655 | |
| 1656 // TODO: Test calling setter on the interface prototype (should throw | |
| 1657 // TypeError in most cases). | |
| 1658 if (member.readonly | |
| 1659 && !member.has_extended_attribute("PutForwards") | |
| 1660 && !member.has_extended_attribute("Replaceable")) | |
| 1661 { | |
| 1662 // "The attribute setter is undefined if the attribute is declared | |
| 1663 // readonly and has neither a [PutForwards] nor a [Replaceable] | |
| 1664 // extended attribute declared on it." | |
| 1665 assert_equals(desc.set, undefined, "setter must be undefined for readonl
y attributes"); | |
| 1666 } | |
| 1667 else | |
| 1668 { | |
| 1669 // "Otherwise, it is a Function object whose behavior when | |
| 1670 // invoked is as follows:" | |
| 1671 assert_equals(typeof desc.set, "function", "setter must be function for
PutForwards, Replaceable, or non-readonly attributes"); | |
| 1672 | |
| 1673 // "If the attribute is a regular attribute, then:" | |
| 1674 if (!member["static"]) { | |
| 1675 // "If /validThis/ is false and the attribute was not specified | |
| 1676 // with the [LenientThis] extended attribute, then throw a | |
| 1677 // TypeError." | |
| 1678 // "If the attribute is declared with a [Replaceable] extended | |
| 1679 // attribute, then: ..." | |
| 1680 // "If validThis is false, then return." | |
| 1681 if (!member.has_extended_attribute("LenientThis")) { | |
| 1682 assert_throws(new TypeError(), function() { | |
| 1683 desc.set.call({}); | |
| 1684 }.bind(this), "calling setter on wrong object type must throw Ty
peError"); | |
| 1685 } else { | |
| 1686 assert_equals(desc.set.call({}), undefined, | |
| 1687 "calling setter on wrong object type must return u
ndefined"); | |
| 1688 } | |
| 1689 } | |
| 1690 | |
| 1691 // "The value of the Function object’s “length” property is the Number | |
| 1692 // value 1." | |
| 1693 assert_equals(desc.set.length, 1, "setter length must be 1"); | |
| 1694 } | |
| 1695 } | |
| 1696 //@} | |
| 1697 | |
| 1698 /// IdlInterfaceMember /// | |
| 1699 function IdlInterfaceMember(obj) | |
| 1700 //@{ | |
| 1701 { | |
| 1702 /** | |
| 1703 * obj is an object produced by the WebIDLParser.js "ifMember" production. | |
| 1704 * We just forward all properties to this object without modification, | |
| 1705 * except for special extAttrs handling. | |
| 1706 */ | |
| 1707 for (var k in obj) | |
| 1708 { | |
| 1709 this[k] = obj[k]; | |
| 1710 } | |
| 1711 if (!("extAttrs" in this)) | |
| 1712 { | |
| 1713 this.extAttrs = []; | |
| 1714 } | |
| 1715 | |
| 1716 this.isUnforgeable = this.has_extended_attribute("Unforgeable"); | |
| 1717 } | |
| 1718 | |
| 1719 //@} | |
| 1720 IdlInterfaceMember.prototype = Object.create(IdlObject.prototype); | |
| 1721 | |
| 1722 /// Internal helper functions /// | |
| 1723 function create_suitable_object(type) | |
| 1724 //@{ | |
| 1725 { | |
| 1726 /** | |
| 1727 * type is an object produced by the WebIDLParser.js "type" production. We | |
| 1728 * return a JavaScript value that matches the type, if we can figure out | |
| 1729 * how. | |
| 1730 */ | |
| 1731 if (type.nullable) | |
| 1732 { | |
| 1733 return null; | |
| 1734 } | |
| 1735 switch (type.idlType) | |
| 1736 { | |
| 1737 case "any": | |
| 1738 case "boolean": | |
| 1739 return true; | |
| 1740 | |
| 1741 case "byte": case "octet": case "short": case "unsigned short": | |
| 1742 case "long": case "unsigned long": case "long long": | |
| 1743 case "unsigned long long": case "float": case "double": | |
| 1744 case "unrestricted float": case "unrestricted double": | |
| 1745 return 7; | |
| 1746 | |
| 1747 case "DOMString": | |
| 1748 case "ByteString": | |
| 1749 case "USVString": | |
| 1750 return "foo"; | |
| 1751 | |
| 1752 case "object": | |
| 1753 return {a: "b"}; | |
| 1754 | |
| 1755 case "Node": | |
| 1756 return document.createTextNode("abc"); | |
| 1757 } | |
| 1758 return null; | |
| 1759 } | |
| 1760 //@} | |
| 1761 | |
| 1762 /// IdlEnum /// | |
| 1763 // Used for IdlArray.prototype.assert_type_is | |
| 1764 function IdlEnum(obj) | |
| 1765 //@{ | |
| 1766 { | |
| 1767 /** | |
| 1768 * obj is an object produced by the WebIDLParser.js "dictionary" | |
| 1769 * production. | |
| 1770 */ | |
| 1771 | |
| 1772 /** Self-explanatory. */ | |
| 1773 this.name = obj.name; | |
| 1774 | |
| 1775 /** An array of values produced by the "enum" production. */ | |
| 1776 this.values = obj.values; | |
| 1777 | |
| 1778 } | |
| 1779 //@} | |
| 1780 | |
| 1781 IdlEnum.prototype = Object.create(IdlObject.prototype); | |
| 1782 | |
| 1783 /// IdlTypedef /// | |
| 1784 // Used for IdlArray.prototype.assert_type_is | |
| 1785 function IdlTypedef(obj) | |
| 1786 //@{ | |
| 1787 { | |
| 1788 /** | |
| 1789 * obj is an object produced by the WebIDLParser.js "typedef" | |
| 1790 * production. | |
| 1791 */ | |
| 1792 | |
| 1793 /** Self-explanatory. */ | |
| 1794 this.name = obj.name; | |
| 1795 | |
| 1796 /** An array of values produced by the "typedef" production. */ | |
| 1797 this.values = obj.values; | |
| 1798 | |
| 1799 } | |
| 1800 //@} | |
| 1801 | |
| 1802 IdlTypedef.prototype = Object.create(IdlObject.prototype); | |
| 1803 | |
| 1804 }()); | |
| 1805 // vim: set expandtab shiftwidth=4 tabstop=4 foldmarker=@{,@} foldmethod=marker: | |
| OLD | NEW |