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