1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/imptests/idlharness.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1884 @@ 1.4 +/* 1.5 +Distributed under both the W3C Test Suite License [1] and the W3C 1.6 +3-clause BSD License [2]. To contribute to a W3C Test Suite, see the 1.7 +policies and contribution forms [3]. 1.8 + 1.9 +[1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license 1.10 +[2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license 1.11 +[3] http://www.w3.org/2004/10/27-testcases 1.12 +*/ 1.13 + 1.14 +/* 1.15 + * This file automatically generates browser tests for WebIDL interfaces, using 1.16 + * the testharness.js framework. To use, first include the following: 1.17 + * 1.18 + * <script src=/resources/testharness.js></script> 1.19 + * <script src=/resources/testharnessreport.js></script> 1.20 + * <script src=/resources/WebIDLParser.js></script> 1.21 + * <script src=/resources/idlharness.js></script> 1.22 + * 1.23 + * Then you'll need some type of IDLs. Here's some script that can be run on a 1.24 + * spec written in HTML, which will grab all the elements with class="idl", 1.25 + * concatenate them, and replace the body so you can copy-paste: 1.26 + * 1.27 + var s = ""; 1.28 + [].forEach.call(document.getElementsByClassName("idl"), function(idl) { 1.29 + //https://www.w3.org/Bugs/Public/show_bug.cgi?id=14914 1.30 + if (!idl.classList.contains("extract")) 1.31 + { 1.32 + s += idl.textContent + "\n\n"; 1.33 + } 1.34 + }); 1.35 + document.body.innerHTML = '<pre></pre>'; 1.36 + document.body.firstChild.textContent = s; 1.37 + * 1.38 + * (TODO: write this in Python or something so that it can be done from the 1.39 + * command line instead.) 1.40 + * 1.41 + * Once you have that, put it in your script somehow. The easiest way is to 1.42 + * embed it literally in an HTML file with <script type=text/plain> or similar, 1.43 + * so that you don't have to do any escaping. Another possibility is to put it 1.44 + * in a separate .idl file that's fetched via XHR or similar. Sample usage: 1.45 + * 1.46 + * var idl_array = new IdlArray(); 1.47 + * idl_array.add_untested_idls("interface Node { readonly attribute DOMString nodeName; };"); 1.48 + * idl_array.add_idls("interface Document : Node { readonly attribute DOMString URL; };"); 1.49 + * idl_array.add_objects({Document: ["document"]}); 1.50 + * idl_array.test(); 1.51 + * 1.52 + * This tests that window.Document exists and meets all the requirements of 1.53 + * WebIDL. It also tests that window.document (the result of evaluating the 1.54 + * string "document") has URL and nodeName properties that behave as they 1.55 + * should, and otherwise meets WebIDL's requirements for an object whose 1.56 + * primary interface is Document. It does not test that window.Node exists, 1.57 + * which is what you want if the Node interface is already tested in some other 1.58 + * specification's suite and your specification only extends or refers to it. 1.59 + * Of course, each IDL string can define many different things, and calls to 1.60 + * add_objects() can register many different objects for different interfaces: 1.61 + * this is a very simple example. 1.62 + * 1.63 + * TODO: Write assert_writable, assert_enumerable, assert_configurable and 1.64 + * their inverses, and use those instead of just checking 1.65 + * getOwnPropertyDescriptor. 1.66 + * 1.67 + * == Public methods of IdlArray == 1.68 + * 1.69 + * IdlArray objects can be obtained with new IdlArray(). Anything not 1.70 + * documented in this section should be considered an implementation detail, 1.71 + * and outside callers should not use it. 1.72 + * 1.73 + * add_idls(idl_string): 1.74 + * Parses idl_string (throwing on parse error) and adds the results to the 1.75 + * IdlArray. All the definitions will be tested when you run test(). If 1.76 + * some of the definitions refer to other definitions, those must be present 1.77 + * too. For instance, if idl_string says that Document inherits from Node, 1.78 + * the Node interface must also have been provided in some call to add_idls() 1.79 + * or add_untested_idls(). 1.80 + * 1.81 + * add_untested_idls(idl_string): 1.82 + * Like add_idls(), but the definitions will not be tested. If an untested 1.83 + * interface is added and then extended with a tested partial interface, the 1.84 + * members of the partial interface will still be tested. Also, all the 1.85 + * members will still be tested for objects added with add_objects(), because 1.86 + * you probably want to test that (for instance) window.document has all the 1.87 + * properties from Node, not just Document, even if the Node interface itself 1.88 + * is tested in a different test suite. 1.89 + * 1.90 + * add_objects(dict): 1.91 + * dict should be an object whose keys are the names of interfaces or 1.92 + * exceptions, and whose values are arrays of strings. When an interface or 1.93 + * exception is tested, every string registered for it with add_objects() 1.94 + * will be evaluated, and tests will be run on the result to verify that it 1.95 + * correctly implements that interface or exception. This is the only way to 1.96 + * test anything about [NoInterfaceObject] interfaces, and there are many 1.97 + * tests that can't be run on any interface without an object to fiddle with. 1.98 + * 1.99 + * The interface has to be the *primary* interface of all the objects 1.100 + * provided. For example, don't pass {Node: ["document"]}, but rather 1.101 + * {Document: ["document"]}. Assuming the Document interface was declared to 1.102 + * inherit from Node, this will automatically test that document implements 1.103 + * the Node interface too. 1.104 + * 1.105 + * Warning: methods will be called on any provided objects, in a manner that 1.106 + * WebIDL requires be safe. For instance, if a method has mandatory 1.107 + * arguments, the test suite will try calling it with too few arguments to 1.108 + * see if it throws an exception. If an implementation incorrectly runs the 1.109 + * function instead of throwing, this might have side effects, possibly even 1.110 + * preventing the test suite from running correctly. 1.111 + * 1.112 + * prevent_multiple_testing(name): 1.113 + * This is a niche method for use in case you're testing many objects that 1.114 + * implement the same interfaces, and don't want to retest the same 1.115 + * interfaces every single time. For instance, HTML defines many interfaces 1.116 + * that all inherit from HTMLElement, so the HTML test suite has something 1.117 + * like 1.118 + * .add_objects({ 1.119 + * HTMLHtmlElement: ['document.documentElement'], 1.120 + * HTMLHeadElement: ['document.head'], 1.121 + * HTMLBodyElement: ['document.body'], 1.122 + * ... 1.123 + * }) 1.124 + * and so on for dozens of element types. This would mean that it would 1.125 + * retest that each and every one of those elements implements HTMLElement, 1.126 + * Element, and Node, which would be thousands of basically redundant tests. 1.127 + * The test suite therefore calls prevent_multiple_testing("HTMLElement"). 1.128 + * This means that once one object has been tested to implement HTMLElement 1.129 + * and its ancestors, no other object will be. Thus in the example code 1.130 + * above, the harness would test that document.documentElement correctly 1.131 + * implements HTMLHtmlElement, HTMLElement, Element, and Node; but 1.132 + * document.head would only be tested for HTMLHeadElement, and so on for 1.133 + * further objects. 1.134 + * 1.135 + * test(): 1.136 + * Run all tests. This should be called after you've called all other 1.137 + * methods to add IDLs and objects. 1.138 + */ 1.139 + 1.140 +/** 1.141 + * Notes for people who want to edit this file (not just use it as a library): 1.142 + * 1.143 + * Most of the interesting stuff happens in the derived classes of IdlObject, 1.144 + * especially IdlInterface. The entry point for all IdlObjects is .test(), 1.145 + * which is called by IdlArray.test(). An IdlObject is conceptually just 1.146 + * "thing we want to run tests on", and an IdlArray is an array of IdlObjects 1.147 + * with some additional data thrown in. 1.148 + * 1.149 + * The object model is based on what WebIDLParser.js produces, which is in turn 1.150 + * based on its pegjs grammar. If you want to figure out what properties an 1.151 + * object will have from WebIDLParser.js, the best way is to look at the 1.152 + * grammar: 1.153 + * 1.154 + * https://github.com/darobin/webidl.js/blob/master/lib/grammar.peg 1.155 + * 1.156 + * So for instance: 1.157 + * 1.158 + * // interface definition 1.159 + * interface 1.160 + * = extAttrs:extendedAttributeList? S? "interface" S name:identifier w herit:ifInheritance? w "{" w mem:ifMember* w "}" w ";" w 1.161 + * { return { type: "interface", name: name, inheritance: herit, members: mem, extAttrs: extAttrs }; } 1.162 + * 1.163 + * This means that an "interface" object will have a .type property equal to 1.164 + * the string "interface", a .name property equal to the identifier that the 1.165 + * parser found, an .inheritance property equal to either null or the result of 1.166 + * the "ifInheritance" production found elsewhere in the grammar, and so on. 1.167 + * After each grammatical production is a JavaScript function in curly braces 1.168 + * that gets called with suitable arguments and returns some JavaScript value. 1.169 + * 1.170 + * (Note that the version of WebIDLParser.js we use might sometimes be 1.171 + * out-of-date or forked.) 1.172 + * 1.173 + * The members and methods of the classes defined by this file are all at least 1.174 + * briefly documented, hopefully. 1.175 + */ 1.176 +(function(){ 1.177 +"use strict"; 1.178 +/// Helpers /// 1.179 +function constValue (cnt) { 1.180 + if (cnt.type === "null") return null; 1.181 + if (cnt.type === "NaN") return NaN; 1.182 + if (cnt.type === "Infinity") return cnt.negative ? -Infinity : Infinity; 1.183 + return cnt.value; 1.184 +} 1.185 + 1.186 +/// IdlArray /// 1.187 +// Entry point 1.188 +window.IdlArray = function() 1.189 +//@{ 1.190 +{ 1.191 + /** 1.192 + * A map from strings to the corresponding named IdlObject, such as 1.193 + * IdlInterface or IdlException. These are the things that test() will run 1.194 + * tests on. 1.195 + */ 1.196 + this.members = {}; 1.197 + 1.198 + /** 1.199 + * A map from strings to arrays of strings. The keys are interface or 1.200 + * exception names, and are expected to also exist as keys in this.members 1.201 + * (otherwise they'll be ignored). This is populated by add_objects() -- 1.202 + * see documentation at the start of the file. The actual tests will be 1.203 + * run by calling this.members[name].test_object(obj) for each obj in 1.204 + * this.objects[name]. obj is a string that will be eval'd to produce a 1.205 + * JavaScript value, which is supposed to be an object implementing the 1.206 + * given IdlObject (interface, exception, etc.). 1.207 + */ 1.208 + this.objects = {}; 1.209 + 1.210 + /** 1.211 + * When adding multiple collections of IDLs one at a time, an earlier one 1.212 + * might contain a partial interface or implements statement that depends 1.213 + * on a later one. Save these up and handle them right before we run 1.214 + * tests. 1.215 + * 1.216 + * .partials is simply an array of objects from WebIDLParser.js' 1.217 + * "partialinterface" production. .implements maps strings to arrays of 1.218 + * strings, such that 1.219 + * 1.220 + * A implements B; 1.221 + * A implements C; 1.222 + * D implements E; 1.223 + * 1.224 + * results in { A: ["B", "C"], D: ["E"] }. 1.225 + */ 1.226 + this.partials = []; 1.227 + this["implements"] = {}; 1.228 +}; 1.229 + 1.230 +//@} 1.231 +IdlArray.prototype.add_idls = function(raw_idls) 1.232 +//@{ 1.233 +{ 1.234 + /** Entry point. See documentation at beginning of file. */ 1.235 + this.internal_add_idls(WebIDL2.parse(raw_idls)); 1.236 +}; 1.237 + 1.238 +//@} 1.239 +IdlArray.prototype.add_untested_idls = function(raw_idls) 1.240 +//@{ 1.241 +{ 1.242 + /** Entry point. See documentation at beginning of file. */ 1.243 + var parsed_idls = WebIDL2.parse(raw_idls); 1.244 + for (var i = 0; i < parsed_idls.length; i++) 1.245 + { 1.246 + parsed_idls[i].untested = true; 1.247 + if ("members" in parsed_idls[i]) 1.248 + { 1.249 + for (var j = 0; j < parsed_idls[i].members.length; j++) 1.250 + { 1.251 + parsed_idls[i].members[j].untested = true; 1.252 + } 1.253 + } 1.254 + } 1.255 + this.internal_add_idls(parsed_idls); 1.256 +}; 1.257 + 1.258 +//@} 1.259 +IdlArray.prototype.internal_add_idls = function(parsed_idls) 1.260 +//@{ 1.261 +{ 1.262 + /** 1.263 + * Internal helper called by add_idls() and add_untested_idls(). 1.264 + * parsed_idls is an array of objects that come from WebIDLParser.js's 1.265 + * "definitions" production. The add_untested_idls() entry point 1.266 + * additionally sets an .untested property on each object (and its 1.267 + * .members) so that they'll be skipped by test() -- they'll only be 1.268 + * used for base interfaces of tested interfaces, return types, etc. 1.269 + */ 1.270 + parsed_idls.forEach(function(parsed_idl) 1.271 + { 1.272 + if (parsed_idl.type == "interface" && parsed_idl.partial) 1.273 + { 1.274 + this.partials.push(parsed_idl); 1.275 + return; 1.276 + } 1.277 + 1.278 + if (parsed_idl.type == "implements") 1.279 + { 1.280 + if (!(parsed_idl.target in this["implements"])) 1.281 + { 1.282 + this["implements"][parsed_idl.target] = []; 1.283 + } 1.284 + this["implements"][parsed_idl.target].push(parsed_idl["implements"]); 1.285 + return; 1.286 + } 1.287 + 1.288 + parsed_idl.array = this; 1.289 + if (parsed_idl.name in this.members) 1.290 + { 1.291 + throw "Duplicate identifier " + parsed_idl.name; 1.292 + } 1.293 + switch(parsed_idl.type) 1.294 + { 1.295 + case "interface": 1.296 + this.members[parsed_idl.name] = new IdlInterface(parsed_idl); 1.297 + break; 1.298 + 1.299 + case "exception": 1.300 + this.members[parsed_idl.name] = new IdlException(parsed_idl); 1.301 + break; 1.302 + 1.303 + case "dictionary": 1.304 + // Nothing to test, but we need the dictionary info around for type 1.305 + // checks 1.306 + this.members[parsed_idl.name] = new IdlDictionary(parsed_idl); 1.307 + break; 1.308 + 1.309 + case "typedef": 1.310 + this.members[parsed_idl.name] = new IdlTypedef(parsed_idl); 1.311 + break; 1.312 + 1.313 + case "callback": 1.314 + // TODO 1.315 + console.log("callback not yet supported"); 1.316 + break; 1.317 + 1.318 + case "enum": 1.319 + this.members[parsed_idl.name] = new IdlEnum(parsed_idl); 1.320 + break; 1.321 + 1.322 + case "callback interface": 1.323 + // TODO 1.324 + console.log("callback interface not yet supported"); 1.325 + break; 1.326 + 1.327 + default: 1.328 + throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported"; 1.329 + } 1.330 + }.bind(this)); 1.331 +}; 1.332 + 1.333 +//@} 1.334 +IdlArray.prototype.add_objects = function(dict) 1.335 +//@{ 1.336 +{ 1.337 + /** Entry point. See documentation at beginning of file. */ 1.338 + for (var k in dict) 1.339 + { 1.340 + if (k in this.objects) 1.341 + { 1.342 + this.objects[k] = this.objects[k].concat(dict[k]); 1.343 + } 1.344 + else 1.345 + { 1.346 + this.objects[k] = dict[k]; 1.347 + } 1.348 + } 1.349 +}; 1.350 + 1.351 +//@} 1.352 +IdlArray.prototype.prevent_multiple_testing = function(name) 1.353 +//@{ 1.354 +{ 1.355 + /** Entry point. See documentation at beginning of file. */ 1.356 + this.members[name].prevent_multiple_testing = true; 1.357 +}; 1.358 + 1.359 +//@} 1.360 +IdlArray.prototype.recursively_get_implements = function(interface_name) 1.361 +//@{ 1.362 +{ 1.363 + /** 1.364 + * Helper function for test(). Returns an array of things that implement 1.365 + * interface_name, so if the IDL contains 1.366 + * 1.367 + * A implements B; 1.368 + * B implements C; 1.369 + * B implements D; 1.370 + * 1.371 + * then recursively_get_implements("A") should return ["B", "C", "D"]. 1.372 + */ 1.373 + var ret = this["implements"][interface_name]; 1.374 + if (ret === undefined) 1.375 + { 1.376 + return []; 1.377 + } 1.378 + for (var i = 0; i < this["implements"][interface_name].length; i++) 1.379 + { 1.380 + ret = ret.concat(this.recursively_get_implements(ret[i])); 1.381 + if (ret.indexOf(ret[i]) != ret.lastIndexOf(ret[i])) 1.382 + { 1.383 + throw "Circular implements statements involving " + ret[i]; 1.384 + } 1.385 + } 1.386 + return ret; 1.387 +}; 1.388 + 1.389 +//@} 1.390 +IdlArray.prototype.test = function() 1.391 +//@{ 1.392 +{ 1.393 + /** Entry point. See documentation at beginning of file. */ 1.394 + 1.395 + // First merge in all the partial interfaces and implements statements we 1.396 + // encountered. 1.397 + this.partials.forEach(function(parsed_idl) 1.398 + { 1.399 + if (!(parsed_idl.name in this.members) 1.400 + || !(this.members[parsed_idl.name] instanceof IdlInterface)) 1.401 + { 1.402 + throw "Partial interface " + parsed_idl.name + " with no original interface"; 1.403 + } 1.404 + if (parsed_idl.extAttrs) 1.405 + { 1.406 + parsed_idl.extAttrs.forEach(function(extAttr) 1.407 + { 1.408 + this.members[parsed_idl.name].extAttrs.push(extAttr); 1.409 + }.bind(this)); 1.410 + } 1.411 + parsed_idl.members.forEach(function(member) 1.412 + { 1.413 + this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member)); 1.414 + }.bind(this)); 1.415 + }.bind(this)); 1.416 + this.partials = []; 1.417 + 1.418 + for (var lhs in this["implements"]) 1.419 + { 1.420 + this.recursively_get_implements(lhs).forEach(function(rhs) 1.421 + { 1.422 + var errStr = lhs + " implements " + rhs + ", but "; 1.423 + if (!(lhs in this.members)) throw errStr + lhs + " is undefined."; 1.424 + if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface."; 1.425 + if (!(rhs in this.members)) throw errStr + rhs + " is undefined."; 1.426 + if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface."; 1.427 + this.members[rhs].members.forEach(function(member) 1.428 + { 1.429 + this.members[lhs].members.push(new IdlInterfaceMember(member)); 1.430 + }.bind(this)); 1.431 + }.bind(this)); 1.432 + } 1.433 + this["implements"] = {}; 1.434 + 1.435 + // Now run test() on every member, and test_object() for every object. 1.436 + for (var name in this.members) 1.437 + { 1.438 + this.members[name].test(); 1.439 + if (name in this.objects) 1.440 + { 1.441 + this.objects[name].forEach(function(str) 1.442 + { 1.443 + this.members[name].test_object(str); 1.444 + }.bind(this)); 1.445 + } 1.446 + } 1.447 +}; 1.448 + 1.449 +//@} 1.450 +IdlArray.prototype.assert_type_is = function(value, type) 1.451 +//@{ 1.452 +{ 1.453 + /** 1.454 + * Helper function that tests that value is an instance of type according 1.455 + * to the rules of WebIDL. value is any JavaScript value, and type is an 1.456 + * object produced by WebIDLParser.js' "type" production. That production 1.457 + * is fairly elaborate due to the complexity of WebIDL's types, so it's 1.458 + * best to look at the grammar to figure out what properties it might have. 1.459 + */ 1.460 + if (type.idlType == "any") 1.461 + { 1.462 + // No assertions to make 1.463 + return; 1.464 + } 1.465 + 1.466 + if (type.nullable && value === null) 1.467 + { 1.468 + // This is fine 1.469 + return; 1.470 + } 1.471 + 1.472 + if (type.array) 1.473 + { 1.474 + // TODO: not supported yet 1.475 + return; 1.476 + } 1.477 + 1.478 + if (type.sequence) 1.479 + { 1.480 + assert_true(Array.isArray(value), "is not array"); 1.481 + if (!value.length) 1.482 + { 1.483 + // Nothing we can do. 1.484 + return; 1.485 + } 1.486 + this.assert_type_is(value[0], type.idlType.idlType); 1.487 + return; 1.488 + } 1.489 + 1.490 + type = type.idlType; 1.491 + 1.492 + switch(type) 1.493 + { 1.494 + case "void": 1.495 + assert_equals(value, undefined); 1.496 + return; 1.497 + 1.498 + case "boolean": 1.499 + assert_equals(typeof value, "boolean"); 1.500 + return; 1.501 + 1.502 + case "byte": 1.503 + assert_equals(typeof value, "number"); 1.504 + assert_equals(value, Math.floor(value), "not an integer"); 1.505 + assert_true(-128 <= value && value <= 127, "byte " + value + " not in range [-128, 127]"); 1.506 + return; 1.507 + 1.508 + case "octet": 1.509 + assert_equals(typeof value, "number"); 1.510 + assert_equals(value, Math.floor(value), "not an integer"); 1.511 + assert_true(0 <= value && value <= 255, "octet " + value + " not in range [0, 255]"); 1.512 + return; 1.513 + 1.514 + case "short": 1.515 + assert_equals(typeof value, "number"); 1.516 + assert_equals(value, Math.floor(value), "not an integer"); 1.517 + assert_true(-32768 <= value && value <= 32767, "short " + value + " not in range [-32768, 32767]"); 1.518 + return; 1.519 + 1.520 + case "unsigned short": 1.521 + assert_equals(typeof value, "number"); 1.522 + assert_equals(value, Math.floor(value), "not an integer"); 1.523 + assert_true(0 <= value && value <= 65535, "unsigned short " + value + " not in range [0, 65535]"); 1.524 + return; 1.525 + 1.526 + case "long": 1.527 + assert_equals(typeof value, "number"); 1.528 + assert_equals(value, Math.floor(value), "not an integer"); 1.529 + assert_true(-2147483648 <= value && value <= 2147483647, "long " + value + " not in range [-2147483648, 2147483647]"); 1.530 + return; 1.531 + 1.532 + case "unsigned long": 1.533 + assert_equals(typeof value, "number"); 1.534 + assert_equals(value, Math.floor(value), "not an integer"); 1.535 + assert_true(0 <= value && value <= 4294967295, "unsigned long " + value + " not in range [0, 4294967295]"); 1.536 + return; 1.537 + 1.538 + case "long long": 1.539 + assert_equals(typeof value, "number"); 1.540 + return; 1.541 + 1.542 + case "unsigned long long": 1.543 + assert_equals(typeof value, "number"); 1.544 + assert_true(0 <= value, "unsigned long long is negative"); 1.545 + return; 1.546 + 1.547 + case "float": 1.548 + case "double": 1.549 + case "unrestricted float": 1.550 + case "unrestricted double": 1.551 + // TODO: distinguish these cases 1.552 + assert_equals(typeof value, "number"); 1.553 + return; 1.554 + 1.555 + case "DOMString": 1.556 + assert_equals(typeof value, "string"); 1.557 + return; 1.558 + 1.559 + case "object": 1.560 + assert_true(typeof value == "object" || typeof value == "function", "wrong type: not object or function"); 1.561 + return; 1.562 + } 1.563 + 1.564 + if (!(type in this.members)) 1.565 + { 1.566 + throw "Unrecognized type " + type; 1.567 + } 1.568 + 1.569 + if (this.members[type] instanceof IdlInterface) 1.570 + { 1.571 + // We don't want to run the full 1.572 + // IdlInterface.prototype.test_instance_of, because that could result 1.573 + // in an infinite loop. TODO: This means we don't have tests for 1.574 + // NoInterfaceObject interfaces, and we also can't test objects that 1.575 + // come from another window. 1.576 + assert_true(typeof value == "object" || typeof value == "function", "wrong type: not object or function"); 1.577 + if (value instanceof Object 1.578 + && !this.members[type].has_extended_attribute("NoInterfaceObject") 1.579 + && type in window) 1.580 + { 1.581 + assert_true(value instanceof window[type], "not instanceof " + type); 1.582 + } 1.583 + } 1.584 + else if (this.members[type] instanceof IdlEnum) 1.585 + { 1.586 + assert_equals(typeof value, "string"); 1.587 + } 1.588 + else if (this.members[type] instanceof IdlDictionary) 1.589 + { 1.590 + // TODO: Test when we actually have something to test this on 1.591 + } 1.592 + else if (this.members[type] instanceof IdlTypedef) 1.593 + { 1.594 + // TODO: Test when we actually have something to test this on 1.595 + } 1.596 + else 1.597 + { 1.598 + throw "Type " + type + " isn't an interface or dictionary"; 1.599 + } 1.600 +}; 1.601 +//@} 1.602 + 1.603 +/// IdlObject /// 1.604 +function IdlObject() {} 1.605 +IdlObject.prototype.test = function() 1.606 +//@{ 1.607 +{ 1.608 + /** 1.609 + * By default, this does nothing, so no actual tests are run for IdlObjects 1.610 + * that don't define any (e.g., IdlDictionary at the time of this writing). 1.611 + */ 1.612 +}; 1.613 + 1.614 +//@} 1.615 +IdlObject.prototype.has_extended_attribute = function(name) 1.616 +//@{ 1.617 +{ 1.618 + /** 1.619 + * This is only meaningful for things that support extended attributes, 1.620 + * such as interfaces, exceptions, and members. 1.621 + */ 1.622 + return this.extAttrs.some(function(o) 1.623 + { 1.624 + return o.name == name; 1.625 + }); 1.626 +}; 1.627 + 1.628 +//@} 1.629 + 1.630 +/// IdlDictionary /// 1.631 +// Used for IdlArray.prototype.assert_type_is 1.632 +function IdlDictionary(obj) 1.633 +//@{ 1.634 +{ 1.635 + /** 1.636 + * obj is an object produced by the WebIDLParser.js "dictionary" 1.637 + * production. 1.638 + */ 1.639 + 1.640 + /** Self-explanatory. */ 1.641 + this.name = obj.name; 1.642 + 1.643 + /** An array of objects produced by the "dictionaryMember" production. */ 1.644 + this.members = obj.members; 1.645 + 1.646 + /** 1.647 + * The name (as a string) of the dictionary type we inherit from, or null 1.648 + * if there is none. 1.649 + */ 1.650 + this.base = obj.inheritance; 1.651 +} 1.652 + 1.653 +//@} 1.654 +IdlDictionary.prototype = Object.create(IdlObject.prototype); 1.655 + 1.656 +/// IdlExceptionOrInterface /// 1.657 +// Code sharing! 1.658 +function IdlExceptionOrInterface(obj) 1.659 +//@{ 1.660 +{ 1.661 + /** 1.662 + * obj is an object produced by the WebIDLParser.js "exception" or 1.663 + * "interface" production, as appropriate. 1.664 + */ 1.665 + 1.666 + /** Self-explanatory. */ 1.667 + this.name = obj.name; 1.668 + 1.669 + /** A back-reference to our IdlArray. */ 1.670 + this.array = obj.array; 1.671 + 1.672 + /** 1.673 + * An indicator of whether we should run tests on the (exception) interface 1.674 + * object and (exception) interface prototype object. Tests on members are 1.675 + * controlled by .untested on each member, not this. 1.676 + */ 1.677 + this.untested = obj.untested; 1.678 + 1.679 + /** An array of objects produced by the "ExtAttr" production. */ 1.680 + this.extAttrs = obj.extAttrs; 1.681 + 1.682 + /** An array of IdlInterfaceMembers. */ 1.683 + this.members = obj.members.map(function(m){return new IdlInterfaceMember(m); }); 1.684 + 1.685 + /** 1.686 + * The name (as a string) of the type we inherit from, or null if there is 1.687 + * none. 1.688 + */ 1.689 + this.base = obj.inheritance; 1.690 +} 1.691 + 1.692 +//@} 1.693 +IdlExceptionOrInterface.prototype = Object.create(IdlObject.prototype); 1.694 +IdlExceptionOrInterface.prototype.test = function() 1.695 +//@{ 1.696 +{ 1.697 + if (this.has_extended_attribute("NoInterfaceObject")) 1.698 + { 1.699 + // No tests to do without an instance. TODO: We should still be able 1.700 + // to run tests on the prototype object, if we obtain one through some 1.701 + // other means. 1.702 + return; 1.703 + } 1.704 + 1.705 + if (!this.untested) 1.706 + { 1.707 + // First test things to do with the exception/interface object and 1.708 + // exception/interface prototype object. 1.709 + this.test_self(); 1.710 + } 1.711 + // Then test things to do with its members (constants, fields, attributes, 1.712 + // operations, . . .). These are run even if .untested is true, because 1.713 + // members might themselves be marked as .untested. This might happen to 1.714 + // interfaces if the interface itself is untested but a partial interface 1.715 + // that extends it is tested -- then the interface itself and its initial 1.716 + // members will be marked as untested, but the members added by the partial 1.717 + // interface are still tested. 1.718 + this.test_members(); 1.719 +}; 1.720 + 1.721 +//@} 1.722 + 1.723 +/// IdlException /// 1.724 +function IdlException(obj) { IdlExceptionOrInterface.call(this, obj); } 1.725 +IdlException.prototype = Object.create(IdlExceptionOrInterface.prototype); 1.726 +IdlException.prototype.test_self = function() 1.727 +//@{ 1.728 +{ 1.729 + test(function() 1.730 + { 1.731 + // "For every exception that is not declared with the 1.732 + // [NoInterfaceObject] extended attribute, a corresponding property 1.733 + // must exist on the exception’s relevant namespace object. The name of 1.734 + // the property is the identifier of the exception, and its value is an 1.735 + // object called the exception interface object, which provides access 1.736 + // to any constants that have been associated with the exception. The 1.737 + // property has the attributes { [[Writable]]: true, [[Enumerable]]: 1.738 + // false, [[Configurable]]: true }." 1.739 + assert_own_property(window, this.name, 1.740 + "window does not have own property " + format_value(this.name)); 1.741 + var desc = Object.getOwnPropertyDescriptor(window, this.name); 1.742 + assert_false("get" in desc, "window's property " + format_value(this.name) + " has getter"); 1.743 + assert_false("set" in desc, "window's property " + format_value(this.name) + " has setter"); 1.744 + assert_true(desc.writable, "window's property " + format_value(this.name) + " is not writable"); 1.745 + assert_false(desc.enumerable, "window's property " + format_value(this.name) + " is enumerable"); 1.746 + assert_true(desc.configurable, "window's property " + format_value(this.name) + " is not configurable"); 1.747 + 1.748 + // "The exception interface object for a given exception must be a 1.749 + // function object." 1.750 + // "If an object is defined to be a function object, then it has 1.751 + // characteristics as follows:" 1.752 + // "Its [[Prototype]] internal property is the Function prototype 1.753 + // object." 1.754 + // Note: This doesn't match browsers as of December 2011, see 1.755 + // http://www.w3.org/Bugs/Public/show_bug.cgi?id=14813 1.756 + assert_equals(Object.getPrototypeOf(window[this.name]), Function.prototype, 1.757 + "prototype of window's property " + format_value(this.name) + " is not Function.prototype"); 1.758 + // "Its [[Get]] internal property is set as described in ECMA-262 1.759 + // section 15.3.5.4." 1.760 + // Not much to test for this. 1.761 + // "Its [[Construct]] internal property is set as described in ECMA-262 1.762 + // section 13.2.2." 1.763 + // Tested below. 1.764 + // "Its [[HasInstance]] internal property is set as described in 1.765 + // ECMA-262 section 15.3.5.3, unless otherwise specified." 1.766 + // TODO 1.767 + // "Its [[Class]] internal property is “Function”." 1.768 + // String() returns something implementation-dependent, because it 1.769 + // calls Function#toString. 1.770 + assert_class_string(window[this.name], "Function", 1.771 + "class string of " + this.name); 1.772 + 1.773 + // TODO: Test 4.9.1.1. Exception interface object [[Call]] method 1.774 + // (which does not match browsers: 1.775 + // http://www.w3.org/Bugs/Public/show_bug.cgi?id=14885) 1.776 + }.bind(this), this.name + " exception: existence and properties of exception interface object"); 1.777 + 1.778 + test(function() 1.779 + { 1.780 + assert_own_property(window, this.name, 1.781 + "window does not have own property " + format_value(this.name)); 1.782 + 1.783 + // "The exception interface object must also have a property named 1.784 + // “prototype” with attributes { [[Writable]]: false, [[Enumerable]]: 1.785 + // false, [[Configurable]]: false } whose value is an object called the 1.786 + // exception interface prototype object. This object also provides 1.787 + // access to the constants that are declared on the exception." 1.788 + assert_own_property(window[this.name], "prototype", 1.789 + 'exception "' + this.name + '" does not have own property "prototype"'); 1.790 + var desc = Object.getOwnPropertyDescriptor(window[this.name], "prototype"); 1.791 + assert_false("get" in desc, this.name + ".prototype has getter"); 1.792 + assert_false("set" in desc, this.name + ".prototype has setter"); 1.793 + assert_false(desc.writable, this.name + ".prototype is writable"); 1.794 + assert_false(desc.enumerable, this.name + ".prototype is enumerable"); 1.795 + assert_false(desc.configurable, this.name + ".prototype is configurable"); 1.796 + 1.797 + // "The exception interface prototype object for a given exception must 1.798 + // have an internal [[Prototype]] property whose value is as follows: 1.799 + // 1.800 + // "If the exception is declared to inherit from another exception, 1.801 + // then the value of the internal [[Prototype]] property is the 1.802 + // exception interface prototype object for the inherited exception. 1.803 + // "Otherwise, the exception is not declared to inherit from another 1.804 + // exception. The value of the internal [[Prototype]] property is the 1.805 + // Error prototype object ([ECMA-262], section 15.11.3.1)." 1.806 + // 1.807 + // Note: This doesn't match browsers as of December 2011, see 1.808 + // https://www.w3.org/Bugs/Public/show_bug.cgi?id=14887. 1.809 + var inherit_exception = this.base ? this.base : "Error"; 1.810 + assert_own_property(window, inherit_exception, 1.811 + 'should inherit from ' + inherit_exception + ', but window has no such property'); 1.812 + assert_own_property(window[inherit_exception], "prototype", 1.813 + 'should inherit from ' + inherit_exception + ', but that object has no "prototype" property'); 1.814 + assert_equals(Object.getPrototypeOf(window[this.name].prototype), 1.815 + window[inherit_exception].prototype, 1.816 + 'prototype of ' + this.name + '.prototype is not ' + inherit_exception + '.prototype'); 1.817 + 1.818 + // "The class string of an exception interface prototype object is the 1.819 + // concatenation of the exception’s identifier and the string 1.820 + // “Prototype”." 1.821 + assert_class_string(window[this.name].prototype, this.name + "Prototype", 1.822 + "class string of " + this.name + ".prototype"); 1.823 + // TODO: Test String(), based on ES definition of 1.824 + // Error.prototype.toString? 1.825 + }.bind(this), this.name + " exception: existence and properties of exception interface prototype object"); 1.826 + 1.827 + test(function() 1.828 + { 1.829 + assert_own_property(window, this.name, 1.830 + "window does not have own property " + format_value(this.name)); 1.831 + assert_own_property(window[this.name], "prototype", 1.832 + 'interface "' + this.name + '" does not have own property "prototype"'); 1.833 + 1.834 + // "There must be a property named “name” on the exception interface 1.835 + // prototype object with attributes { [[Writable]]: true, 1.836 + // [[Enumerable]]: false, [[Configurable]]: true } and whose value is 1.837 + // the identifier of the exception." 1.838 + assert_own_property(window[this.name].prototype, "name", 1.839 + 'prototype object does not have own property "name"'); 1.840 + var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, "name"); 1.841 + assert_false("get" in desc, this.name + ".prototype.name has getter"); 1.842 + assert_false("set" in desc, this.name + ".prototype.name has setter"); 1.843 + assert_true(desc.writable, this.name + ".prototype.name is not writable"); 1.844 + assert_false(desc.enumerable, this.name + ".prototype.name is enumerable"); 1.845 + assert_true(desc.configurable, this.name + ".prototype.name is not configurable"); 1.846 + assert_equals(desc.value, this.name, this.name + ".prototype.name has incorrect value"); 1.847 + }.bind(this), this.name + " exception: existence and properties of exception interface prototype object's \"name\" property"); 1.848 + 1.849 + test(function() 1.850 + { 1.851 + assert_own_property(window, this.name, 1.852 + "window does not have own property " + format_value(this.name)); 1.853 + assert_own_property(window[this.name], "prototype", 1.854 + 'interface "' + this.name + '" does not have own property "prototype"'); 1.855 + 1.856 + // "If the [NoInterfaceObject] extended attribute was not specified on 1.857 + // the exception, then there must also be a property named 1.858 + // “constructor” on the exception interface prototype object with 1.859 + // attributes { [[Writable]]: true, [[Enumerable]]: false, 1.860 + // [[Configurable]]: true } and whose value is a reference to the 1.861 + // exception interface object for the exception." 1.862 + assert_own_property(window[this.name].prototype, "constructor", 1.863 + this.name + '.prototype does not have own property "constructor"'); 1.864 + var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, "constructor"); 1.865 + assert_false("get" in desc, this.name + ".prototype.constructor has getter"); 1.866 + assert_false("set" in desc, this.name + ".prototype.constructor has setter"); 1.867 + assert_true(desc.writable, this.name + ".prototype.constructor is not writable"); 1.868 + assert_false(desc.enumerable, this.name + ".prototype.constructor is enumerable"); 1.869 + assert_true(desc.configurable, this.name + ".prototype.constructor in not configurable"); 1.870 + assert_equals(window[this.name].prototype.constructor, window[this.name], 1.871 + this.name + '.prototype.constructor is not the same object as ' + this.name); 1.872 + }.bind(this), this.name + " exception: existence and properties of exception interface prototype object's \"constructor\" property"); 1.873 +}; 1.874 + 1.875 +//@} 1.876 +IdlException.prototype.test_members = function() 1.877 +//@{ 1.878 +{ 1.879 + for (var i = 0; i < this.members.length; i++) 1.880 + { 1.881 + var member = this.members[i]; 1.882 + if (member.untested) 1.883 + { 1.884 + continue; 1.885 + } 1.886 + if (member.type == "const" && member.name != "prototype") 1.887 + { 1.888 + test(function() 1.889 + { 1.890 + assert_own_property(window, this.name, 1.891 + "window does not have own property " + format_value(this.name)); 1.892 + 1.893 + // "For each constant defined on the exception, there must be a 1.894 + // corresponding property on the exception interface object, if 1.895 + // it exists, if the identifier of the constant is not 1.896 + // “prototype”." 1.897 + assert_own_property(window[this.name], member.name); 1.898 + // "The value of the property is the ECMAScript value that is 1.899 + // equivalent to the constant’s IDL value, according to the 1.900 + // rules in section 4.2 above." 1.901 + assert_equals(window[this.name][member.name], constValue(member.value), 1.902 + "property has wrong value"); 1.903 + // "The property has attributes { [[Writable]]: false, 1.904 + // [[Enumerable]]: true, [[Configurable]]: false }." 1.905 + var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name); 1.906 + assert_false("get" in desc, "property has getter"); 1.907 + assert_false("set" in desc, "property has setter"); 1.908 + assert_false(desc.writable, "property is writable"); 1.909 + assert_true(desc.enumerable, "property is not enumerable"); 1.910 + assert_false(desc.configurable, "property is configurable"); 1.911 + }.bind(this), this.name + " exception: constant " + member.name + " on exception interface object"); 1.912 + // "In addition, a property with the same characteristics must 1.913 + // exist on the exception interface prototype object." 1.914 + test(function() 1.915 + { 1.916 + assert_own_property(window, this.name, 1.917 + "window does not have own property " + format_value(this.name)); 1.918 + assert_own_property(window[this.name], "prototype", 1.919 + 'exception "' + this.name + '" does not have own property "prototype"'); 1.920 + 1.921 + assert_own_property(window[this.name].prototype, member.name); 1.922 + assert_equals(window[this.name].prototype[member.name], constValue(member.value), 1.923 + "property has wrong value"); 1.924 + var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, member.name); 1.925 + assert_false("get" in desc, "property has getter"); 1.926 + assert_false("set" in desc, "property has setter"); 1.927 + assert_false(desc.writable, "property is writable"); 1.928 + assert_true(desc.enumerable, "property is not enumerable"); 1.929 + assert_false(desc.configurable, "property is configurable"); 1.930 + }.bind(this), this.name + " exception: constant " + member.name + " on exception interface prototype object"); 1.931 + } 1.932 + else if (member.type == "field") 1.933 + { 1.934 + test(function() 1.935 + { 1.936 + assert_own_property(window, this.name, 1.937 + "window does not have own property " + format_value(this.name)); 1.938 + assert_own_property(window[this.name], "prototype", 1.939 + 'exception "' + this.name + '" does not have own property "prototype"'); 1.940 + 1.941 + // "For each exception field, there must be a corresponding 1.942 + // property on the exception interface prototype object, whose 1.943 + // characteristics are as follows: 1.944 + // "The name of the property is the identifier of the exception 1.945 + // field." 1.946 + assert_own_property(window[this.name].prototype, member.name); 1.947 + // "The property has attributes { [[Get]]: G, [[Enumerable]]: 1.948 + // true, [[Configurable]]: true }, where G is the exception 1.949 + // field getter, defined below." 1.950 + var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, member.name); 1.951 + assert_false("value" in desc, "property descriptor has value but is supposed to be accessor"); 1.952 + assert_false("writable" in desc, 'property descriptor has "writable" field but is supposed to be accessor'); 1.953 + // TODO: ES5 doesn't seem to say whether desc should have a 1.954 + // .set property. 1.955 + assert_true(desc.enumerable, "property is not enumerable"); 1.956 + assert_true(desc.configurable, "property is not configurable"); 1.957 + // "The exception field getter is a Function object whose 1.958 + // behavior when invoked is as follows:" 1.959 + assert_equals(typeof desc.get, "function", "typeof getter"); 1.960 + // "The value of the Function object’s “length” property is the 1.961 + // Number value 0." 1.962 + // This test is before the TypeError tests so that it's easiest 1.963 + // to see that Firefox 11a1 only fails one assert in this test. 1.964 + assert_equals(desc.get.length, 0, "getter length"); 1.965 + // "Let O be the result of calling ToObject on the this value. 1.966 + // "If O is not a platform object representing an exception for 1.967 + // the exception on which the exception field was declared, 1.968 + // then throw a TypeError." 1.969 + // TODO: Test on a platform object representing an exception. 1.970 + assert_throws(new TypeError(), function() 1.971 + { 1.972 + window[this.name].prototype[member.name]; 1.973 + }.bind(this), "getting property on prototype object must throw TypeError"); 1.974 + assert_throws(new TypeError(), function() 1.975 + { 1.976 + desc.get.call({}); 1.977 + }.bind(this), "calling getter on wrong object type must throw TypeError"); 1.978 + }.bind(this), this.name + " exception: field " + member.name + " on exception interface prototype object"); 1.979 + } 1.980 + } 1.981 +}; 1.982 + 1.983 +//@} 1.984 +IdlException.prototype.test_object = function(desc) 1.985 +//@{ 1.986 +{ 1.987 + var obj, exception = null; 1.988 + try 1.989 + { 1.990 + obj = eval(desc); 1.991 + } 1.992 + catch(e) 1.993 + { 1.994 + exception = e; 1.995 + } 1.996 + 1.997 + test(function() 1.998 + { 1.999 + assert_equals(exception, null, "Unexpected exception when evaluating object"); 1.1000 + assert_equals(typeof obj, "object", "wrong typeof object"); 1.1001 + 1.1002 + // We can't easily test that its prototype is correct if there's no 1.1003 + // interface object, or the object is from a different global 1.1004 + // environment (not instanceof Object). TODO: test in this case that 1.1005 + // its prototype at least looks correct, even if we can't test that 1.1006 + // it's actually correct. 1.1007 + if (!this.has_extended_attribute("NoInterfaceObject") 1.1008 + && (typeof obj != "object" || obj instanceof Object)) 1.1009 + { 1.1010 + assert_own_property(window, this.name, 1.1011 + "window does not have own property " + format_value(this.name)); 1.1012 + assert_own_property(window[this.name], "prototype", 1.1013 + 'exception "' + this.name + '" does not have own property "prototype"'); 1.1014 + 1.1015 + // "The value of the internal [[Prototype]] property of the 1.1016 + // exception object must be the exception interface prototype 1.1017 + // object from the global environment the exception object is 1.1018 + // associated with." 1.1019 + assert_equals(Object.getPrototypeOf(obj), 1.1020 + window[this.name].prototype, 1.1021 + desc + "'s prototype is not " + this.name + ".prototype"); 1.1022 + } 1.1023 + 1.1024 + // "The class string of the exception object must be the identifier of 1.1025 + // the exception." 1.1026 + assert_class_string(obj, this.name, "class string of " + desc); 1.1027 + // Stringifier is not defined for DOMExceptions, because message isn't 1.1028 + // defined. 1.1029 + }.bind(this), this.name + " must be represented by " + desc); 1.1030 + 1.1031 + for (var i = 0; i < this.members.length; i++) 1.1032 + { 1.1033 + var member = this.members[i]; 1.1034 + test(function() 1.1035 + { 1.1036 + assert_equals(exception, null, "Unexpected exception when evaluating object"); 1.1037 + assert_equals(typeof obj, "object", "wrong typeof object"); 1.1038 + assert_inherits(obj, member.name); 1.1039 + if (member.type == "const") 1.1040 + { 1.1041 + assert_equals(obj[member.name], constValue(member.value)); 1.1042 + } 1.1043 + if (member.type == "field") 1.1044 + { 1.1045 + this.array.assert_type_is(obj[member.name], member.idlType); 1.1046 + } 1.1047 + }.bind(this), this.name + " exception: " + desc + ' must inherit property "' + member.name + '" with the proper type'); 1.1048 + } 1.1049 +}; 1.1050 +//@} 1.1051 + 1.1052 +/// IdlInterface /// 1.1053 +function IdlInterface(obj) { IdlExceptionOrInterface.call(this, obj); } 1.1054 +IdlInterface.prototype = Object.create(IdlExceptionOrInterface.prototype); 1.1055 +IdlInterface.prototype.is_callback = function() 1.1056 +//@{ 1.1057 +{ 1.1058 + return this.has_extended_attribute("Callback"); 1.1059 +}; 1.1060 +//@} 1.1061 + 1.1062 +IdlInterface.prototype.has_constants = function() 1.1063 +//@{ 1.1064 +{ 1.1065 + return this.members.some(function(member) { 1.1066 + return member.type === "const"; 1.1067 + }); 1.1068 +}; 1.1069 +//@} 1.1070 + 1.1071 +IdlInterface.prototype.test_self = function() 1.1072 +//@{ 1.1073 +{ 1.1074 + test(function() 1.1075 + { 1.1076 + // This function tests WebIDL as of 2012-11-28. 1.1077 + 1.1078 + // "For every interface that: 1.1079 + // * is a callback interface that has constants declared on it, or 1.1080 + // * is a non-callback interface that is not declared with the 1.1081 + // [NoInterfaceObject] extended attribute, 1.1082 + // a corresponding property MUST exist on the ECMAScript global object. 1.1083 + // The name of the property is the identifier of the interface, and its 1.1084 + // value is an object called the interface object. 1.1085 + // The property has the attributes { [[Writable]]: true, 1.1086 + // [[Enumerable]]: false, [[Configurable]]: true }." 1.1087 + if (this.is_callback() && !this.has_constants()) { 1.1088 + return; 1.1089 + } 1.1090 + 1.1091 + // TODO: Should we test here that the property is actually writable 1.1092 + // etc., or trust getOwnPropertyDescriptor? 1.1093 + assert_own_property(window, this.name, 1.1094 + "window does not have own property " + format_value(this.name)); 1.1095 + var desc = Object.getOwnPropertyDescriptor(window, this.name); 1.1096 + assert_false("get" in desc, "window's property " + format_value(this.name) + " has getter"); 1.1097 + assert_false("set" in desc, "window's property " + format_value(this.name) + " has setter"); 1.1098 + assert_true(desc.writable, "window's property " + format_value(this.name) + " is not writable"); 1.1099 + assert_false(desc.enumerable, "window's property " + format_value(this.name) + " is enumerable"); 1.1100 + assert_true(desc.configurable, "window's property " + format_value(this.name) + " is not configurable"); 1.1101 + 1.1102 + if (this.is_callback()) { 1.1103 + // "The internal [[Prototype]] property of an interface object for 1.1104 + // a callback interface MUST be the Object.prototype object." 1.1105 + assert_equals(Object.getPrototypeOf(window[this.name]), Object.prototype, 1.1106 + "prototype of window's property " + format_value(this.name) + " is not Object.prototype"); 1.1107 + 1.1108 + return; 1.1109 + } 1.1110 + 1.1111 + // "The interface object for a given non-callback interface is a 1.1112 + // function object." 1.1113 + // "If an object is defined to be a function object, then it has 1.1114 + // characteristics as follows:" 1.1115 + 1.1116 + // "* Its [[Prototype]] internal property is the Function prototype 1.1117 + // object." 1.1118 + assert_equals(Object.getPrototypeOf(window[this.name]), Function.prototype, 1.1119 + "prototype of window's property " + format_value(this.name) + " is not Function.prototype"); 1.1120 + 1.1121 + // "* Its [[Get]] internal property is set as described in ECMA-262 1.1122 + // section 15.3.5.4." 1.1123 + // Not much to test for this. 1.1124 + 1.1125 + // "* Its [[Construct]] internal property is set as described in 1.1126 + // ECMA-262 section 13.2.2." 1.1127 + // Tested below if no constructor is defined. TODO: test constructors 1.1128 + // if defined. 1.1129 + 1.1130 + // "* Its [[HasInstance]] internal property is set as described in 1.1131 + // ECMA-262 section 15.3.5.3, unless otherwise specified." 1.1132 + // TODO 1.1133 + 1.1134 + // "* Its [[NativeBrand]] internal property is “Function”." 1.1135 + // String() returns something implementation-dependent, because it calls 1.1136 + // Function#toString. 1.1137 + assert_class_string(window[this.name], "Function", "class string of " + this.name); 1.1138 + 1.1139 + if (!this.has_extended_attribute("Constructor")) { 1.1140 + // "The internal [[Call]] method of the interface object behaves as 1.1141 + // follows . . . 1.1142 + // 1.1143 + // "If I was not declared with a [Constructor] extended attribute, 1.1144 + // then throw a TypeError." 1.1145 + assert_throws(new TypeError(), function() { 1.1146 + window[this.name](); 1.1147 + }.bind(this), "interface object didn't throw TypeError when called as a function"); 1.1148 + assert_throws(new TypeError(), function() { 1.1149 + new window[this.name](); 1.1150 + }.bind(this), "interface object didn't throw TypeError when called as a constructor"); 1.1151 + } 1.1152 + }.bind(this), this.name + " interface: existence and properties of interface object"); 1.1153 + 1.1154 + if (!this.is_callback()) { 1.1155 + test(function() { 1.1156 + // This function tests WebIDL as of 2013-08-25. 1.1157 + // http://dev.w3.org/2006/webapi/WebIDL/#es-interface-call 1.1158 + 1.1159 + assert_own_property(window, this.name, 1.1160 + "window does not have own property " + format_value(this.name)); 1.1161 + 1.1162 + // "Interface objects for non-callback interfaces MUST have a 1.1163 + // property named “length” with attributes { [[Writable]]: false, 1.1164 + // [[Enumerable]]: false, [[Configurable]]: false } whose value is 1.1165 + // a Number." 1.1166 + assert_own_property(window[this.name], "length"); 1.1167 + var desc = Object.getOwnPropertyDescriptor(window[this.name], "length"); 1.1168 + assert_false("get" in desc, this.name + ".length has getter"); 1.1169 + assert_false("set" in desc, this.name + ".length has setter"); 1.1170 + assert_false(desc.writable, this.name + ".length is writable"); 1.1171 + assert_false(desc.enumerable, this.name + ".length is enumerable"); 1.1172 + assert_false(desc.configurable, this.name + ".length is configurable"); 1.1173 + 1.1174 + var constructors = this.extAttrs 1.1175 + .filter(function(attr) { return attr.name == "Constructor"; }); 1.1176 + var expected_length; 1.1177 + if (!constructors.length) { 1.1178 + // "If the [Constructor] extended attribute, does not appear on 1.1179 + // the interface definition, then the value is 0." 1.1180 + expected_length = 0; 1.1181 + } else { 1.1182 + // "Otherwise, the value is determined as follows: . . . 1.1183 + // "Return the length of the shortest argument list of the 1.1184 + // entries in S." 1.1185 + expected_length = constructors.map(function(attr) { 1.1186 + return attr.arguments ? attr.arguments.filter(function(arg) { 1.1187 + return !arg.optional; 1.1188 + }).length : 0; 1.1189 + }) 1.1190 + .reduce(function(m, n) { return Math.min(m, n); }); 1.1191 + } 1.1192 + assert_equals(window[this.name].length, expected_length, "wrong value for " + this.name + ".length"); 1.1193 + }.bind(this), this.name + " interface object length"); 1.1194 + } 1.1195 + 1.1196 + // TODO: Test named constructors if I find any interfaces that have them. 1.1197 + 1.1198 + test(function() 1.1199 + { 1.1200 + assert_own_property(window, this.name, 1.1201 + "window does not have own property " + format_value(this.name)); 1.1202 + 1.1203 + if (this.has_extended_attribute("Callback")) { 1.1204 + assert_false("prototype" in window[this.name], 1.1205 + this.name + ' should not have a "prototype" property'); 1.1206 + return; 1.1207 + } 1.1208 + 1.1209 + // "The interface object must also have a property named “prototype” 1.1210 + // with attributes { [[Writable]]: false, [[Enumerable]]: false, 1.1211 + // [[Configurable]]: false } whose value is an object called the 1.1212 + // interface prototype object. This object has properties that 1.1213 + // correspond to the attributes and operations defined on the 1.1214 + // interface, and is described in more detail in section 4.5.3 below." 1.1215 + assert_own_property(window[this.name], "prototype", 1.1216 + 'interface "' + this.name + '" does not have own property "prototype"'); 1.1217 + var desc = Object.getOwnPropertyDescriptor(window[this.name], "prototype"); 1.1218 + assert_false("get" in desc, this.name + ".prototype has getter"); 1.1219 + assert_false("set" in desc, this.name + ".prototype has setter"); 1.1220 + assert_false(desc.writable, this.name + ".prototype is writable"); 1.1221 + assert_false(desc.enumerable, this.name + ".prototype is enumerable"); 1.1222 + assert_false(desc.configurable, this.name + ".prototype is configurable"); 1.1223 + 1.1224 + // Next, test that the [[Prototype]] of the interface prototype object 1.1225 + // is correct. (This is made somewhat difficult by the existence of 1.1226 + // [NoInterfaceObject].) 1.1227 + // TODO: Aryeh thinks there's at least other place in this file where 1.1228 + // we try to figure out if an interface prototype object is 1.1229 + // correct. Consolidate that code. 1.1230 + 1.1231 + // "The interface prototype object for a given interface A must have an 1.1232 + // internal [[Prototype]] property whose value is as follows: 1.1233 + // "If A is not declared to inherit from another interface, then the 1.1234 + // value of the internal [[Prototype]] property of A is the Array 1.1235 + // prototype object ([ECMA-262], section 15.4.4) if the interface was 1.1236 + // declared with ArrayClass, or the Object prototype object otherwise 1.1237 + // ([ECMA-262], section 15.2.4). 1.1238 + // "Otherwise, A does inherit from another interface. The value of the 1.1239 + // internal [[Prototype]] property of A is the interface prototype 1.1240 + // object for the inherited interface." 1.1241 + var inherit_interface, inherit_interface_has_interface_object; 1.1242 + if (this.base) { 1.1243 + inherit_interface = this.base; 1.1244 + inherit_interface_has_interface_object = 1.1245 + !this.array 1.1246 + .members[inherit_interface] 1.1247 + .has_extended_attribute("NoInterfaceObject"); 1.1248 + } else if (this.has_extended_attribute('ArrayClass')) { 1.1249 + inherit_interface = 'Array'; 1.1250 + inherit_interface_has_interface_object = true; 1.1251 + } else { 1.1252 + inherit_interface = 'Object'; 1.1253 + inherit_interface_has_interface_object = true; 1.1254 + } 1.1255 + if (inherit_interface_has_interface_object) { 1.1256 + assert_own_property(window, inherit_interface, 1.1257 + 'should inherit from ' + inherit_interface + ', but window has no such property'); 1.1258 + assert_own_property(window[inherit_interface], 'prototype', 1.1259 + 'should inherit from ' + inherit_interface + ', but that object has no "prototype" property'); 1.1260 + assert_equals(Object.getPrototypeOf(window[this.name].prototype), 1.1261 + window[inherit_interface].prototype, 1.1262 + 'prototype of ' + this.name + '.prototype is not ' + inherit_interface + '.prototype'); 1.1263 + } else { 1.1264 + // We can't test that we get the correct object, because this is the 1.1265 + // only way to get our hands on it. We only test that its class 1.1266 + // string, at least, is correct. 1.1267 + assert_class_string(Object.getPrototypeOf(window[this.name].prototype), 1.1268 + inherit_interface + 'Prototype', 1.1269 + 'Class name for prototype of ' + this.name + 1.1270 + '.prototype is not "' + inherit_interface + 'Prototype"'); 1.1271 + } 1.1272 + 1.1273 + // "The class string of an interface prototype object is the 1.1274 + // concatenation of the interface’s identifier and the string 1.1275 + // “Prototype”." 1.1276 + assert_class_string(window[this.name].prototype, this.name + "Prototype", 1.1277 + "class string of " + this.name + ".prototype"); 1.1278 + // String() should end up calling {}.toString if nothing defines a 1.1279 + // stringifier. 1.1280 + if (!this.has_stringifier()) { 1.1281 + assert_equals(String(window[this.name].prototype), "[object " + this.name + "Prototype]", 1.1282 + "String(" + this.name + ".prototype)"); 1.1283 + } 1.1284 + }.bind(this), this.name + " interface: existence and properties of interface prototype object"); 1.1285 + 1.1286 + test(function() 1.1287 + { 1.1288 + assert_own_property(window, this.name, 1.1289 + "window does not have own property " + format_value(this.name)); 1.1290 + 1.1291 + if (this.has_extended_attribute("Callback")) { 1.1292 + assert_false("prototype" in window[this.name], 1.1293 + this.name + ' should not have a "prototype" property'); 1.1294 + return; 1.1295 + } 1.1296 + 1.1297 + assert_own_property(window[this.name], "prototype", 1.1298 + 'interface "' + this.name + '" does not have own property "prototype"'); 1.1299 + 1.1300 + // "If the [NoInterfaceObject] extended attribute was not specified on 1.1301 + // the interface, then the interface prototype object must also have a 1.1302 + // property named “constructor” with attributes { [[Writable]]: true, 1.1303 + // [[Enumerable]]: false, [[Configurable]]: true } whose value is a 1.1304 + // reference to the interface object for the interface." 1.1305 + assert_own_property(window[this.name].prototype, "constructor", 1.1306 + this.name + '.prototype does not have own property "constructor"'); 1.1307 + var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, "constructor"); 1.1308 + assert_false("get" in desc, this.name + ".prototype.constructor has getter"); 1.1309 + assert_false("set" in desc, this.name + ".prototype.constructor has setter"); 1.1310 + assert_true(desc.writable, this.name + ".prototype.constructor is not writable"); 1.1311 + assert_false(desc.enumerable, this.name + ".prototype.constructor is enumerable"); 1.1312 + assert_true(desc.configurable, this.name + ".prototype.constructor in not configurable"); 1.1313 + assert_equals(window[this.name].prototype.constructor, window[this.name], 1.1314 + this.name + '.prototype.constructor is not the same object as ' + this.name); 1.1315 + }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property'); 1.1316 +}; 1.1317 + 1.1318 +//@} 1.1319 +IdlInterface.prototype.test_members = function() 1.1320 +//@{ 1.1321 +{ 1.1322 + for (var i = 0; i < this.members.length; i++) 1.1323 + { 1.1324 + var member = this.members[i]; 1.1325 + if (member.untested) 1.1326 + { 1.1327 + continue; 1.1328 + } 1.1329 + if (member.type == "const") 1.1330 + { 1.1331 + test(function() 1.1332 + { 1.1333 + assert_own_property(window, this.name, 1.1334 + "window does not have own property " + format_value(this.name)); 1.1335 + 1.1336 + // "For each constant defined on an interface A, there must be 1.1337 + // a corresponding property on the interface object, if it 1.1338 + // exists." 1.1339 + assert_own_property(window[this.name], member.name); 1.1340 + // "The value of the property is that which is obtained by 1.1341 + // converting the constant’s IDL value to an ECMAScript 1.1342 + // value." 1.1343 + assert_equals(window[this.name][member.name], constValue(member.value), 1.1344 + "property has wrong value"); 1.1345 + // "The property has attributes { [[Writable]]: false, 1.1346 + // [[Enumerable]]: true, [[Configurable]]: false }." 1.1347 + var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name); 1.1348 + assert_false("get" in desc, "property has getter"); 1.1349 + assert_false("set" in desc, "property has setter"); 1.1350 + assert_false(desc.writable, "property is writable"); 1.1351 + assert_true(desc.enumerable, "property is not enumerable"); 1.1352 + assert_false(desc.configurable, "property is configurable"); 1.1353 + }.bind(this), this.name + " interface: constant " + member.name + " on interface object"); 1.1354 + // "In addition, a property with the same characteristics must 1.1355 + // exist on the interface prototype object." 1.1356 + test(function() 1.1357 + { 1.1358 + assert_own_property(window, this.name, 1.1359 + "window does not have own property " + format_value(this.name)); 1.1360 + 1.1361 + if (this.has_extended_attribute("Callback")) { 1.1362 + assert_false("prototype" in window[this.name], 1.1363 + this.name + ' should not have a "prototype" property'); 1.1364 + return; 1.1365 + } 1.1366 + 1.1367 + assert_own_property(window[this.name], "prototype", 1.1368 + 'interface "' + this.name + '" does not have own property "prototype"'); 1.1369 + 1.1370 + assert_own_property(window[this.name].prototype, member.name); 1.1371 + assert_equals(window[this.name].prototype[member.name], constValue(member.value), 1.1372 + "property has wrong value"); 1.1373 + var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name); 1.1374 + assert_false("get" in desc, "property has getter"); 1.1375 + assert_false("set" in desc, "property has setter"); 1.1376 + assert_false(desc.writable, "property is writable"); 1.1377 + assert_true(desc.enumerable, "property is not enumerable"); 1.1378 + assert_false(desc.configurable, "property is configurable"); 1.1379 + }.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object"); 1.1380 + } 1.1381 + else if (member.type == "attribute") 1.1382 + { 1.1383 + if (member.has_extended_attribute("Unforgeable")) 1.1384 + { 1.1385 + // We do the checks in test_interface_of instead 1.1386 + continue; 1.1387 + } 1.1388 + test(function() 1.1389 + { 1.1390 + assert_own_property(window, this.name, 1.1391 + "window does not have own property " + format_value(this.name)); 1.1392 + assert_own_property(window[this.name], "prototype", 1.1393 + 'interface "' + this.name + '" does not have own property "prototype"'); 1.1394 + 1.1395 + if (member["static"]) { 1.1396 + assert_own_property(window[this.name], member.name, 1.1397 + "The interface object must have a property " + 1.1398 + format_value(member.name)); 1.1399 + } 1.1400 + else 1.1401 + { 1.1402 + assert_true(member.name in window[this.name].prototype, 1.1403 + "The prototype object must have a property " + 1.1404 + format_value(member.name)); 1.1405 + 1.1406 + // TODO: Needs to test for LenientThis. 1.1407 + assert_throws(new TypeError(), function() { 1.1408 + window[this.name].prototype[member.name]; 1.1409 + }.bind(this), "getting property on prototype object must throw TypeError"); 1.1410 + do_interface_attribute_asserts(window[this.name].prototype, member); 1.1411 + } 1.1412 + }.bind(this), this.name + " interface: attribute " + member.name); 1.1413 + } 1.1414 + else if (member.type == "operation") 1.1415 + { 1.1416 + // TODO: Need to correctly handle multiple operations with the same 1.1417 + // identifier. 1.1418 + if (!member.name) 1.1419 + { 1.1420 + // Unnamed getter or such 1.1421 + continue; 1.1422 + } 1.1423 + test(function() 1.1424 + { 1.1425 + assert_own_property(window, this.name, 1.1426 + "window does not have own property " + format_value(this.name)); 1.1427 + 1.1428 + if (this.has_extended_attribute("Callback")) { 1.1429 + assert_false("prototype" in window[this.name], 1.1430 + this.name + ' should not have a "prototype" property'); 1.1431 + return; 1.1432 + } 1.1433 + 1.1434 + assert_own_property(window[this.name], "prototype", 1.1435 + 'interface "' + this.name + '" does not have own property "prototype"'); 1.1436 + 1.1437 + // "For each unique identifier of an operation defined on the 1.1438 + // interface, there must be a corresponding property on the 1.1439 + // interface prototype object (if it is a regular operation) or 1.1440 + // the interface object (if it is a static operation), unless 1.1441 + // the effective overload set for that identifier and operation 1.1442 + // and with an argument count of 0 (for the ECMAScript language 1.1443 + // binding) has no entries." 1.1444 + // 1.1445 + var prototypeOrInterfaceObject; 1.1446 + if (member["static"]) { 1.1447 + assert_own_property(window[this.name], member.name, 1.1448 + "interface prototype object missing static operation"); 1.1449 + prototypeOrInterfaceObject = window[this.name]; 1.1450 + } 1.1451 + else 1.1452 + { 1.1453 + assert_own_property(window[this.name].prototype, member.name, 1.1454 + "interface prototype object missing non-static operation"); 1.1455 + prototypeOrInterfaceObject = window[this.name].prototype; 1.1456 + } 1.1457 + 1.1458 + var desc = Object.getOwnPropertyDescriptor(prototypeOrInterfaceObject, member.name); 1.1459 + // "The property has attributes { [[Writable]]: true, 1.1460 + // [[Enumerable]]: true, [[Configurable]]: true }." 1.1461 + assert_false("get" in desc, "property has getter"); 1.1462 + assert_false("set" in desc, "property has setter"); 1.1463 + assert_true(desc.writable, "property is not writable"); 1.1464 + assert_true(desc.enumerable, "property is not enumerable"); 1.1465 + assert_true(desc.configurable, "property is not configurable"); 1.1466 + // "The value of the property is a Function object whose 1.1467 + // behavior is as follows . . ." 1.1468 + assert_equals(typeof prototypeOrInterfaceObject[member.name], "function", 1.1469 + "property must be a function"); 1.1470 + // "The value of the Function object’s “length” property is 1.1471 + // a Number determined as follows: 1.1472 + // ". . . 1.1473 + // "Return the length of the shortest argument list of the 1.1474 + // entries in S." 1.1475 + // 1.1476 + // TODO: Doesn't handle overloading or variadic arguments. 1.1477 + assert_equals(prototypeOrInterfaceObject[member.name].length, 1.1478 + member.arguments.filter(function(arg) { 1.1479 + return !arg.optional; 1.1480 + }).length, 1.1481 + "property has wrong .length"); 1.1482 + 1.1483 + // Make some suitable arguments 1.1484 + var args = member.arguments.map(function(arg) { 1.1485 + return create_suitable_object(arg.idlType); 1.1486 + }); 1.1487 + 1.1488 + // "Let O be a value determined as follows: 1.1489 + // ". . . 1.1490 + // "Otherwise, throw a TypeError." 1.1491 + // This should be hit if the operation is not static, there is 1.1492 + // no [ImplicitThis] attribute, and the this value is null. 1.1493 + // 1.1494 + // TODO: We currently ignore the [ImplicitThis] case. 1.1495 + if (!member["static"]) { 1.1496 + assert_throws(new TypeError(), function() { 1.1497 + window[this.name].prototype[member.name].apply(null, args); 1.1498 + }, "calling operation with this = null didn't throw TypeError"); 1.1499 + } 1.1500 + 1.1501 + // ". . . If O is not null and is also not a platform object 1.1502 + // that implements interface I, throw a TypeError." 1.1503 + // 1.1504 + // TODO: Test a platform object that implements some other 1.1505 + // interface. (Have to be sure to get inheritance right.) 1.1506 + assert_throws(new TypeError(), function() { 1.1507 + window[this.name].prototype[member.name].apply({}, args); 1.1508 + }, "calling operation with this = {} didn't throw TypeError"); 1.1509 + }.bind(this), this.name + " interface: operation " + member.name + 1.1510 + "(" + member.arguments.map(function(m) { return m.idlType.idlType; }) + 1.1511 + ")"); 1.1512 + } 1.1513 + // TODO: check more member types, like stringifier 1.1514 + } 1.1515 +}; 1.1516 + 1.1517 +//@} 1.1518 +IdlInterface.prototype.test_object = function(desc) 1.1519 +//@{ 1.1520 +{ 1.1521 + var obj, exception = null; 1.1522 + try 1.1523 + { 1.1524 + obj = eval(desc); 1.1525 + } 1.1526 + catch(e) 1.1527 + { 1.1528 + exception = e; 1.1529 + } 1.1530 + 1.1531 + // TODO: WebIDLParser doesn't currently support named legacycallers, so I'm 1.1532 + // not sure what those would look like in the AST 1.1533 + var expected_typeof = this.members.some(function(member) 1.1534 + { 1.1535 + return member.legacycaller 1.1536 + || ("idlType" in member && member.idlType.legacycaller) 1.1537 + || ("idlType" in member && typeof member.idlType == "object" 1.1538 + && "idlType" in member.idlType && member.idlType.idlType == "legacycaller"); 1.1539 + }) ? "function" : "object"; 1.1540 + 1.1541 + this.test_primary_interface_of(desc, obj, exception, expected_typeof); 1.1542 + var current_interface = this; 1.1543 + while (current_interface) 1.1544 + { 1.1545 + if (!(current_interface.name in this.array.members)) 1.1546 + { 1.1547 + throw "Interface " + current_interface.name + " not found (inherited by " + this.name + ")"; 1.1548 + } 1.1549 + if (current_interface.prevent_multiple_testing && current_interface.already_tested) 1.1550 + { 1.1551 + return; 1.1552 + } 1.1553 + current_interface.test_interface_of(desc, obj, exception, expected_typeof); 1.1554 + current_interface = this.array.members[current_interface.base]; 1.1555 + } 1.1556 +}; 1.1557 + 1.1558 +//@} 1.1559 +IdlInterface.prototype.test_primary_interface_of = function(desc, obj, exception, expected_typeof) 1.1560 +//@{ 1.1561 +{ 1.1562 + // We can't easily test that its prototype is correct if there's no 1.1563 + // interface object, or the object is from a different global environment 1.1564 + // (not instanceof Object). TODO: test in this case that its prototype at 1.1565 + // least looks correct, even if we can't test that it's actually correct. 1.1566 + if (!this.has_extended_attribute("NoInterfaceObject") 1.1567 + && (typeof obj != expected_typeof || obj instanceof Object)) 1.1568 + { 1.1569 + test(function() 1.1570 + { 1.1571 + assert_equals(exception, null, "Unexpected exception when evaluating object"); 1.1572 + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); 1.1573 + assert_own_property(window, this.name, 1.1574 + "window does not have own property " + format_value(this.name)); 1.1575 + assert_own_property(window[this.name], "prototype", 1.1576 + 'interface "' + this.name + '" does not have own property "prototype"'); 1.1577 + 1.1578 + // "The value of the internal [[Prototype]] property of the 1.1579 + // platform object is the interface prototype object of the primary 1.1580 + // interface from the platform object’s associated global 1.1581 + // environment." 1.1582 + assert_equals(Object.getPrototypeOf(obj), 1.1583 + window[this.name].prototype, 1.1584 + desc + "'s prototype is not " + this.name + ".prototype"); 1.1585 + }.bind(this), this.name + " must be primary interface of " + desc); 1.1586 + } 1.1587 + 1.1588 + // "The class string of a platform object that implements one or more 1.1589 + // interfaces must be the identifier of the primary interface of the 1.1590 + // platform object." 1.1591 + test(function() 1.1592 + { 1.1593 + assert_equals(exception, null, "Unexpected exception when evaluating object"); 1.1594 + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); 1.1595 + assert_class_string(obj, this.name, "class string of " + desc); 1.1596 + if (!this.has_stringifier()) 1.1597 + { 1.1598 + assert_equals(String(obj), "[object " + this.name + "]", "String(" + desc + ")"); 1.1599 + } 1.1600 + }.bind(this), "Stringification of " + desc); 1.1601 +}; 1.1602 + 1.1603 +//@} 1.1604 +IdlInterface.prototype.test_interface_of = function(desc, obj, exception, expected_typeof) 1.1605 +//@{ 1.1606 +{ 1.1607 + // TODO: Indexed and named properties, more checks on interface members 1.1608 + this.already_tested = true; 1.1609 + 1.1610 + for (var i = 0; i < this.members.length; i++) 1.1611 + { 1.1612 + var member = this.members[i]; 1.1613 + if (member.has_extended_attribute("Unforgeable")) 1.1614 + { 1.1615 + test(function() 1.1616 + { 1.1617 + assert_equals(exception, null, "Unexpected exception when evaluating object"); 1.1618 + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); 1.1619 + do_interface_attribute_asserts(obj, member); 1.1620 + }.bind(this), this.name + " interface: " + desc + ' must have own property "' + member.name + '"'); 1.1621 + } 1.1622 + else if ((member.type == "const" 1.1623 + || member.type == "attribute" 1.1624 + || member.type == "operation") 1.1625 + && member.name) 1.1626 + { 1.1627 + test(function() 1.1628 + { 1.1629 + assert_equals(exception, null, "Unexpected exception when evaluating object"); 1.1630 + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); 1.1631 + if (!member["static"]) { 1.1632 + assert_inherits(obj, member.name); 1.1633 + if (member.type == "const") 1.1634 + { 1.1635 + assert_equals(obj[member.name], constValue(member.value)); 1.1636 + } 1.1637 + if (member.type == "attribute") 1.1638 + { 1.1639 + // Attributes are accessor properties, so they might 1.1640 + // legitimately throw an exception rather than returning 1.1641 + // anything. 1.1642 + var property, thrown = false; 1.1643 + try 1.1644 + { 1.1645 + property = obj[member.name]; 1.1646 + } 1.1647 + catch (e) 1.1648 + { 1.1649 + thrown = true; 1.1650 + } 1.1651 + if (!thrown) 1.1652 + { 1.1653 + this.array.assert_type_is(property, member.idlType); 1.1654 + } 1.1655 + } 1.1656 + if (member.type == "operation") 1.1657 + { 1.1658 + assert_equals(typeof obj[member.name], "function"); 1.1659 + } 1.1660 + } 1.1661 + }.bind(this), this.name + " interface: " + desc + ' must inherit property "' + member.name + '" with the proper type (' + i + ')'); 1.1662 + } 1.1663 + // TODO: This is wrong if there are multiple operations with the same 1.1664 + // identifier. 1.1665 + // TODO: Test passing arguments of the wrong type. 1.1666 + if (member.type == "operation" && member.name && member.arguments.length) 1.1667 + { 1.1668 + test(function() 1.1669 + { 1.1670 + assert_equals(exception, null, "Unexpected exception when evaluating object"); 1.1671 + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); 1.1672 + if (!member["static"]) { 1.1673 + assert_inherits(obj, member.name); 1.1674 + } 1.1675 + else 1.1676 + { 1.1677 + assert_false(member.name in obj); 1.1678 + } 1.1679 + var args = []; 1.1680 + for (var i = 0; i < member.arguments.length; i++) 1.1681 + { 1.1682 + if (member.arguments[i].optional) 1.1683 + { 1.1684 + break; 1.1685 + } 1.1686 + assert_throws(new TypeError(), function() 1.1687 + { 1.1688 + obj[member.name].apply(obj, args); 1.1689 + }.bind(this), "Called with " + i + " arguments"); 1.1690 + 1.1691 + args.push(create_suitable_object(member.arguments[i].idlType)); 1.1692 + } 1.1693 + }.bind(this), this.name + " interface: calling " + member.name + 1.1694 + "(" + member.arguments.map(function(m) { return m.idlType.idlType; }) + 1.1695 + ") on " + desc + " with too few arguments must throw TypeError"); 1.1696 + } 1.1697 + } 1.1698 +}; 1.1699 + 1.1700 +//@} 1.1701 +IdlInterface.prototype.has_stringifier = function() 1.1702 +//@{ 1.1703 +{ 1.1704 + if (this.members.some(function(member) { return member.stringifier; })) { 1.1705 + return true; 1.1706 + } 1.1707 + if (this.base && 1.1708 + this.array.members[this.base].has_stringifier()) { 1.1709 + return true; 1.1710 + } 1.1711 + return false; 1.1712 +}; 1.1713 + 1.1714 +//@} 1.1715 +function do_interface_attribute_asserts(obj, member) 1.1716 +//@{ 1.1717 +{ 1.1718 + // "For each attribute defined on the interface, there must exist a 1.1719 + // corresponding property. If the attribute was declared with the 1.1720 + // [Unforgeable] extended attribute, then the property exists on every 1.1721 + // object that implements the interface. Otherwise, it exists on the 1.1722 + // interface’s interface prototype object." 1.1723 + // 1.1724 + // This is called by test_self() with the prototype as obj, and by 1.1725 + // test_interface_of() with the object as obj. 1.1726 + assert_own_property(obj, member.name); 1.1727 + 1.1728 + // "The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]: 1.1729 + // true, [[Configurable]]: configurable }, where: 1.1730 + // "configurable is false if the attribute was declared with the 1.1731 + // [Unforgeable] extended attribute and true otherwise; 1.1732 + // "G is the attribute getter, defined below; and 1.1733 + // "S is the attribute setter, also defined below." 1.1734 + var desc = Object.getOwnPropertyDescriptor(obj, member.name); 1.1735 + assert_false("value" in desc, 'property descriptor has value but is supposed to be accessor'); 1.1736 + assert_false("writable" in desc, 'property descriptor has "writable" field but is supposed to be accessor'); 1.1737 + assert_true(desc.enumerable, "property is not enumerable"); 1.1738 + if (member.has_extended_attribute("Unforgeable")) 1.1739 + { 1.1740 + assert_false(desc.configurable, "[Unforgeable] property must not be configurable"); 1.1741 + } 1.1742 + else 1.1743 + { 1.1744 + assert_true(desc.configurable, "property must be configurable"); 1.1745 + } 1.1746 + 1.1747 + // "The attribute getter is a Function object whose behavior when invoked 1.1748 + // is as follows: 1.1749 + // "... 1.1750 + // "The value of the Function object’s “length” property is the Number 1.1751 + // value 0." 1.1752 + assert_equals(typeof desc.get, "function", "getter must be Function"); 1.1753 + assert_equals(desc.get.length, 0, "getter length must be 0"); 1.1754 + // TODO: Account for LenientThis 1.1755 + assert_throws(new TypeError(), function() 1.1756 + { 1.1757 + desc.get.call({}); 1.1758 + }.bind(this), "calling getter on wrong object type must throw TypeError"); 1.1759 + 1.1760 + // TODO: Test calling setter on the interface prototype (should throw 1.1761 + // TypeError in most cases). 1.1762 + // 1.1763 + // "The attribute setter is undefined if the attribute is declared readonly 1.1764 + // and has neither a [PutForwards] nor a [Replaceable] extended attribute 1.1765 + // declared on it. Otherwise, it is a Function object whose behavior when 1.1766 + // invoked is as follows: 1.1767 + // "... 1.1768 + // "The value of the Function object’s “length” property is the Number 1.1769 + // value 1." 1.1770 + if (member.readonly 1.1771 + && !member.has_extended_attribute("PutForwards") 1.1772 + && !member.has_extended_attribute("Replaceable")) 1.1773 + { 1.1774 + assert_equals(desc.set, undefined, "setter must be undefined for readonly attributes"); 1.1775 + } 1.1776 + else 1.1777 + { 1.1778 + assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes"); 1.1779 + assert_equals(desc.set.length, 1, "setter length must be 1"); 1.1780 + } 1.1781 +} 1.1782 +//@} 1.1783 + 1.1784 +/// IdlInterfaceMember /// 1.1785 +function IdlInterfaceMember(obj) 1.1786 +//@{ 1.1787 +{ 1.1788 + /** 1.1789 + * obj is an object produced by the WebIDLParser.js "ifMember" production. 1.1790 + * We just forward all properties to this object without modification, 1.1791 + * except for special extAttrs handling. 1.1792 + */ 1.1793 + for (var k in obj) 1.1794 + { 1.1795 + this[k] = obj[k]; 1.1796 + } 1.1797 + if (!("extAttrs" in this)) 1.1798 + { 1.1799 + this.extAttrs = []; 1.1800 + } 1.1801 +} 1.1802 + 1.1803 +//@} 1.1804 +IdlInterfaceMember.prototype = Object.create(IdlObject.prototype); 1.1805 + 1.1806 +/// Internal helper functions /// 1.1807 +function create_suitable_object(type) 1.1808 +//@{ 1.1809 +{ 1.1810 + /** 1.1811 + * type is an object produced by the WebIDLParser.js "type" production. We 1.1812 + * return a JavaScript value that matches the type, if we can figure out 1.1813 + * how. 1.1814 + */ 1.1815 + if (type.nullable) 1.1816 + { 1.1817 + return null; 1.1818 + } 1.1819 + switch (type.idlType) 1.1820 + { 1.1821 + case "any": 1.1822 + case "boolean": 1.1823 + return true; 1.1824 + 1.1825 + case "byte": case "octet": case "short": case "unsigned short": 1.1826 + case "long": case "unsigned long": case "long long": 1.1827 + case "unsigned long long": case "float": case "double": 1.1828 + case "unrestricted float": case "unrestricted double": 1.1829 + return 7; 1.1830 + 1.1831 + case "DOMString": 1.1832 + return "foo"; 1.1833 + 1.1834 + case "object": 1.1835 + return {a: "b"}; 1.1836 + 1.1837 + case "Node": 1.1838 + return document.createTextNode("abc"); 1.1839 + } 1.1840 + return null; 1.1841 +} 1.1842 +//@} 1.1843 + 1.1844 +/// IdlEnum /// 1.1845 +// Used for IdlArray.prototype.assert_type_is 1.1846 +function IdlEnum(obj) 1.1847 +//@{ 1.1848 +{ 1.1849 + /** 1.1850 + * obj is an object produced by the WebIDLParser.js "dictionary" 1.1851 + * production. 1.1852 + */ 1.1853 + 1.1854 + /** Self-explanatory. */ 1.1855 + this.name = obj.name; 1.1856 + 1.1857 + /** An array of values produced by the "enum" production. */ 1.1858 + this.values = obj.values; 1.1859 + 1.1860 +} 1.1861 +//@} 1.1862 + 1.1863 +IdlEnum.prototype = Object.create(IdlObject.prototype); 1.1864 + 1.1865 +/// IdlTypedef /// 1.1866 +// Used for IdlArray.prototype.assert_type_is 1.1867 +function IdlTypedef(obj) 1.1868 +//@{ 1.1869 +{ 1.1870 + /** 1.1871 + * obj is an object produced by the WebIDLParser.js "typedef" 1.1872 + * production. 1.1873 + */ 1.1874 + 1.1875 + /** Self-explanatory. */ 1.1876 + this.name = obj.name; 1.1877 + 1.1878 + /** An array of values produced by the "typedef" production. */ 1.1879 + this.values = obj.values; 1.1880 + 1.1881 +} 1.1882 +//@} 1.1883 + 1.1884 +IdlTypedef.prototype = Object.create(IdlObject.prototype); 1.1885 + 1.1886 +}()); 1.1887 +// vim: set expandtab shiftwidth=4 tabstop=4 foldmarker=@{,@} foldmethod=marker: