dom/imptests/idlharness.js

changeset 0
6474c204b198
     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:

mercurial