author | Ms2ger <ms2ger@gmail.com> |
Sat, 20 Jun 2015 09:16:51 +0200 | |
changeset 280648 | caabf5d3e1bd17762bb6acaf940f9496a1ceb400 |
parent 280647 | fe94dced5e595db6b1e8aaa824583a31e6b05355 |
child 280649 | ea5b6a488b421af3eac319d095c233644105f0c5 |
push id | 4932 |
push user | jlund@mozilla.com |
push date | Mon, 10 Aug 2015 18:23:06 +0000 |
treeherder | mozilla-beta@6dd5a4f5f745 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jgraham |
bugs | 1173288 |
milestone | 41.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/imptests/WebIDLParser.js +++ b/dom/imptests/WebIDLParser.js @@ -915,10 +915,10 @@ parse: function (str, opt) { if (!opt) opt = {}; var tokens = tokenise(str); return parse(tokens, opt); } }; if (inNode) module.exports = obj; - else window.WebIDL2 = obj; + else self.WebIDL2 = obj; }());
--- a/dom/imptests/failures/html/dom/test_interfaces.html.json +++ b/dom/imptests/failures/html/dom/test_interfaces.html.json @@ -1,42 +1,35 @@ { "DOMException exception: existence and properties of exception interface prototype object's \"name\" property": true, - "CustomEvent interface: existence and properties of interface object": true, "EventListener interface: existence and properties of interface prototype object": true, "EventListener interface: existence and properties of interface prototype object's \"constructor\" property": true, "EventListener interface: operation handleEvent(Event)": true, "MutationObserver interface: operation observe(Node,MutationObserverInit)": true, - "Node interface: existence and properties of interface object": true, - "Document interface: existence and properties of interface object": true, "Document interface: operation prepend([object Object],[object Object])": true, "Document interface: operation append([object Object],[object Object])": true, - "XMLDocument interface: existence and properties of interface object": true, "Document interface: xmlDoc must inherit property \"prepend\" with the proper type (28)": true, "Document interface: calling prepend([object Object],[object Object]) on xmlDoc with too few arguments must throw TypeError": true, "Document interface: xmlDoc must inherit property \"append\" with the proper type (29)": true, "Document interface: calling append([object Object],[object Object]) on xmlDoc with too few arguments must throw TypeError": true, - "DocumentFragment interface: existence and properties of interface object": true, "DocumentFragment interface: operation prepend([object Object],[object Object])": true, "DocumentFragment interface: operation append([object Object],[object Object])": true, "DocumentFragment interface: document.createDocumentFragment() must inherit property \"prepend\" with the proper type (4)": true, "DocumentFragment interface: calling prepend([object Object],[object Object]) on document.createDocumentFragment() with too few arguments must throw TypeError": true, "DocumentFragment interface: document.createDocumentFragment() must inherit property \"append\" with the proper type (5)": true, "DocumentFragment interface: calling append([object Object],[object Object]) on document.createDocumentFragment() with too few arguments must throw TypeError": true, - "DocumentType interface: existence and properties of interface object": true, "DocumentType interface: operation before([object Object],[object Object])": true, "DocumentType interface: operation after([object Object],[object Object])": true, "DocumentType interface: operation replace([object Object],[object Object])": true, "DocumentType interface: document.doctype must inherit property \"before\" with the proper type (3)": true, "DocumentType interface: calling before([object Object],[object Object]) on document.doctype with too few arguments must throw TypeError": true, "DocumentType interface: document.doctype must inherit property \"after\" with the proper type (4)": true, "DocumentType interface: calling after([object Object],[object Object]) on document.doctype with too few arguments must throw TypeError": true, "DocumentType interface: document.doctype must inherit property \"replace\" with the proper type (5)": true, "DocumentType interface: calling replace([object Object],[object Object]) on document.doctype with too few arguments must throw TypeError": true, - "Element interface: existence and properties of interface object": true, "Element interface: attribute namespaceURI": true, "Element interface: attribute prefix": true, "Element interface: attribute localName": true, "Element interface: operation prepend([object Object],[object Object])": true, "Element interface: operation append([object Object],[object Object])": true, "Element interface: operation before([object Object],[object Object])": true, "Element interface: operation after([object Object],[object Object])": true, "Element interface: operation replace([object Object],[object Object])": true, @@ -47,41 +40,53 @@ "Element interface: element must inherit property \"before\" with the proper type (25)": true, "Element interface: calling before([object Object],[object Object]) on element with too few arguments must throw TypeError": true, "Element interface: element must inherit property \"after\" with the proper type (26)": true, "Element interface: calling after([object Object],[object Object]) on element with too few arguments must throw TypeError": true, "Element interface: element must inherit property \"replace\" with the proper type (27)": true, "Element interface: calling replace([object Object],[object Object]) on element with too few arguments must throw TypeError": true, "Attr interface: existence and properties of interface object": true, "Attr interface: existence and properties of interface prototype object": true, - "CharacterData interface: existence and properties of interface object": true, "CharacterData interface: operation before([object Object],[object Object])": true, "CharacterData interface: operation after([object Object],[object Object])": true, "CharacterData interface: operation replace([object Object],[object Object])": true, - "Text interface: existence and properties of interface object": true, "CharacterData interface: document.createTextNode(\"abc\") must inherit property \"before\" with the proper type (7)": true, "CharacterData interface: calling before([object Object],[object Object]) on document.createTextNode(\"abc\") with too few arguments must throw TypeError": true, "CharacterData interface: document.createTextNode(\"abc\") must inherit property \"after\" with the proper type (8)": true, "CharacterData interface: calling after([object Object],[object Object]) on document.createTextNode(\"abc\") with too few arguments must throw TypeError": true, "CharacterData interface: document.createTextNode(\"abc\") must inherit property \"replace\" with the proper type (9)": true, "CharacterData interface: calling replace([object Object],[object Object]) on document.createTextNode(\"abc\") with too few arguments must throw TypeError": true, - "ProcessingInstruction interface: existence and properties of interface object": true, "CharacterData interface: xmlDoc.createProcessingInstruction(\"abc\", \"def\") must inherit property \"before\" with the proper type (7)": true, "CharacterData interface: calling before([object Object],[object Object]) on xmlDoc.createProcessingInstruction(\"abc\", \"def\") with too few arguments must throw TypeError": true, "CharacterData interface: xmlDoc.createProcessingInstruction(\"abc\", \"def\") must inherit property \"after\" with the proper type (8)": true, "CharacterData interface: calling after([object Object],[object Object]) on xmlDoc.createProcessingInstruction(\"abc\", \"def\") with too few arguments must throw TypeError": true, "CharacterData interface: xmlDoc.createProcessingInstruction(\"abc\", \"def\") must inherit property \"replace\" with the proper type (9)": true, "CharacterData interface: calling replace([object Object],[object Object]) on xmlDoc.createProcessingInstruction(\"abc\", \"def\") with too few arguments must throw TypeError": true, - "Comment interface: existence and properties of interface object": true, "CharacterData interface: document.createComment(\"abc\") must inherit property \"before\" with the proper type (7)": true, "CharacterData interface: calling before([object Object],[object Object]) on document.createComment(\"abc\") with too few arguments must throw TypeError": true, "CharacterData interface: document.createComment(\"abc\") must inherit property \"after\" with the proper type (8)": true, "CharacterData interface: calling after([object Object],[object Object]) on document.createComment(\"abc\") with too few arguments must throw TypeError": true, "CharacterData interface: document.createComment(\"abc\") must inherit property \"replace\" with the proper type (9)": true, "CharacterData interface: calling replace([object Object],[object Object]) on document.createComment(\"abc\") with too few arguments must throw TypeError": true, - "NodeFilter interface: existence and properties of interface object": true, - "NodeList interface: existence and properties of interface prototype object": true, - "DOMTokenList interface: operation add(DOMString)": true, - "DOMTokenList interface: operation remove(DOMString)": true, - "DOMTokenList interface: calling add(DOMString) on document.body.classList with too few arguments must throw TypeError": true, - "DOMTokenList interface: calling remove(DOMString) on document.body.classList with too few arguments must throw TypeError": true, - "DOMSettableTokenList interface: existence and properties of interface object": true + "NodeFilter interface: existence and properties of interface prototype object": true, + "NodeFilter interface: existence and properties of interface prototype object": true, + "NodeFilter interface: existence and properties of interface prototype object's \"constructor\" property": true, + "EventListener interface: existence and properties of interface object": true, + "EventListener interface object length": true, + "NodeFilter interface: constant FILTER_ACCEPT on interface prototype object": true, + "NodeFilter interface: constant FILTER_REJECT on interface prototype object": true, + "NodeFilter interface: constant FILTER_SKIP on interface prototype object": true, + "NodeFilter interface: constant SHOW_ALL on interface prototype object": true, + "NodeFilter interface: constant SHOW_ELEMENT on interface prototype object": true, + "NodeFilter interface: constant SHOW_ATTRIBUTE on interface prototype object": true, + "NodeFilter interface: constant SHOW_TEXT on interface prototype object": true, + "NodeFilter interface: constant SHOW_CDATA_SECTION on interface prototype object": true, + "NodeFilter interface: constant SHOW_ENTITY_REFERENCE on interface prototype object": true, + "NodeFilter interface: constant SHOW_ENTITY on interface prototype object": true, + "NodeFilter interface: constant SHOW_PROCESSING_INSTRUCTION on interface prototype object": true, + "NodeFilter interface: constant SHOW_COMMENT on interface prototype object": true, + "NodeFilter interface: constant SHOW_DOCUMENT on interface prototype object": true, + "NodeFilter interface: constant SHOW_DOCUMENT_TYPE on interface prototype object": true, + "NodeFilter interface: constant SHOW_DOCUMENT_FRAGMENT on interface prototype object": true, + "NodeFilter interface: constant SHOW_NOTATION on interface prototype object": true, + "NodeFilter interface: operation acceptNode(Node)": true, + "NodeList interface: existence and properties of interface prototype object": true }
deleted file mode 100644 --- a/dom/imptests/failures/webapps/XMLHttpRequest/tests/submissions/Ms2ger/mochitest.ini +++ /dev/null @@ -1,6 +0,0 @@ -# THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT -[DEFAULT] -support-files = - - -[test_interfaces.html.json]
deleted file mode 100644 --- a/dom/imptests/failures/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_interfaces.html.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "XMLHttpRequestUpload interface: existence and properties of interface object": true, - "XMLHttpRequest interface: existence and properties of interface object": true -}
--- a/dom/imptests/html/dom/test_interfaces.html +++ b/dom/imptests/html/dom/test_interfaces.html @@ -5,45 +5,16 @@ <script src=/resources/testharnessreport.js></script> <script src=/resources/WebIDLParser.js></script> <script src=/resources/idlharness.js></script> <h1>DOM IDL tests</h1> <div id=log></div> <script type=text/plain> -exception DOMException { - const unsigned short INDEX_SIZE_ERR = 1; - const unsigned short DOMSTRING_SIZE_ERR = 2; // historical - const unsigned short HIERARCHY_REQUEST_ERR = 3; - const unsigned short WRONG_DOCUMENT_ERR = 4; - const unsigned short INVALID_CHARACTER_ERR = 5; - const unsigned short NO_DATA_ALLOWED_ERR = 6; // historical - const unsigned short NO_MODIFICATION_ALLOWED_ERR = 7; - const unsigned short NOT_FOUND_ERR = 8; - const unsigned short NOT_SUPPORTED_ERR = 9; - const unsigned short INUSE_ATTRIBUTE_ERR = 10; // historical - const unsigned short INVALID_STATE_ERR = 11; - const unsigned short SYNTAX_ERR = 12; - const unsigned short INVALID_MODIFICATION_ERR = 13; - const unsigned short NAMESPACE_ERR = 14; - const unsigned short INVALID_ACCESS_ERR = 15; - const unsigned short VALIDATION_ERR = 16; // historical - const unsigned short TYPE_MISMATCH_ERR = 17; // historical; use TypeError instead - const unsigned short SECURITY_ERR = 18; - const unsigned short NETWORK_ERR = 19; - const unsigned short ABORT_ERR = 20; - const unsigned short URL_MISMATCH_ERR = 21; - const unsigned short QUOTA_EXCEEDED_ERR = 22; - const unsigned short TIMEOUT_ERR = 23; - const unsigned short INVALID_NODE_TYPE_ERR = 24; - const unsigned short DATA_CLONE_ERR = 25; - unsigned short code; -}; - [Constructor(DOMString name)] interface DOMError { readonly attribute DOMString name; }; [Constructor(DOMString type, optional EventInit eventInitDict)] interface Event { readonly attribute DOMString type; @@ -446,30 +417,28 @@ interface DOMTokenList { }; interface DOMSettableTokenList : DOMTokenList { attribute DOMString value; }; </script> <script> "use strict"; -var xmlDoc, domException, detachedRange, element; +var xmlDoc, detachedRange, element; var idlArray; setup(function() { xmlDoc = document.implementation.createDocument(null, "", null); - try { document.appendChild(document); } catch(e) { domException = e; } detachedRange = document.createRange(); detachedRange.detach(); element = xmlDoc.createElementNS(null, "test"); element.setAttribute("bar", "baz"); idlArray = new IdlArray(); idlArray.add_idls(document.querySelector("script[type=text\\/plain]").textContent); idlArray.add_objects({ - DOMException: ['domException'], Event: ['document.createEvent("Event")', 'new Event("foo")'], CustomEvent: ['new CustomEvent("foo")'], XMLDocument: ['xmlDoc'], DOMImplementation: ['document.implementation'], DocumentFragment: ['document.createDocumentFragment()'], DocumentType: ['document.doctype'], Element: ['element'], Attr: ['document.querySelector("[id]").attributes[0]'],
--- a/dom/imptests/idlharness.js +++ b/dom/imptests/idlharness.js @@ -51,19 +51,32 @@ policies and contribution forms [3]. /// Helpers /// function constValue (cnt) { if (cnt.type === "null") return null; if (cnt.type === "NaN") return NaN; if (cnt.type === "Infinity") return cnt.negative ? -Infinity : Infinity; return cnt.value; } +function minOverloadLength(overloads) { + if (!overloads.length) { + return 0; + } + + return overloads.map(function(attr) { + return attr.arguments ? attr.arguments.filter(function(arg) { + return !arg.optional && !arg.variadic; + }).length : 0; + }) + .reduce(function(m, n) { return Math.min(m, n); }); +} + /// IdlArray /// // Entry point -window.IdlArray = function() +self.IdlArray = function() //@{ { /** * A map from strings to the corresponding named IdlObject, such as * IdlInterface or IdlException. These are the things that test() will run * tests on. */ this.members = {}; @@ -161,21 +174,18 @@ IdlArray.prototype.internal_add_idls = f parsed_idl.array = this; if (parsed_idl.name in this.members) { throw "Duplicate identifier " + parsed_idl.name; } switch(parsed_idl.type) { case "interface": - this.members[parsed_idl.name] = new IdlInterface(parsed_idl); - break; - - case "exception": - this.members[parsed_idl.name] = new IdlException(parsed_idl); + this.members[parsed_idl.name] = + new IdlInterface(parsed_idl, /* is_callback = */ false); break; case "dictionary": // Nothing to test, but we need the dictionary info around for type // checks this.members[parsed_idl.name] = new IdlDictionary(parsed_idl); break; @@ -188,18 +198,18 @@ IdlArray.prototype.internal_add_idls = f console.log("callback not yet supported"); break; case "enum": this.members[parsed_idl.name] = new IdlEnum(parsed_idl); break; case "callback interface": - // TODO - console.log("callback interface not yet supported"); + this.members[parsed_idl.name] = + new IdlInterface(parsed_idl, /* is_callback = */ true); break; default: throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported"; } }.bind(this)); }; @@ -408,22 +418,24 @@ IdlArray.prototype.assert_type_is = func assert_true(0 <= value && value <= 4294967295, "unsigned long " + value + " not in range [0, 4294967295]"); return; case "long long": assert_equals(typeof value, "number"); return; case "unsigned long long": + case "DOMTimeStamp": assert_equals(typeof value, "number"); assert_true(0 <= value, "unsigned long long is negative"); return; case "float": case "double": + case "DOMHighResTimeStamp": case "unrestricted float": case "unrestricted double": // TODO: distinguish these cases assert_equals(typeof value, "number"); return; case "DOMString": case "ByteString": @@ -443,23 +455,23 @@ IdlArray.prototype.assert_type_is = func } if (this.members[type] instanceof IdlInterface) { // We don't want to run the full // IdlInterface.prototype.test_instance_of, because that could result // in an infinite loop. TODO: This means we don't have tests for // NoInterfaceObject interfaces, and we also can't test objects that - // come from another window. + // come from another self. assert_true(typeof value == "object" || typeof value == "function", "wrong type: not object or function"); if (value instanceof Object && !this.members[type].has_extended_attribute("NoInterfaceObject") - && type in window) + && type in self) { - assert_true(value instanceof window[type], "not instanceof " + type); + assert_true(value instanceof self[type], "not instanceof " + type); } } else if (this.members[type] instanceof IdlEnum) { assert_equals(typeof value, "string"); } else if (this.members[type] instanceof IdlDictionary) { @@ -524,21 +536,18 @@ function IdlDictionary(obj) * if there is none. */ this.base = obj.inheritance; } //@} IdlDictionary.prototype = Object.create(IdlObject.prototype); -/// IdlExceptionOrInterface /// -// Code sharing! -function IdlExceptionOrInterface(obj) -//@{ -{ +/// IdlInterface /// +function IdlInterface(obj, is_callback) { /** * obj is an object produced by the WebIDLParser.js "exception" or * "interface" production, as appropriate. */ /** Self-explanatory. */ this.name = obj.name; @@ -552,27 +561,58 @@ function IdlExceptionOrInterface(obj) */ this.untested = obj.untested; /** An array of objects produced by the "ExtAttr" production. */ this.extAttrs = obj.extAttrs; /** An array of IdlInterfaceMembers. */ this.members = obj.members.map(function(m){return new IdlInterfaceMember(m); }); + if (this.has_extended_attribute("Unforgeable")) { + this.members + .filter(function(m) { return !m["static"] && (m.type == "attribute" || m.type == "operation"); }) + .forEach(function(m) { return m.isUnforgeable = true; }); + } /** * The name (as a string) of the type we inherit from, or null if there is * none. */ this.base = obj.inheritance; + + this._is_callback = is_callback; } - +IdlInterface.prototype = Object.create(IdlObject.prototype); +IdlInterface.prototype.is_callback = function() +//@{ +{ + return this._is_callback; +}; //@} -IdlExceptionOrInterface.prototype = Object.create(IdlObject.prototype); -IdlExceptionOrInterface.prototype.test = function() + +IdlInterface.prototype.has_constants = function() +//@{ +{ + return this.members.some(function(member) { + return member.type === "const"; + }); +}; +//@} + +IdlInterface.prototype.is_global = function() +//@{ +{ + return this.extAttrs.some(function(attribute) { + return attribute.name === "Global" || + attribute.name === "PrimaryGlobal"; + }); +}; +//@} + +IdlInterface.prototype.test = function() //@{ { if (this.has_extended_attribute("NoInterfaceObject")) { // No tests to do without an instance. TODO: We should still be able // to run tests on the prototype object, if we obtain one through some // other means. return; @@ -588,853 +628,604 @@ IdlExceptionOrInterface.prototype.test = // operations, . . .). These are run even if .untested is true, because // members might themselves be marked as .untested. This might happen to // interfaces if the interface itself is untested but a partial interface // that extends it is tested -- then the interface itself and its initial // members will be marked as untested, but the members added by the partial // interface are still tested. this.test_members(); }; - -//@} - -/// IdlException /// -function IdlException(obj) { IdlExceptionOrInterface.call(this, obj); } -IdlException.prototype = Object.create(IdlExceptionOrInterface.prototype); -IdlException.prototype.test_self = function() -//@{ -{ - test(function() - { - // "For every exception that is not declared with the - // [NoInterfaceObject] extended attribute, a corresponding property - // must exist on the exception’s relevant namespace object. The name of - // the property is the identifier of the exception, and its value is an - // object called the exception interface object, which provides access - // to any constants that have been associated with the exception. The - // property has the attributes { [[Writable]]: true, [[Enumerable]]: - // false, [[Configurable]]: true }." - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); - var desc = Object.getOwnPropertyDescriptor(window, this.name); - assert_false("get" in desc, "window's property " + format_value(this.name) + " has getter"); - assert_false("set" in desc, "window's property " + format_value(this.name) + " has setter"); - assert_true(desc.writable, "window's property " + format_value(this.name) + " is not writable"); - assert_false(desc.enumerable, "window's property " + format_value(this.name) + " is enumerable"); - assert_true(desc.configurable, "window's property " + format_value(this.name) + " is not configurable"); - - // "The exception interface object for a given exception must be a - // function object." - // "If an object is defined to be a function object, then it has - // characteristics as follows:" - // "Its [[Prototype]] internal property is the Function prototype - // object." - // Note: This doesn't match browsers as of December 2011, see - // http://www.w3.org/Bugs/Public/show_bug.cgi?id=14813 - assert_equals(Object.getPrototypeOf(window[this.name]), Function.prototype, - "prototype of window's property " + format_value(this.name) + " is not Function.prototype"); - // "Its [[Get]] internal property is set as described in ECMA-262 - // section 15.3.5.4." - // Not much to test for this. - // "Its [[Construct]] internal property is set as described in ECMA-262 - // section 13.2.2." - // Tested below. - // "Its [[HasInstance]] internal property is set as described in - // ECMA-262 section 15.3.5.3, unless otherwise specified." - // TODO - // "Its [[Class]] internal property is “Function”." - // String() returns something implementation-dependent, because it - // calls Function#toString. - assert_class_string(window[this.name], "Function", - "class string of " + this.name); - - // TODO: Test 4.9.1.1. Exception interface object [[Call]] method - // (which does not match browsers: - // http://www.w3.org/Bugs/Public/show_bug.cgi?id=14885) - }.bind(this), this.name + " exception: existence and properties of exception interface object"); - - test(function() - { - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); - - // "The exception interface object must also have a property named - // “prototype” with attributes { [[Writable]]: false, [[Enumerable]]: - // false, [[Configurable]]: false } whose value is an object called the - // exception interface prototype object. This object also provides - // access to the constants that are declared on the exception." - assert_own_property(window[this.name], "prototype", - 'exception "' + this.name + '" does not have own property "prototype"'); - var desc = Object.getOwnPropertyDescriptor(window[this.name], "prototype"); - assert_false("get" in desc, this.name + ".prototype has getter"); - assert_false("set" in desc, this.name + ".prototype has setter"); - assert_false(desc.writable, this.name + ".prototype is writable"); - assert_false(desc.enumerable, this.name + ".prototype is enumerable"); - assert_false(desc.configurable, this.name + ".prototype is configurable"); - - // "The exception interface prototype object for a given exception must - // have an internal [[Prototype]] property whose value is as follows: - // - // "If the exception is declared to inherit from another exception, - // then the value of the internal [[Prototype]] property is the - // exception interface prototype object for the inherited exception. - // "Otherwise, the exception is not declared to inherit from another - // exception. The value of the internal [[Prototype]] property is the - // Error prototype object ([ECMA-262], section 15.11.3.1)." - // - // Note: This doesn't match browsers as of December 2011, see - // https://www.w3.org/Bugs/Public/show_bug.cgi?id=14887. - var inherit_exception = this.base ? this.base : "Error"; - assert_own_property(window, inherit_exception, - 'should inherit from ' + inherit_exception + ', but window has no such property'); - assert_own_property(window[inherit_exception], "prototype", - 'should inherit from ' + inherit_exception + ', but that object has no "prototype" property'); - assert_equals(Object.getPrototypeOf(window[this.name].prototype), - window[inherit_exception].prototype, - 'prototype of ' + this.name + '.prototype is not ' + inherit_exception + '.prototype'); - - // "The class string of an exception interface prototype object is the - // concatenation of the exception’s identifier and the string - // “Prototype”." - assert_class_string(window[this.name].prototype, this.name + "Prototype", - "class string of " + this.name + ".prototype"); - // TODO: Test String(), based on ES definition of - // Error.prototype.toString? - }.bind(this), this.name + " exception: existence and properties of exception interface prototype object"); - - test(function() - { - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); - assert_own_property(window[this.name], "prototype", - 'interface "' + this.name + '" does not have own property "prototype"'); - - // "There must be a property named “name” on the exception interface - // prototype object with attributes { [[Writable]]: true, - // [[Enumerable]]: false, [[Configurable]]: true } and whose value is - // the identifier of the exception." - assert_own_property(window[this.name].prototype, "name", - 'prototype object does not have own property "name"'); - var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, "name"); - assert_false("get" in desc, this.name + ".prototype.name has getter"); - assert_false("set" in desc, this.name + ".prototype.name has setter"); - assert_true(desc.writable, this.name + ".prototype.name is not writable"); - assert_false(desc.enumerable, this.name + ".prototype.name is enumerable"); - assert_true(desc.configurable, this.name + ".prototype.name is not configurable"); - assert_equals(desc.value, this.name, this.name + ".prototype.name has incorrect value"); - }.bind(this), this.name + " exception: existence and properties of exception interface prototype object's \"name\" property"); - - test(function() - { - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); - assert_own_property(window[this.name], "prototype", - 'interface "' + this.name + '" does not have own property "prototype"'); - - // "If the [NoInterfaceObject] extended attribute was not specified on - // the exception, then there must also be a property named - // “constructor” on the exception interface prototype object with - // attributes { [[Writable]]: true, [[Enumerable]]: false, - // [[Configurable]]: true } and whose value is a reference to the - // exception interface object for the exception." - assert_own_property(window[this.name].prototype, "constructor", - this.name + '.prototype does not have own property "constructor"'); - var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, "constructor"); - assert_false("get" in desc, this.name + ".prototype.constructor has getter"); - assert_false("set" in desc, this.name + ".prototype.constructor has setter"); - assert_true(desc.writable, this.name + ".prototype.constructor is not writable"); - assert_false(desc.enumerable, this.name + ".prototype.constructor is enumerable"); - assert_true(desc.configurable, this.name + ".prototype.constructor in not configurable"); - assert_equals(window[this.name].prototype.constructor, window[this.name], - this.name + '.prototype.constructor is not the same object as ' + this.name); - }.bind(this), this.name + " exception: existence and properties of exception interface prototype object's \"constructor\" property"); -}; - -//@} -IdlException.prototype.test_members = function() -//@{ -{ - for (var i = 0; i < this.members.length; i++) - { - var member = this.members[i]; - if (member.untested) - { - continue; - } - if (member.type == "const" && member.name != "prototype") - { - test(function() - { - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); - - // "For each constant defined on the exception, there must be a - // corresponding property on the exception interface object, if - // it exists, if the identifier of the constant is not - // “prototype”." - assert_own_property(window[this.name], member.name); - // "The value of the property is the ECMAScript value that is - // equivalent to the constant’s IDL value, according to the - // rules in section 4.2 above." - assert_equals(window[this.name][member.name], constValue(member.value), - "property has wrong value"); - // "The property has attributes { [[Writable]]: false, - // [[Enumerable]]: true, [[Configurable]]: false }." - var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name); - assert_false("get" in desc, "property has getter"); - assert_false("set" in desc, "property has setter"); - assert_false(desc.writable, "property is writable"); - assert_true(desc.enumerable, "property is not enumerable"); - assert_false(desc.configurable, "property is configurable"); - }.bind(this), this.name + " exception: constant " + member.name + " on exception interface object"); - // "In addition, a property with the same characteristics must - // exist on the exception interface prototype object." - test(function() - { - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); - assert_own_property(window[this.name], "prototype", - 'exception "' + this.name + '" does not have own property "prototype"'); - - assert_own_property(window[this.name].prototype, member.name); - assert_equals(window[this.name].prototype[member.name], constValue(member.value), - "property has wrong value"); - var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, member.name); - assert_false("get" in desc, "property has getter"); - assert_false("set" in desc, "property has setter"); - assert_false(desc.writable, "property is writable"); - assert_true(desc.enumerable, "property is not enumerable"); - assert_false(desc.configurable, "property is configurable"); - }.bind(this), this.name + " exception: constant " + member.name + " on exception interface prototype object"); - } - else if (member.type == "field") - { - test(function() - { - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); - assert_own_property(window[this.name], "prototype", - 'exception "' + this.name + '" does not have own property "prototype"'); - - // "For each exception field, there must be a corresponding - // property on the exception interface prototype object, whose - // characteristics are as follows: - // "The name of the property is the identifier of the exception - // field." - assert_own_property(window[this.name].prototype, member.name); - // "The property has attributes { [[Get]]: G, [[Enumerable]]: - // true, [[Configurable]]: true }, where G is the exception - // field getter, defined below." - var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, member.name); - assert_false("value" in desc, "property descriptor has value but is supposed to be accessor"); - assert_false("writable" in desc, 'property descriptor has "writable" field but is supposed to be accessor'); - // TODO: ES5 doesn't seem to say whether desc should have a - // .set property. - assert_true(desc.enumerable, "property is not enumerable"); - assert_true(desc.configurable, "property is not configurable"); - // "The exception field getter is a Function object whose - // behavior when invoked is as follows:" - assert_equals(typeof desc.get, "function", "typeof getter"); - // "The value of the Function object’s “length” property is the - // Number value 0." - // This test is before the TypeError tests so that it's easiest - // to see that Firefox 11a1 only fails one assert in this test. - assert_equals(desc.get.length, 0, "getter length"); - // "Let O be the result of calling ToObject on the this value. - // "If O is not a platform object representing an exception for - // the exception on which the exception field was declared, - // then throw a TypeError." - // TODO: Test on a platform object representing an exception. - assert_throws(new TypeError(), function() - { - window[this.name].prototype[member.name]; - }.bind(this), "getting property on prototype object must throw TypeError"); - assert_throws(new TypeError(), function() - { - desc.get.call({}); - }.bind(this), "calling getter on wrong object type must throw TypeError"); - }.bind(this), this.name + " exception: field " + member.name + " on exception interface prototype object"); - } - } -}; - -//@} -IdlException.prototype.test_object = function(desc) -//@{ -{ - var obj, exception = null; - try - { - obj = eval(desc); - } - catch(e) - { - exception = e; - } - - test(function() - { - assert_equals(exception, null, "Unexpected exception when evaluating object"); - assert_equals(typeof obj, "object", "wrong typeof object"); - - // We can't easily test that its prototype is correct if there's no - // interface object, or the object is from a different global - // environment (not instanceof Object). TODO: test in this case that - // its prototype at least looks correct, even if we can't test that - // it's actually correct. - if (!this.has_extended_attribute("NoInterfaceObject") - && (typeof obj != "object" || obj instanceof Object)) - { - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); - assert_own_property(window[this.name], "prototype", - 'exception "' + this.name + '" does not have own property "prototype"'); - - // "The value of the internal [[Prototype]] property of the - // exception object must be the exception interface prototype - // object from the global environment the exception object is - // associated with." - assert_equals(Object.getPrototypeOf(obj), - window[this.name].prototype, - desc + "'s prototype is not " + this.name + ".prototype"); - } - - // "The class string of the exception object must be the identifier of - // the exception." - assert_class_string(obj, this.name, "class string of " + desc); - // Stringifier is not defined for DOMExceptions, because message isn't - // defined. - }.bind(this), this.name + " must be represented by " + desc); - - for (var i = 0; i < this.members.length; i++) - { - var member = this.members[i]; - test(function() - { - assert_equals(exception, null, "Unexpected exception when evaluating object"); - assert_equals(typeof obj, "object", "wrong typeof object"); - assert_inherits(obj, member.name); - if (member.type == "const") - { - assert_equals(obj[member.name], constValue(member.value)); - } - if (member.type == "field") - { - this.array.assert_type_is(obj[member.name], member.idlType); - } - }.bind(this), this.name + " exception: " + desc + ' must inherit property "' + member.name + '" with the proper type'); - } -}; -//@} - -/// IdlInterface /// -function IdlInterface(obj) { IdlExceptionOrInterface.call(this, obj); } -IdlInterface.prototype = Object.create(IdlExceptionOrInterface.prototype); -IdlInterface.prototype.is_callback = function() -//@{ -{ - return this.has_extended_attribute("Callback"); -}; -//@} - -IdlInterface.prototype.has_constants = function() -//@{ -{ - return this.members.some(function(member) { - return member.type === "const"; - }); -}; //@} IdlInterface.prototype.test_self = function() //@{ { test(function() { - // This function tests WebIDL as of 2012-11-28. + // This function tests WebIDL as of 2015-01-13. + // TODO: Consider [Exposed]. - // "For every interface that: + // "For every interface that is exposed in a given ECMAScript global + // environment and: // * is a callback interface that has constants declared on it, or // * is a non-callback interface that is not declared with the // [NoInterfaceObject] extended attribute, // a corresponding property MUST exist on the ECMAScript global object. // The name of the property is the identifier of the interface, and its // value is an object called the interface object. // The property has the attributes { [[Writable]]: true, // [[Enumerable]]: false, [[Configurable]]: true }." if (this.is_callback() && !this.has_constants()) { return; } // TODO: Should we test here that the property is actually writable // etc., or trust getOwnPropertyDescriptor? - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); - var desc = Object.getOwnPropertyDescriptor(window, this.name); - assert_false("get" in desc, "window's property " + format_value(this.name) + " has getter"); - assert_false("set" in desc, "window's property " + format_value(this.name) + " has setter"); - assert_true(desc.writable, "window's property " + format_value(this.name) + " is not writable"); - assert_false(desc.enumerable, "window's property " + format_value(this.name) + " is enumerable"); - assert_true(desc.configurable, "window's property " + format_value(this.name) + " is not configurable"); + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + var desc = Object.getOwnPropertyDescriptor(self, this.name); + assert_false("get" in desc, "self's property " + format_value(this.name) + " has getter"); + assert_false("set" in desc, "self's property " + format_value(this.name) + " has setter"); + assert_true(desc.writable, "self's property " + format_value(this.name) + " is not writable"); + assert_false(desc.enumerable, "self's property " + format_value(this.name) + " is enumerable"); + assert_true(desc.configurable, "self's property " + format_value(this.name) + " is not configurable"); if (this.is_callback()) { // "The internal [[Prototype]] property of an interface object for // a callback interface MUST be the Object.prototype object." - assert_equals(Object.getPrototypeOf(window[this.name]), Object.prototype, - "prototype of window's property " + format_value(this.name) + " is not Object.prototype"); + assert_equals(Object.getPrototypeOf(self[this.name]), Object.prototype, + "prototype of self's property " + format_value(this.name) + " is not Object.prototype"); return; } // "The interface object for a given non-callback interface is a // function object." // "If an object is defined to be a function object, then it has // characteristics as follows:" - // "* Its [[Prototype]] internal property is the Function prototype - // object." - assert_equals(Object.getPrototypeOf(window[this.name]), Function.prototype, - "prototype of window's property " + format_value(this.name) + " is not Function.prototype"); + // Its [[Prototype]] internal property is otherwise specified (see + // below). // "* Its [[Get]] internal property is set as described in ECMA-262 - // section 15.3.5.4." + // section 9.1.8." // Not much to test for this. // "* Its [[Construct]] internal property is set as described in - // ECMA-262 section 13.2.2." + // ECMA-262 section 19.2.2.3." // Tested below if no constructor is defined. TODO: test constructors // if defined. - // "* Its [[HasInstance]] internal property is set as described in - // ECMA-262 section 15.3.5.3, unless otherwise specified." + // "* Its @@hasInstance property is set as described in ECMA-262 + // section 19.2.3.8, unless otherwise specified." // TODO - // "* Its [[NativeBrand]] internal property is “Function”." - // String() returns something implementation-dependent, because it calls - // Function#toString. - assert_class_string(window[this.name], "Function", "class string of " + this.name); + // ES6 (rev 30) 19.1.3.6: + // "Else, if O has a [[Call]] internal method, then let builtinTag be + // "Function"." + assert_class_string(self[this.name], "Function", "class string of " + this.name); + + // "The [[Prototype]] internal property of an interface object for a + // non-callback interface is determined as follows:" + var prototype = Object.getPrototypeOf(self[this.name]); + if (this.base) { + // "* If the interface inherits from some other interface, the + // value of [[Prototype]] is the interface object for that other + // interface." + var has_interface_object = + !this.array + .members[this.base] + .has_extended_attribute("NoInterfaceObject"); + if (has_interface_object) { + assert_own_property(self, this.base, + 'should inherit from ' + this.base + + ', but self has no such property'); + assert_equals(prototype, self[this.base], + 'prototype of ' + this.name + ' is not ' + + this.base); + } + } else { + // "If the interface doesn't inherit from any other interface, the + // value of [[Prototype]] is %FunctionPrototype% ([ECMA-262], + // section 6.1.7.4)." + assert_equals(prototype, Function.prototype, + "prototype of self's property " + format_value(this.name) + " is not Function.prototype"); + } if (!this.has_extended_attribute("Constructor")) { // "The internal [[Call]] method of the interface object behaves as // follows . . . // // "If I was not declared with a [Constructor] extended attribute, // then throw a TypeError." assert_throws(new TypeError(), function() { - window[this.name](); + self[this.name](); }.bind(this), "interface object didn't throw TypeError when called as a function"); assert_throws(new TypeError(), function() { - new window[this.name](); + new self[this.name](); }.bind(this), "interface object didn't throw TypeError when called as a constructor"); } }.bind(this), this.name + " interface: existence and properties of interface object"); if (!this.is_callback()) { test(function() { // This function tests WebIDL as of 2014-10-25. // https://heycam.github.io/webidl/#es-interface-call - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); // "Interface objects for non-callback interfaces MUST have a // property named “length” with attributes { [[Writable]]: false, // [[Enumerable]]: false, [[Configurable]]: true } whose value is // a Number." - assert_own_property(window[this.name], "length"); - var desc = Object.getOwnPropertyDescriptor(window[this.name], "length"); + assert_own_property(self[this.name], "length"); + var desc = Object.getOwnPropertyDescriptor(self[this.name], "length"); assert_false("get" in desc, this.name + ".length has getter"); assert_false("set" in desc, this.name + ".length has setter"); assert_false(desc.writable, this.name + ".length is writable"); assert_false(desc.enumerable, this.name + ".length is enumerable"); assert_true(desc.configurable, this.name + ".length is not configurable"); var constructors = this.extAttrs .filter(function(attr) { return attr.name == "Constructor"; }); - var expected_length; - if (!constructors.length) { - // "If the [Constructor] extended attribute, does not appear on - // the interface definition, then the value is 0." - expected_length = 0; - } else { - // "Otherwise, the value is determined as follows: . . . - // "Return the length of the shortest argument list of the - // entries in S." - expected_length = constructors.map(function(attr) { - return attr.arguments ? attr.arguments.filter(function(arg) { - return !arg.optional; - }).length : 0; - }) - .reduce(function(m, n) { return Math.min(m, n); }); - } - assert_equals(window[this.name].length, expected_length, "wrong value for " + this.name + ".length"); + var expected_length = minOverloadLength(constructors); + assert_equals(self[this.name].length, expected_length, "wrong value for " + this.name + ".length"); }.bind(this), this.name + " interface object length"); } // TODO: Test named constructors if I find any interfaces that have them. test(function() { - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); + // This function tests WebIDL as of 2015-01-21. + // https://heycam.github.io/webidl/#interface-object - if (this.has_extended_attribute("Callback")) { - assert_false("prototype" in window[this.name], + if (this.is_callback() && !this.has_constants()) { + return; + } + + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + + if (this.is_callback()) { + assert_false("prototype" in self[this.name], this.name + ' should not have a "prototype" property'); return; } - // "The interface object must also have a property named “prototype” - // with attributes { [[Writable]]: false, [[Enumerable]]: false, - // [[Configurable]]: false } whose value is an object called the - // interface prototype object. This object has properties that - // correspond to the attributes and operations defined on the - // interface, and is described in more detail in section 4.5.3 below." - assert_own_property(window[this.name], "prototype", + // "An interface object for a non-callback interface must have a + // property named “prototype” with attributes { [[Writable]]: false, + // [[Enumerable]]: false, [[Configurable]]: false } whose value is an + // object called the interface prototype object. This object has + // properties that correspond to the regular attributes and regular + // operations defined on the interface, and is described in more detail + // in section 4.5.4 below." + assert_own_property(self[this.name], "prototype", 'interface "' + this.name + '" does not have own property "prototype"'); - var desc = Object.getOwnPropertyDescriptor(window[this.name], "prototype"); + var desc = Object.getOwnPropertyDescriptor(self[this.name], "prototype"); assert_false("get" in desc, this.name + ".prototype has getter"); assert_false("set" in desc, this.name + ".prototype has setter"); assert_false(desc.writable, this.name + ".prototype is writable"); assert_false(desc.enumerable, this.name + ".prototype is enumerable"); assert_false(desc.configurable, this.name + ".prototype is configurable"); // Next, test that the [[Prototype]] of the interface prototype object // is correct. (This is made somewhat difficult by the existence of // [NoInterfaceObject].) // TODO: Aryeh thinks there's at least other place in this file where // we try to figure out if an interface prototype object is // correct. Consolidate that code. // "The interface prototype object for a given interface A must have an - // internal [[Prototype]] property whose value is as follows: - // "If A is not declared to inherit from another interface, then the - // value of the internal [[Prototype]] property of A is the Array - // prototype object ([ECMA-262], section 15.4.4) if the interface was - // declared with ArrayClass, or the Object prototype object otherwise + // internal [[Prototype]] property whose value is returned from the + // following steps: + // "If A is declared with the [Global] or [PrimaryGlobal] extended + // attribute, and A supports named properties, then return the named + // properties object for A, as defined in section 4.5.5 below. + // "Otherwise, if A is declared to inherit from another interface, then + // return the interface prototype object for the inherited interface. + // "Otherwise, if A is declared with the [ArrayClass] extended + // attribute, then return %ArrayPrototype% ([ECMA-262], section + // 6.1.7.4). + // "Otherwise, return %ObjectPrototype% ([ECMA-262], section 6.1.7.4). // ([ECMA-262], section 15.2.4). - // "Otherwise, A does inherit from another interface. The value of the - // internal [[Prototype]] property of A is the interface prototype - // object for the inherited interface." - var inherit_interface, inherit_interface_has_interface_object; - if (this.base) { - inherit_interface = this.base; - inherit_interface_has_interface_object = - !this.array - .members[inherit_interface] - .has_extended_attribute("NoInterfaceObject"); - } else if (this.has_extended_attribute('ArrayClass')) { - inherit_interface = 'Array'; - inherit_interface_has_interface_object = true; + if (this.name === "Window") { + assert_class_string(Object.getPrototypeOf(self[this.name].prototype), + 'WindowProperties', + 'Class name for prototype of Window' + + '.prototype is not "WindowProperties"'); } else { - inherit_interface = 'Object'; - inherit_interface_has_interface_object = true; - } - if (inherit_interface_has_interface_object) { - assert_own_property(window, inherit_interface, - 'should inherit from ' + inherit_interface + ', but window has no such property'); - assert_own_property(window[inherit_interface], 'prototype', - 'should inherit from ' + inherit_interface + ', but that object has no "prototype" property'); - assert_equals(Object.getPrototypeOf(window[this.name].prototype), - window[inherit_interface].prototype, - 'prototype of ' + this.name + '.prototype is not ' + inherit_interface + '.prototype'); - } else { - // We can't test that we get the correct object, because this is the - // only way to get our hands on it. We only test that its class - // string, at least, is correct. - assert_class_string(Object.getPrototypeOf(window[this.name].prototype), - inherit_interface + 'Prototype', - 'Class name for prototype of ' + this.name + - '.prototype is not "' + inherit_interface + 'Prototype"'); + var inherit_interface, inherit_interface_has_interface_object; + if (this.base) { + inherit_interface = this.base; + inherit_interface_has_interface_object = + !this.array + .members[inherit_interface] + .has_extended_attribute("NoInterfaceObject"); + } else if (this.has_extended_attribute('ArrayClass')) { + inherit_interface = 'Array'; + inherit_interface_has_interface_object = true; + } else { + inherit_interface = 'Object'; + inherit_interface_has_interface_object = true; + } + if (inherit_interface_has_interface_object) { + assert_own_property(self, inherit_interface, + 'should inherit from ' + inherit_interface + ', but self has no such property'); + assert_own_property(self[inherit_interface], 'prototype', + 'should inherit from ' + inherit_interface + ', but that object has no "prototype" property'); + assert_equals(Object.getPrototypeOf(self[this.name].prototype), + self[inherit_interface].prototype, + 'prototype of ' + this.name + '.prototype is not ' + inherit_interface + '.prototype'); + } else { + // We can't test that we get the correct object, because this is the + // only way to get our hands on it. We only test that its class + // string, at least, is correct. + assert_class_string(Object.getPrototypeOf(self[this.name].prototype), + inherit_interface + 'Prototype', + 'Class name for prototype of ' + this.name + + '.prototype is not "' + inherit_interface + 'Prototype"'); + } } // "The class string of an interface prototype object is the // concatenation of the interface’s identifier and the string // “Prototype”." - assert_class_string(window[this.name].prototype, this.name + "Prototype", + assert_class_string(self[this.name].prototype, this.name + "Prototype", "class string of " + this.name + ".prototype"); // String() should end up calling {}.toString if nothing defines a // stringifier. if (!this.has_stringifier()) { - assert_equals(String(window[this.name].prototype), "[object " + this.name + "Prototype]", + assert_equals(String(self[this.name].prototype), "[object " + this.name + "Prototype]", "String(" + this.name + ".prototype)"); } }.bind(this), this.name + " interface: existence and properties of interface prototype object"); test(function() { - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); + if (this.is_callback() && !this.has_constants()) { + return; + } - if (this.has_extended_attribute("Callback")) { - assert_false("prototype" in window[this.name], + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + + if (this.is_callback()) { + assert_false("prototype" in self[this.name], this.name + ' should not have a "prototype" property'); return; } - assert_own_property(window[this.name], "prototype", + assert_own_property(self[this.name], "prototype", 'interface "' + this.name + '" does not have own property "prototype"'); // "If the [NoInterfaceObject] extended attribute was not specified on // the interface, then the interface prototype object must also have a // property named “constructor” with attributes { [[Writable]]: true, // [[Enumerable]]: false, [[Configurable]]: true } whose value is a // reference to the interface object for the interface." - assert_own_property(window[this.name].prototype, "constructor", + assert_own_property(self[this.name].prototype, "constructor", this.name + '.prototype does not have own property "constructor"'); - var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, "constructor"); + var desc = Object.getOwnPropertyDescriptor(self[this.name].prototype, "constructor"); assert_false("get" in desc, this.name + ".prototype.constructor has getter"); assert_false("set" in desc, this.name + ".prototype.constructor has setter"); assert_true(desc.writable, this.name + ".prototype.constructor is not writable"); assert_false(desc.enumerable, this.name + ".prototype.constructor is enumerable"); assert_true(desc.configurable, this.name + ".prototype.constructor in not configurable"); - assert_equals(window[this.name].prototype.constructor, window[this.name], + assert_equals(self[this.name].prototype.constructor, self[this.name], this.name + '.prototype.constructor is not the same object as ' + this.name); }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property'); }; //@} IdlInterface.prototype.test_member_const = function(member) //@{ { test(function() { - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); + if (this.is_callback() && !this.has_constants()) { + return; + } + + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); // "For each constant defined on an interface A, there must be // a corresponding property on the interface object, if it // exists." - assert_own_property(window[this.name], member.name); + assert_own_property(self[this.name], member.name); // "The value of the property is that which is obtained by // converting the constant’s IDL value to an ECMAScript // value." - assert_equals(window[this.name][member.name], constValue(member.value), + assert_equals(self[this.name][member.name], constValue(member.value), "property has wrong value"); // "The property has attributes { [[Writable]]: false, // [[Enumerable]]: true, [[Configurable]]: false }." - var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name); + var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name); assert_false("get" in desc, "property has getter"); assert_false("set" in desc, "property has setter"); assert_false(desc.writable, "property is writable"); assert_true(desc.enumerable, "property is not enumerable"); assert_false(desc.configurable, "property is configurable"); }.bind(this), this.name + " interface: constant " + member.name + " on interface object"); // "In addition, a property with the same characteristics must // exist on the interface prototype object." test(function() { - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); + if (this.is_callback() && !this.has_constants()) { + return; + } - if (this.has_extended_attribute("Callback")) { - assert_false("prototype" in window[this.name], + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + + if (this.is_callback()) { + assert_false("prototype" in self[this.name], this.name + ' should not have a "prototype" property'); return; } - assert_own_property(window[this.name], "prototype", + assert_own_property(self[this.name], "prototype", 'interface "' + this.name + '" does not have own property "prototype"'); - assert_own_property(window[this.name].prototype, member.name); - assert_equals(window[this.name].prototype[member.name], constValue(member.value), + assert_own_property(self[this.name].prototype, member.name); + assert_equals(self[this.name].prototype[member.name], constValue(member.value), "property has wrong value"); - var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name); + var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name); assert_false("get" in desc, "property has getter"); assert_false("set" in desc, "property has setter"); assert_false(desc.writable, "property is writable"); assert_true(desc.enumerable, "property is not enumerable"); assert_false(desc.configurable, "property is configurable"); }.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object"); }; //@} IdlInterface.prototype.test_member_attribute = function(member) //@{ { test(function() { - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); - assert_own_property(window[this.name], "prototype", + if (this.is_callback() && !this.has_constants()) { + return; + } + + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + assert_own_property(self[this.name], "prototype", 'interface "' + this.name + '" does not have own property "prototype"'); if (member["static"]) { - assert_own_property(window[this.name], member.name, + assert_own_property(self[this.name], member.name, "The interface object must have a property " + format_value(member.name)); - } - else - { - assert_true(member.name in window[this.name].prototype, + } else if (this.is_global()) { + assert_own_property(self, member.name, + "The global object must have a property " + + format_value(member.name)); + assert_false(member.name in self[this.name].prototype, + "The prototype object must not have a property " + + format_value(member.name)); + + // Try/catch around the get here, since it can legitimately throw. + // If it does, we obviously can't check for equality with direct + // invocation of the getter. + var gotValue; + var propVal; + try { + propVal = self[member.name]; + gotValue = true; + } catch (e) { + gotValue = false; + } + if (gotValue) { + var getter = Object.getOwnPropertyDescriptor(self, member.name).get; + assert_equals(typeof(getter), "function", + format_value(member.name) + " must have a getter"); + assert_equals(propVal, getter.call(undefined), + "Gets on a global should not require an explicit this"); + } + this.do_interface_attribute_asserts(self, member); + } else { + assert_true(member.name in self[this.name].prototype, "The prototype object must have a property " + format_value(member.name)); if (!member.has_extended_attribute("LenientThis")) { assert_throws(new TypeError(), function() { - window[this.name].prototype[member.name]; + self[this.name].prototype[member.name]; }.bind(this), "getting property on prototype object must throw TypeError"); } else { - assert_equals(window[this.name].prototype[member.name], undefined, + assert_equals(self[this.name].prototype[member.name], undefined, "getting property on prototype object must return undefined"); } - do_interface_attribute_asserts(window[this.name].prototype, member); + this.do_interface_attribute_asserts(self[this.name].prototype, member); } }.bind(this), this.name + " interface: attribute " + member.name); }; //@} IdlInterface.prototype.test_member_operation = function(member) //@{ { test(function() { - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); + if (this.is_callback() && !this.has_constants()) { + return; + } - if (this.has_extended_attribute("Callback")) { - assert_false("prototype" in window[this.name], + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + + if (this.is_callback()) { + assert_false("prototype" in self[this.name], this.name + ' should not have a "prototype" property'); return; } - assert_own_property(window[this.name], "prototype", + assert_own_property(self[this.name], "prototype", 'interface "' + this.name + '" does not have own property "prototype"'); // "For each unique identifier of an operation defined on the // interface, there must be a corresponding property on the // interface prototype object (if it is a regular operation) or // the interface object (if it is a static operation), unless // the effective overload set for that identifier and operation // and with an argument count of 0 (for the ECMAScript language // binding) has no entries." // - var prototypeOrInterfaceObject; + var memberHolderObject; if (member["static"]) { - assert_own_property(window[this.name], member.name, - "interface prototype object missing static operation"); - prototypeOrInterfaceObject = window[this.name]; - } - else - { - assert_own_property(window[this.name].prototype, member.name, + assert_own_property(self[this.name], member.name, + "interface object missing static operation"); + memberHolderObject = self[this.name]; + } else if (this.is_global()) { + assert_own_property(self, member.name, + "global object missing non-static operation"); + memberHolderObject = self; + } else { + assert_own_property(self[this.name].prototype, member.name, "interface prototype object missing non-static operation"); - prototypeOrInterfaceObject = window[this.name].prototype; + memberHolderObject = self[this.name].prototype; } - var desc = Object.getOwnPropertyDescriptor(prototypeOrInterfaceObject, member.name); - // "The property has attributes { [[Writable]]: true, - // [[Enumerable]]: true, [[Configurable]]: true }." - assert_false("get" in desc, "property has getter"); - assert_false("set" in desc, "property has setter"); - assert_true(desc.writable, "property is not writable"); - assert_true(desc.enumerable, "property is not enumerable"); - assert_true(desc.configurable, "property is not configurable"); - // "The value of the property is a Function object whose - // behavior is as follows . . ." - assert_equals(typeof prototypeOrInterfaceObject[member.name], "function", - "property must be a function"); - // "The value of the Function object’s “length” property is - // a Number determined as follows: - // ". . . - // "Return the length of the shortest argument list of the - // entries in S." - // - // TODO: Doesn't handle overloading or variadic arguments. - assert_equals(prototypeOrInterfaceObject[member.name].length, - member.arguments.filter(function(arg) { - return !arg.optional; - }).length, - "property has wrong .length"); + this.do_member_operation_asserts(memberHolderObject, member); + }.bind(this), this.name + " interface: operation " + member.name + + "(" + member.arguments.map(function(m) { return m.idlType.idlType; }) + + ")"); +}; - // Make some suitable arguments - var args = member.arguments.map(function(arg) { - return create_suitable_object(arg.idlType); - }); +//@} +IdlInterface.prototype.do_member_operation_asserts = function(memberHolderObject, member) +//@{ +{ + var operationUnforgeable = member.isUnforgeable; + var desc = Object.getOwnPropertyDescriptor(memberHolderObject, member.name); + // "The property has attributes { [[Writable]]: B, + // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the + // operation is unforgeable on the interface, and true otherwise". + assert_false("get" in desc, "property has getter"); + assert_false("set" in desc, "property has setter"); + assert_equals(desc.writable, !operationUnforgeable, + "property should be writable if and only if not unforgeable"); + assert_true(desc.enumerable, "property is not enumerable"); + assert_equals(desc.configurable, !operationUnforgeable, + "property should be configurable if and only if not unforgeable"); + // "The value of the property is a Function object whose + // behavior is as follows . . ." + assert_equals(typeof memberHolderObject[member.name], "function", + "property must be a function"); + // "The value of the Function object’s “length” property is + // a Number determined as follows: + // ". . . + // "Return the length of the shortest argument list of the + // entries in S." + assert_equals(memberHolderObject[member.name].length, + minOverloadLength(this.members.filter(function(m) { + return m.type == "operation" && m.name == member.name; + })), + "property has wrong .length"); - // "Let O be a value determined as follows: - // ". . . - // "Otherwise, throw a TypeError." - // This should be hit if the operation is not static, there is - // no [ImplicitThis] attribute, and the this value is null. - // - // TODO: We currently ignore the [ImplicitThis] case. - if (!member["static"]) { + // Make some suitable arguments + var args = member.arguments.map(function(arg) { + return create_suitable_object(arg.idlType); + }); + + // "Let O be a value determined as follows: + // ". . . + // "Otherwise, throw a TypeError." + // This should be hit if the operation is not static, there is + // no [ImplicitThis] attribute, and the this value is null. + // + // TODO: We currently ignore the [ImplicitThis] case. Except we manually + // check for globals, since otherwise we'll invoke window.close(). And we + // have to skip this test for anything that on the proto chain of "self", + // since that does in fact have implicit-this behavior. + if (!member["static"]) { + if (!this.is_global() && + memberHolderObject[member.name] != self[member.name]) + { assert_throws(new TypeError(), function() { - window[this.name].prototype[member.name].apply(null, args); + memberHolderObject[member.name].apply(null, args); }, "calling operation with this = null didn't throw TypeError"); } // ". . . If O is not null and is also not a platform object // that implements interface I, throw a TypeError." // // TODO: Test a platform object that implements some other // interface. (Have to be sure to get inheritance right.) assert_throws(new TypeError(), function() { - window[this.name].prototype[member.name].apply({}, args); + memberHolderObject[member.name].apply({}, args); }, "calling operation with this = {} didn't throw TypeError"); - }.bind(this), this.name + " interface: operation " + member.name + - "(" + member.arguments.map(function(m) { return m.idlType.idlType; }) + - ")"); -}; + } +} //@} IdlInterface.prototype.test_member_stringifier = function(member) //@{ { test(function() { - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); + if (this.is_callback() && !this.has_constants()) { + return; + } - if (this.has_extended_attribute("Callback")) { - assert_false("prototype" in window[this.name], + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + + if (this.is_callback()) { + assert_false("prototype" in self[this.name], this.name + ' should not have a "prototype" property'); return; } - assert_own_property(window[this.name], "prototype", + assert_own_property(self[this.name], "prototype", 'interface "' + this.name + '" does not have own property "prototype"'); // ". . . the property exists on the interface prototype object." - var interfacePrototypeObject = window[this.name].prototype; - assert_own_property(window[this.name].prototype, "toString", + var interfacePrototypeObject = self[this.name].prototype; + assert_own_property(self[this.name].prototype, "toString", "interface prototype object missing non-static operation"); + var stringifierUnforgeable = member.isUnforgeable; var desc = Object.getOwnPropertyDescriptor(interfacePrototypeObject, "toString"); // "The property has attributes { [[Writable]]: B, // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the // stringifier is unforgeable on the interface, and true otherwise." assert_false("get" in desc, "property has getter"); assert_false("set" in desc, "property has setter"); - assert_true(desc.writable, "property is not writable"); + assert_equals(desc.writable, !stringifierUnforgeable, + "property should be writable if and only if not unforgeable"); assert_true(desc.enumerable, "property is not enumerable"); - assert_true(desc.configurable, "property is not configurable"); + assert_equals(desc.configurable, !stringifierUnforgeable, + "property should be configurable if and only if not unforgeable"); // "The value of the property is a Function object, which behaves as // follows . . ." assert_equals(typeof interfacePrototypeObject.toString, "function", "property must be a function"); // "The value of the Function object’s “length” property is the Number // value 0." assert_equals(interfacePrototypeObject.toString.length, 0, "property has wrong .length"); // "Let O be the result of calling ToObject on the this value." assert_throws(new TypeError(), function() { - window[this.name].prototype.toString.apply(null, []); + self[this.name].prototype.toString.apply(null, []); }, "calling stringifier with this = null didn't throw TypeError"); // "If O is not an object that implements the interface on which the // stringifier was declared, then throw a TypeError." // // TODO: Test a platform object that implements some other // interface. (Have to be sure to get inheritance right.) assert_throws(new TypeError(), function() { - window[this.name].prototype.toString.apply({}, []); + self[this.name].prototype.toString.apply({}, []); }, "calling stringifier with this = {} didn't throw TypeError"); }.bind(this), this.name + " interface: stringifier"); }; //@} IdlInterface.prototype.test_members = function() //@{ { @@ -1448,26 +1239,32 @@ IdlInterface.prototype.test_members = fu switch (member.type) { case "const": this.test_member_const(member); break; case "attribute": // For unforgeable attributes, we do the checks in // test_interface_of instead. - if (!member.has_extended_attribute("Unforgeable")) { + if (!member.isUnforgeable) + { this.test_member_attribute(member); } break; case "operation": // TODO: Need to correctly handle multiple operations with the same // identifier. + // For unforgeable operations, we do the checks in + // test_interface_of instead. if (member.name) { - this.test_member_operation(member); + if (!member.isUnforgeable) + { + this.test_member_operation(member); + } } else if (member.stringifier) { this.test_member_stringifier(member); } break; default: // TODO: check more member types. break; @@ -1526,27 +1323,27 @@ IdlInterface.prototype.test_primary_inte // least looks correct, even if we can't test that it's actually correct. if (!this.has_extended_attribute("NoInterfaceObject") && (typeof obj != expected_typeof || obj instanceof Object)) { test(function() { assert_equals(exception, null, "Unexpected exception when evaluating object"); assert_equals(typeof obj, expected_typeof, "wrong typeof object"); - assert_own_property(window, this.name, - "window does not have own property " + format_value(this.name)); - assert_own_property(window[this.name], "prototype", + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + assert_own_property(self[this.name], "prototype", 'interface "' + this.name + '" does not have own property "prototype"'); // "The value of the internal [[Prototype]] property of the // platform object is the interface prototype object of the primary // interface from the platform object’s associated global // environment." assert_equals(Object.getPrototypeOf(obj), - window[this.name].prototype, + self[this.name].prototype, desc + "'s prototype is not " + this.name + ".prototype"); }.bind(this), this.name + " must be primary interface of " + desc); } // "The class string of a platform object that implements one or more // interfaces must be the identifier of the primary interface of the // platform object." test(function() @@ -1566,36 +1363,54 @@ IdlInterface.prototype.test_interface_of //@{ { // TODO: Indexed and named properties, more checks on interface members this.already_tested = true; for (var i = 0; i < this.members.length; i++) { var member = this.members[i]; - if (member.has_extended_attribute("Unforgeable")) + if (member.type == "attribute" && member.isUnforgeable) { test(function() { assert_equals(exception, null, "Unexpected exception when evaluating object"); assert_equals(typeof obj, expected_typeof, "wrong typeof object"); - do_interface_attribute_asserts(obj, member); + this.do_interface_attribute_asserts(obj, member); + }.bind(this), this.name + " interface: " + desc + ' must have own property "' + member.name + '"'); + } + else if (member.type == "operation" && + member.name && + member.isUnforgeable) + { + test(function() + { + assert_equals(exception, null, "Unexpected exception when evaluating object"); + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); + assert_own_property(obj, member.name, + "Doesn't have the unforgeable operation property"); + this.do_member_operation_asserts(obj, member); }.bind(this), this.name + " interface: " + desc + ' must have own property "' + member.name + '"'); } else if ((member.type == "const" || member.type == "attribute" || member.type == "operation") && member.name) { test(function() { assert_equals(exception, null, "Unexpected exception when evaluating object"); assert_equals(typeof obj, expected_typeof, "wrong typeof object"); if (!member["static"]) { - assert_inherits(obj, member.name); + if (!this.is_global()) { + assert_inherits(obj, member.name); + } else { + assert_own_property(obj, member.name); + } + if (member.type == "const") { assert_equals(obj[member.name], constValue(member.value)); } if (member.type == "attribute") { // Attributes are accessor properties, so they might // legitimately throw an exception rather than returning @@ -1626,29 +1441,32 @@ IdlInterface.prototype.test_interface_of // TODO: Test passing arguments of the wrong type. if (member.type == "operation" && member.name && member.arguments.length) { test(function() { assert_equals(exception, null, "Unexpected exception when evaluating object"); assert_equals(typeof obj, expected_typeof, "wrong typeof object"); if (!member["static"]) { - assert_inherits(obj, member.name); + if (!this.is_global() && !member.isUnforgeable) { + assert_inherits(obj, member.name); + } else { + assert_own_property(obj, member.name); + } } else { assert_false(member.name in obj); } + + var minLength = minOverloadLength(this.members.filter(function(m) { + return m.type == "operation" && m.name == member.name; + })); var args = []; - for (var i = 0; i < member.arguments.length; i++) - { - if (member.arguments[i].optional) - { - break; - } + for (var i = 0; i < minLength; i++) { assert_throws(new TypeError(), function() { obj[member.name].apply(obj, args); }.bind(this), "Called with " + i + " arguments"); args.push(create_suitable_object(member.arguments[i].idlType)); } }.bind(this), this.name + " interface: calling " + member.name + @@ -1668,92 +1486,117 @@ IdlInterface.prototype.has_stringifier = if (this.base && this.array.members[this.base].has_stringifier()) { return true; } return false; }; //@} -function do_interface_attribute_asserts(obj, member) +IdlInterface.prototype.do_interface_attribute_asserts = function(obj, member) //@{ { - // "For each attribute defined on the interface, there must exist a - // corresponding property. If the attribute was declared with the - // [Unforgeable] extended attribute, then the property exists on every - // object that implements the interface. Otherwise, it exists on the - // interface’s interface prototype object." - // - // This is called by test_self() with the prototype as obj, and by - // test_interface_of() with the object as obj. + // This function tests WebIDL as of 2015-01-27. + // TODO: Consider [Exposed]. + + // This is called by test_member_attribute() with the prototype as obj if + // it is not a global, and the global otherwise, and by test_interface_of() + // with the object as obj. + + // "For each exposed attribute of the interface, whether it was declared on + // the interface itself or one of its consequential interfaces, there MUST + // exist a corresponding property. The characteristics of this property are + // as follows:" + + // "The name of the property is the identifier of the attribute." assert_own_property(obj, member.name); // "The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]: // true, [[Configurable]]: configurable }, where: // "configurable is false if the attribute was declared with the // [Unforgeable] extended attribute and true otherwise; // "G is the attribute getter, defined below; and // "S is the attribute setter, also defined below." var desc = Object.getOwnPropertyDescriptor(obj, member.name); assert_false("value" in desc, 'property descriptor has value but is supposed to be accessor'); assert_false("writable" in desc, 'property descriptor has "writable" field but is supposed to be accessor'); assert_true(desc.enumerable, "property is not enumerable"); - if (member.has_extended_attribute("Unforgeable")) + if (member.isUnforgeable) { assert_false(desc.configurable, "[Unforgeable] property must not be configurable"); } else { assert_true(desc.configurable, "property must be configurable"); } + // "The attribute getter is a Function object whose behavior when invoked - // is as follows: - // "... + // is as follows:" + assert_equals(typeof desc.get, "function", "getter must be Function"); + + // "If the attribute is a regular attribute, then:" + if (!member["static"]) { + // "If O is not a platform object that implements I, then: + // "If the attribute was specified with the [LenientThis] extended + // attribute, then return undefined. + // "Otherwise, throw a TypeError." + if (!member.has_extended_attribute("LenientThis")) { + assert_throws(new TypeError(), function() { + desc.get.call({}); + }.bind(this), "calling getter on wrong object type must throw TypeError"); + } else { + assert_equals(desc.get.call({}), undefined, + "calling getter on wrong object type must return undefined"); + } + } + // "The value of the Function object’s “length” property is the Number // value 0." - assert_equals(typeof desc.get, "function", "getter must be Function"); assert_equals(desc.get.length, 0, "getter length must be 0"); - if (!member.has_extended_attribute("LenientThis")) { - assert_throws(new TypeError(), function() { - desc.get.call({}); - }.bind(this), "calling getter on wrong object type must throw TypeError"); - } else { - assert_equals(desc.get.call({}), undefined, - "calling getter on wrong object type must return undefined"); - } + // TODO: Test calling setter on the interface prototype (should throw // TypeError in most cases). - // - // "The attribute setter is undefined if the attribute is declared readonly - // and has neither a [PutForwards] nor a [Replaceable] extended attribute - // declared on it. Otherwise, it is a Function object whose behavior when - // invoked is as follows: - // "... - // "The value of the Function object’s “length” property is the Number - // value 1." if (member.readonly && !member.has_extended_attribute("PutForwards") && !member.has_extended_attribute("Replaceable")) { + // "The attribute setter is undefined if the attribute is declared + // readonly and has neither a [PutForwards] nor a [Replaceable] + // extended attribute declared on it." assert_equals(desc.set, undefined, "setter must be undefined for readonly attributes"); } else { + // "Otherwise, it is a Function object whose behavior when + // invoked is as follows:" assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes"); + + // "If the attribute is a regular attribute, then:" + if (!member["static"]) { + // "If /validThis/ is false and the attribute was not specified + // with the [LenientThis] extended attribute, then throw a + // TypeError." + // "If the attribute is declared with a [Replaceable] extended + // attribute, then: ..." + // "If validThis is false, then return." + if (!member.has_extended_attribute("LenientThis")) { + assert_throws(new TypeError(), function() { + desc.set.call({}); + }.bind(this), "calling setter on wrong object type must throw TypeError"); + } else { + assert_equals(desc.set.call({}), undefined, + "calling setter on wrong object type must return undefined"); + } + } + + // "The value of the Function object’s “length” property is the Number + // value 1." assert_equals(desc.set.length, 1, "setter length must be 1"); - if (!member.has_extended_attribute("LenientThis")) { - assert_throws(new TypeError(), function() { - desc.set.call({}); - }.bind(this), "calling setter on wrong object type must throw TypeError"); - } else { - assert_equals(desc.set.call({}), undefined, - "calling setter on wrong object type must return undefined"); - } } } //@} /// IdlInterfaceMember /// function IdlInterfaceMember(obj) //@{ { @@ -1765,16 +1608,18 @@ function IdlInterfaceMember(obj) for (var k in obj) { this[k] = obj[k]; } if (!("extAttrs" in this)) { this.extAttrs = []; } + + this.isUnforgeable = this.has_extended_attribute("Unforgeable"); } //@} IdlInterfaceMember.prototype = Object.create(IdlObject.prototype); /// Internal helper functions /// function create_suitable_object(type) //@{
--- a/dom/imptests/moz.build +++ b/dom/imptests/moz.build @@ -27,10 +27,9 @@ MOCHITEST_MANIFESTS += [ 'failures/html/html/semantics/forms/the-select-element/mochitest.ini', 'failures/html/html/semantics/scripting-1/the-script-element/mochitest.ini', 'failures/html/html/semantics/tabular-data/the-table-element/mochitest.ini', 'failures/html/html/webappapis/atob/mochitest.ini', 'failures/html/js/builtins/mochitest.ini', 'failures/html/microdata/microdata-dom-api/mochitest.ini', 'failures/html/typedarrays/mochitest.ini', 'failures/webapps/WebStorage/tests/submissions/Infraware/mochitest.ini', - 'failures/webapps/XMLHttpRequest/tests/submissions/Ms2ger/mochitest.ini', ]
--- a/dom/imptests/testharness.css +++ b/dom/imptests/testharness.css @@ -9,21 +9,16 @@ html { } #log .error, #log .error a { color: white; background: red; } -#log pre { - border: 1px solid black; - padding: 1em; -} - section#summary { margin-bottom:1em; } table#results { border-collapse:collapse; table-layout:fixed; width:100%;
--- a/dom/imptests/testharness.js +++ b/dom/imptests/testharness.js @@ -17,17 +17,18 @@ policies and contribution forms [3]. var debug = false; // default timeout is 10 seconds, test can override if needed var settings = { output:true, harness_timeout:{ "normal":10000, "long":60000 }, - test_timeout:null + test_timeout:null, + message_events: ["start", "test_state", "result", "completion"] }; var xhtml_ns = "http://www.w3.org/1999/xhtml"; /* * TestEnvironment is an abstraction for the environment in which the test * harness is used. Each implementation of a test environment has to provide * the following interface: @@ -59,30 +60,73 @@ policies and contribution forms [3]. * apisample11.html and apisample12.html. */ function WindowTestEnvironment() { this.name_counter = 0; this.window_cache = null; this.output_handler = null; this.all_loaded = false; var this_obj = this; + this.message_events = []; + + this.message_functions = { + start: [add_start_callback, remove_start_callback, + function (properties) { + this_obj._dispatch("start_callback", [properties], + {type: "start", properties: properties}); + }], + + test_state: [add_test_state_callback, remove_test_state_callback, + function(test) { + this_obj._dispatch("test_state_callback", [test], + {type: "test_state", + test: test.structured_clone()}); + }], + result: [add_result_callback, remove_result_callback, + function (test) { + this_obj.output_handler.show_status(); + this_obj._dispatch("result_callback", [test], + {type: "result", + test: test.structured_clone()}); + }], + completion: [add_completion_callback, remove_completion_callback, + function (tests, harness_status) { + var cloned_tests = map(tests, function(test) { + return test.structured_clone(); + }); + this_obj._dispatch("completion_callback", [tests, harness_status], + {type: "complete", + tests: cloned_tests, + status: harness_status.structured_clone()}); + }] + } + on_event(window, 'load', function() { this_obj.all_loaded = true; }); } WindowTestEnvironment.prototype._dispatch = function(selector, callback_args, message_arg) { this._forEach_windows( - function(w, is_same_origin) { - if (is_same_origin && selector in w) { + function(w, same_origin) { + if (same_origin) { try { - w[selector].apply(undefined, callback_args); - } catch (e) { - if (debug) { - throw e; + var has_selector = selector in w; + } catch(e) { + // If document.domain was set at some point same_origin can be + // wrong and the above will fail. + has_selector = false; + } + if (has_selector) { + try { + w[selector].apply(undefined, callback_args); + } catch (e) { + if (debug) { + throw e; + } } } } if (supports_post_message(w) && w !== self) { w.postMessage(message_arg, "*"); } }); }; @@ -136,51 +180,63 @@ policies and contribution forms [3]. }); }; WindowTestEnvironment.prototype.on_tests_ready = function() { var output = new Output(); this.output_handler = output; var this_obj = this; + add_start_callback(function (properties) { this_obj.output_handler.init(properties); - this_obj._dispatch("start_callback", [properties], - { type: "start", properties: properties }); }); + add_test_state_callback(function(test) { this_obj.output_handler.show_status(); - this_obj._dispatch("test_state_callback", [test], - { type: "test_state", test: test.structured_clone() }); }); + add_result_callback(function (test) { this_obj.output_handler.show_status(); - this_obj._dispatch("result_callback", [test], - { type: "result", test: test.structured_clone() }); }); + add_completion_callback(function (tests, harness_status) { this_obj.output_handler.show_results(tests, harness_status); - var cloned_tests = map(tests, function(test) { return test.structured_clone(); }); - this_obj._dispatch("completion_callback", [tests, harness_status], - { type: "complete", tests: cloned_tests, - status: harness_status.structured_clone() }); }); + this.setup_messages(settings.message_events); }; + WindowTestEnvironment.prototype.setup_messages = function(new_events) { + var this_obj = this; + forEach(settings.message_events, function(x) { + var current_dispatch = this_obj.message_events.indexOf(x) !== -1; + var new_dispatch = new_events.indexOf(x) !== -1; + if (!current_dispatch && new_dispatch) { + this_obj.message_functions[x][0](this_obj.message_functions[x][2]); + } else if (current_dispatch && !new_dispatch) { + this_obj.message_functions[x][1](this_obj.message_functions[x][2]); + } + }); + this.message_events = new_events; + } + WindowTestEnvironment.prototype.next_default_test_name = function() { //Don't use document.title to work around an Opera bug in XHTML documents var title = document.getElementsByTagName("title")[0]; var prefix = (title && title.firstChild && title.firstChild.data) || "Untitled"; var suffix = this.name_counter > 0 ? " " + this.name_counter : ""; this.name_counter++; return prefix + suffix; }; WindowTestEnvironment.prototype.on_new_harness_properties = function(properties) { this.output_handler.setup(properties); + if (properties.hasOwnProperty("message_events")) { + this.setup_messages(properties.message_events); + } }; WindowTestEnvironment.prototype.add_on_loaded_callback = function(callback) { on_event(window, 'load', callback); }; WindowTestEnvironment.prototype.test_timeout = function() { var metas = document.getElementsByTagName("meta"); @@ -354,18 +410,30 @@ policies and contribution forms [3]. function ServiceWorkerTestEnvironment() { WorkerTestEnvironment.call(this); this.all_loaded = false; this.on_loaded_callback = null; var this_obj = this; self.addEventListener("message", function(event) { if (event.data.type && event.data.type === "connect") { - this_obj._add_message_port(event.ports[0]); - event.ports[0].start(); + if (event.ports && event.ports[0]) { + // If a MessageChannel was passed, then use it to + // send results back to the main window. This + // allows the tests to work even if the browser + // does not fully support MessageEvent.source in + // ServiceWorkers yet. + this_obj._add_message_port(event.ports[0]); + event.ports[0].start(); + } else { + // If there is no MessageChannel, then attempt to + // use the MessageEvent.source to send results + // back to the main window. + this_obj._add_message_port(event.source); + } } }); // The oninstall event is received after the service worker script and // all imported scripts have been fetched and executed. It's the // equivalent of an onload event for a document. All tests should have // been added by the time this event is received, thus it's not // necessary to wait until the onactivate event. @@ -459,16 +527,22 @@ policies and contribution forms [3]. if (value instanceof AssertionError) { throw value; } assert(false, "promise_test", null, "Unhandled rejection with value: ${value}", {value:value}); })); } + function promise_rejects(test, expected, promise) { + return promise.then(test.unreached_func("Should have rejected.")).catch(function(e) { + assert_throws(expected, function() { throw e }); + }); + } + /** * This constructor helper allows DOM events to be handled using Promises, * which can make it a lot easier to test a very specific series of events, * including ensuring that unexpected events are not fired at any point. */ function EventWatcher(test, watchedNode, eventTypes) { if (typeof eventTypes == 'string') { @@ -574,16 +648,17 @@ policies and contribution forms [3]. function on_event(object, event, callback) { object.addEventListener(event, callback, false); } expose(test, 'test'); expose(async_test, 'async_test'); expose(promise_test, 'promise_test'); + expose(promise_rejects, 'promise_rejects'); expose(generate_tests, 'generate_tests'); expose(setup, 'setup'); expose(done, 'done'); expose(on_event, 'on_event'); /* * Return a string truncated to the given length, with ... added at the end * if it was longer. @@ -837,17 +912,17 @@ policies and contribution forms [3]. assert(actual.length === expected.length, "assert_array_equals", description, "lengths differ, expected ${expected} got ${actual}", {expected:expected.length, actual:actual.length}); for (var i = 0; i < actual.length; i++) { assert(actual.hasOwnProperty(i) === expected.hasOwnProperty(i), "assert_array_equals", description, - "property ${i}, property expected to be $expected but was $actual", + "property ${i}, property expected to be ${expected} but was ${actual}", {i:i, expected:expected.hasOwnProperty(i) ? "present" : "missing", actual:actual.hasOwnProperty(i) ? "present" : "missing"}); assert(same_value(expected[i], actual[i]), "assert_array_equals", description, "property ${i}, expected ${expected} but got ${actual}", {i:i, expected:expected[i], actual:actual[i]}); } } @@ -928,17 +1003,17 @@ policies and contribution forms [3]. * Test if a primitive number is less than or equal to another */ assert(typeof actual === "number", "assert_less_than_equal", description, "expected a number but got a ${type_actual}", {type_actual:typeof actual}); assert(actual <= expected, - "assert_less_than", description, + "assert_less_than_equal", description, "expected a number less than or equal to ${expected} but got ${actual}", {expected:expected, actual:actual}); } expose(assert_less_than_equal, "assert_less_than_equal"); function assert_greater_than_equal(actual, expected, description) { /* @@ -1120,32 +1195,38 @@ policies and contribution forms [3]. NetworkError: 19, AbortError: 20, URLMismatchError: 21, QuotaExceededError: 22, TimeoutError: 23, InvalidNodeTypeError: 24, DataCloneError: 25, + EncodingError: 0, + NotReadableError: 0, UnknownError: 0, ConstraintError: 0, DataError: 0, TransactionInactiveError: 0, ReadOnlyError: 0, - VersionError: 0 + VersionError: 0, + OperationError: 0, }; if (!(name in name_code_map)) { throw new AssertionError('Test bug: unrecognized DOMException code "' + code + '" passed to assert_throws()'); } var required_props = { code: name_code_map[name] }; if (required_props.code === 0 || - ("name" in e && e.name !== e.name.toUpperCase() && e.name !== "DOMException")) { + (typeof e == "object" && + "name" in e && + e.name !== e.name.toUpperCase() && + e.name !== "DOMException")) { // New style exception: also test the name property. required_props.name = name; } //We'd like to test that e instanceof the appropriate interface, //but we can't, because we don't know what window it was created //in. It might be an instanceof the appropriate interface on some //unknown other window. TODO: Work around this somehow? @@ -1209,16 +1290,17 @@ policies and contribution forms [3]. var timeout = properties.timeout ? properties.timeout : settings.test_timeout; if (timeout !== null) { this.timeout_length = timeout * tests.timeout_multiplier; } else { this.timeout_length = null; } this.message = null; + this.stack = null; this.steps = []; this.cleanup_callbacks = []; tests.push(this); } @@ -1245,16 +1327,17 @@ policies and contribution forms [3]. msg = msg ? String(msg) : msg; this._structured_clone = merge({ name:String(this.name), properties:merge({}, this.properties), }, Test.statuses); } this._structured_clone.status = this.status; this._structured_clone.message = this.message; + this._structured_clone.stack = this.stack; this._structured_clone.index = this.index; return this._structured_clone; }; Test.prototype.step = function(func, this_obj) { if (this.phase > this.phases.STARTED) { return; @@ -1277,25 +1360,20 @@ policies and contribution forms [3]. } try { return func.apply(this_obj, Array.prototype.slice.call(arguments, 2)); } catch (e) { if (this.phase >= this.phases.HAS_RESULT) { return; } - var message = (typeof e === "object" && e !== null) ? e.message : e; - if (typeof e.stack != "undefined" && typeof e.message == "string") { - //Try to make it more informative for some exceptions, at least - //in Gecko and WebKit. This results in a stack dump instead of - //just errors like "Cannot read property 'parentNode' of null" - //or "root is null". Makes it a lot longer, of course. - message += "(stack: " + e.stack + ")"; - } - this.set_status(this.FAIL, message); + var message = String((typeof e === "object" && e !== null) ? e.message : e); + var stack = e.stack ? e.stack : null; + + this.set_status(this.FAIL, message, stack); this.phase = this.phases.HAS_RESULT; this.done(); } }; Test.prototype.step_func = function(func, this_obj) { var test_this = this; @@ -1351,20 +1429,21 @@ policies and contribution forms [3]. var this_obj = this; this.timeout_id = setTimeout(function() { this_obj.timeout(); }, this.timeout_length); } }; - Test.prototype.set_status = function(status, message) + Test.prototype.set_status = function(status, message, stack) { this.status = status; this.message = message; + this.stack = stack ? stack : null; }; Test.prototype.timeout = function() { this.timeout_id = null; this.set_status(this.TIMEOUT, "Test timed out"); this.phase = this.phases.HAS_RESULT; this.done(); @@ -1411,32 +1490,33 @@ policies and contribution forms [3]. this.phase = this.phases.INITIAL; this.update_state_from(clone); tests.push(this); } RemoteTest.prototype.structured_clone = function() { var clone = {}; Object.keys(this).forEach( - function(key) { + (function(key) { if (typeof(this[key]) === "object") { clone[key] = merge({}, this[key]); } else { clone[key] = this[key]; } - }); + }).bind(this)); clone.phases = merge({}, this.phases); return clone; }; RemoteTest.prototype.cleanup = function() {}; RemoteTest.prototype.phases = Test.prototype.phases; RemoteTest.prototype.update_state_from = function(clone) { this.status = clone.status; this.message = clone.message; + this.stack = clone.stack; if (this.phase === this.phases.INITIAL) { this.phase = this.phases.STARTED; } }; RemoteTest.prototype.done = function() { this.phase = this.phases.COMPLETE; } @@ -1450,25 +1530,34 @@ policies and contribution forms [3]. this.tests = new Array(); var this_obj = this; worker.onerror = function(error) { this_obj.worker_error(error); }; var message_port; if (is_service_worker(worker)) { - // The ServiceWorker's implicit MessagePort is currently not - // reliably accessible from the ServiceWorkerGlobalScope due to - // Blink setting MessageEvent.source to null for messages sent via - // ServiceWorker.postMessage(). Until that's resolved, create an - // explicit MessageChannel and pass one end to the worker. - var message_channel = new MessageChannel(); - message_port = message_channel.port1; - message_port.start(); - worker.postMessage({type: "connect"}, [message_channel.port2]); + if (window.MessageChannel) { + // The ServiceWorker's implicit MessagePort is currently not + // reliably accessible from the ServiceWorkerGlobalScope due to + // Blink setting MessageEvent.source to null for messages sent + // via ServiceWorker.postMessage(). Until that's resolved, + // create an explicit MessageChannel and pass one end to the + // worker. + var message_channel = new MessageChannel(); + message_port = message_channel.port1; + message_port.start(); + worker.postMessage({type: "connect"}, [message_channel.port2]); + } else { + // If MessageChannel is not available, then try the + // ServiceWorker.postMessage() approach using MessageEvent.source + // on the other end. + message_port = navigator.serviceWorker; + worker.postMessage({type: "connect"}); + } } else if (is_shared_worker(worker)) { message_port = worker.port; } else { message_port = worker; } // Keeping a reference to the worker until worker_done() is seen // prevents the Worker object and its MessageChannel from going away @@ -1486,17 +1575,18 @@ policies and contribution forms [3]. RemoteWorker.prototype.worker_error = function(error) { var message = error.message || String(error); var filename = (error.filename ? " " + error.filename: ""); // FIXME: Display worker error states separately from main document // error state. this.worker_done({ status: { status: tests.status.ERROR, - message: "Error in worker" + filename + ": " + message + message: "Error in worker" + filename + ": " + message, + stack: error.stack } }); error.preventDefault(); }; RemoteWorker.prototype.test_state = function(data) { var remote_test = this.tests[data.test.index]; if (!remote_test) { @@ -1514,16 +1604,17 @@ policies and contribution forms [3]. tests.result(remote_test); }; RemoteWorker.prototype.worker_done = function(data) { if (tests.status.status === null && data.status.status !== data.status.OK) { tests.status.status = data.status.status; tests.status.message = data.status.message; + tests.status.stack = data.status.stack; } this.running = false; this.worker = null; if (tests.all_done()) { tests.complete(); } }; @@ -1536,16 +1627,17 @@ policies and contribution forms [3]. /* * Harness */ function TestsStatus() { this.status = null; this.message = null; + this.stack = null; } TestsStatus.statuses = { OK:0, ERROR:1, TIMEOUT:2 }; @@ -1553,17 +1645,18 @@ policies and contribution forms [3]. TestsStatus.prototype.structured_clone = function() { if (!this._structured_clone) { var msg = this.message; msg = msg ? String(msg) : msg; this._structured_clone = merge({ status:this.status, - message:msg + message:msg, + stack:this.stack }, TestsStatus.statuses); } return this._structured_clone; }; function Tests() { this.tests = []; @@ -1643,16 +1736,17 @@ policies and contribution forms [3]. } if (func) { try { func(); } catch (e) { this.status.status = this.status.ERROR; this.status.message = String(e); + this.status.stack = e.stack ? e.stack : null; } } this.set_timeout(); }; Tests.prototype.set_file_is_test = function() { if (this.tests.length > 0) { throw new Error("Tried to set file as test after creating a test"); @@ -1806,31 +1900,52 @@ policies and contribution forms [3]. function add_start_callback(callback) { tests.start_callbacks.push(callback); } function add_test_state_callback(callback) { tests.test_state_callbacks.push(callback); } - function add_result_callback(callback) - { + function add_result_callback(callback) { tests.test_done_callbacks.push(callback); } - function add_completion_callback(callback) - { - tests.all_done_callbacks.push(callback); + function add_completion_callback(callback) { + tests.all_done_callbacks.push(callback); } expose(add_start_callback, 'add_start_callback'); expose(add_test_state_callback, 'add_test_state_callback'); expose(add_result_callback, 'add_result_callback'); expose(add_completion_callback, 'add_completion_callback'); + function remove(array, item) { + var index = array.indexOf(item); + if (index > -1) { + array.splice(index, 1); + } + } + + function remove_start_callback(callback) { + remove(tests.start_callbacks, callback); + } + + function remove_test_state_callback(callback) { + remove(tests.test_state_callbacks, callback); + } + + function remove_result_callback(callback) { + remove(tests.test_done_callbacks, callback); + } + + function remove_completion_callback(callback) { + remove(tests.all_done_callbacks, callback); + } + /* * Output listener */ function Output() { this.output_document = document; this.output_node = null; this.enabled = settings.output; @@ -2000,16 +2115,19 @@ policies and contribution forms [3]. ["span", {"class":status_class(status)}, status ], ] ]]; if (harness_status.status === harness_status.ERROR) { rv[0].push(["pre", {}, harness_status.message]); + if (harness_status.stack) { + rv[0].push(["pre", {}, harness_status.stack]); + } } return rv; }, ["p", {}, "Found ${num_tests} tests"], function() { var rv = [["div", {}]]; var i = 0; while (status_text.hasOwnProperty(i)) { @@ -2097,16 +2215,19 @@ policies and contribution forms [3]. escape_html(status_class(status_text[tests[i].status])) + '"><td>' + escape_html(status_text[tests[i].status]) + "</td><td>" + escape_html(tests[i].name) + "</td><td>" + (assertions ? escape_html(get_assertion(tests[i])) + "</td><td>" : "") + escape_html(tests[i].message ? tests[i].message : " ") + + (tests[i].stack ? "<pre>" + + escape_html(tests[i].stack) + + "</pre>": "") + "</td></tr>"; } html += "</tbody></table>"; try { log.lastChild.innerHTML = html; } catch (e) { log.appendChild(document.createElementNS(xhtml_ns, "p")) .textContent = "Setting innerHTML for the log threw an exception."; @@ -2292,21 +2413,44 @@ policies and contribution forms [3]. error, substitutions); throw new AssertionError(msg); } } function AssertionError(message) { this.message = message; + this.stack = this.get_stack(); } - AssertionError.prototype.toString = function() { - return this.message; - }; + AssertionError.prototype = Object.create(Error.prototype); + + AssertionError.prototype.get_stack = function() { + var stack = new Error().stack; + if (!stack) { + try { + throw new Error(); + } catch (e) { + stack = e.stack; + } + } + var lines = stack.split("\n"); + var rv = []; + var re = /\/resources\/testharness\.js/; + var i = 0; + // Fire remove any preamble that doesn't match the regexp + while (!re.test(lines[i])) { + i++ + } + // Then remove top frames in testharness.js itself + while (re.test(lines[i])) { + i++ + } + return lines.slice(i).join("\n"); + } function make_message(function_name, description, error, substitutions) { for (var p in substitutions) { if (substitutions.hasOwnProperty(p)) { substitutions[p] = format_value(substitutions[p]); } } @@ -2435,23 +2579,23 @@ policies and contribution forms [3]. var tests = new Tests(); addEventListener("error", function(e) { if (tests.file_is_test) { var test = tests.tests[0]; if (test.phase >= test.phases.HAS_RESULT) { return; } - var message = e.message; - test.set_status(test.FAIL, message); + test.set_status(test.FAIL, e.message, e.stack); test.phase = test.phases.HAS_RESULT; test.done(); done(); } else if (!tests.allow_uncaught_exception) { tests.status.status = tests.status.ERROR; tests.status.message = e.message; + tests.status.stack = e.stack; } }); test_environment.on_tests_ready(); })(); // vim: set expandtab shiftwidth=4 tabstop=4: