diff options
Diffstat (limited to 'dom/imptests')
101 files changed, 10724 insertions, 0 deletions
diff --git a/dom/imptests/README b/dom/imptests/README new file mode 100644 index 000000000..13906cef9 --- /dev/null +++ b/dom/imptests/README @@ -0,0 +1,106 @@ +This directory contains tests imported from W3C test suites. In order to make it +as easy as possible to update these tests, no changes are made to the imported +files (names for scripted tests do get a test_ prefix to integrate with the test +runner, however). The scripts to update tests are provided. + + +======================= +Files in this directory +======================= + +Source; Usage and purpose; License + +* testharness.js / testharness.css + Directly imported from the W3C repository (<http://dvcs.w3.org/hg/resources>), + with the updateTestharness.py script. + Provide the test harness. + W3C Test Suite License / W3C 3-clause BSD License + +* idlharness.js + Directly imported from the W3C repository (<http://dvcs.w3.org/hg/resources>), + with the updateTestharness.py script. + Used to test WebIDL. + W3C Test Suite License / W3C 3-clause BSD License + +* WebIDLParser.js + Directly imported from the W3C repository (<http://dvcs.w3.org/hg/resources>), + with the updateTestharness.py script. + Used by idlharness.js to parse IDL blocks. + MIT License + +* updateTestharness.py + Used to update the above files. + MPL + +* parseManifest.py + Imported from <https://bitbucket.org/ms2ger/test-runner>. Parses MANIFEST + files (provided in the W3C repository) as documented at + <https://bitbucket.org/ms2ger/test-runner/raw/tip/manifests.txt>. + MIT License + +* testharnessreport.js + Glue between testharness.js and our Mochitest runner. + MPL + +* importTestsuite.py + Imports a test suite from a remote repository. Takes one argument, a file in + the format described under webapps.txt. + Note: removes both source and destination directory before starting. Do not + use with outstanding changes in either directory. + MPL + +* parseFailures.py + Parses failures out of a mochitest log and writes out JSON files and Makefiles + into the correct failures/ folder. + The mochitest log should be produced by setting the 'dumpFailures' flag in + testharnessreport.js; this will print out the encountered failures, marked + by @ signs. + MPL + +* writeBuildFiles.py + Helper functions to write out automatically generated build files. + MPL + +* Makefile.in + Integration with our build system. Installs support files into /resources and + includes a .mk file for each repository. + MPL + +* failures/ + Expected failures for tests in each repository. Each test's failures, if + any, are in a file with the same path and name with .json appended. New + expected fail files currently needed to be added manually to makefiles. + +* html.mk / webapps.mk / ... + Generated by importTestsuite.py from webapps.txt. + Contains a list of the directories with tests. To be included in Makefile.in. + +* html.txt / webapps.txt / ... + Input to importTestsuite.py. + Lists the URL of the repository and the destination directory (separated by a + vertical bar), followed by a list of directories within the repository + (separated by line feeds). + +* html / webapps / ... + Actual tests. + W3C Test Suite License / W3C 3-clause BSD License + + +===================================================================== +Importing an additional directory from an already-imported repository +===================================================================== + +Add a line to the relevant data file (e.g. webapps.txt), with the path to the +additional directory relative to the root of the remote repository, and then run +the importTestsuite.py script, passing the data file as its argument. + + +========================== +Importing a new test suite +========================== + +Create a data file in the format documented above, and run the +importTestsuite.py script, passing the data file as its argument. +This will create a foo.mk file; include this file in dom/imptests/Makefile.in. + +Add any necessary files in failures/. diff --git a/dom/imptests/WebIDLParser.js b/dom/imptests/WebIDLParser.js new file mode 100644 index 000000000..103a7f48b --- /dev/null +++ b/dom/imptests/WebIDLParser.js @@ -0,0 +1,924 @@ + + +(function () { + var tokenise = function (str) { + var tokens = [] + , re = { + "float": /^-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/ + , "integer": /^-?(0([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*)/ + , "identifier": /^[A-Z_a-z][0-9A-Z_a-z]*/ + , "string": /^"[^"]*"/ + , "whitespace": /^(?:[\t\n\r ]+|[\t\n\r ]*((\/\/.*|\/\*(.|\n|\r)*?\*\/)[\t\n\r ]*))+/ + , "other": /^[^\t\n\r 0-9A-Z_a-z]/ + } + , types = [] + ; + for (var k in re) types.push(k); + while (str.length > 0) { + var matched = false; + for (var i = 0, n = types.length; i < n; i++) { + var type = types[i]; + str = str.replace(re[type], function (tok) { + tokens.push({ type: type, value: tok }); + matched = true; + return ""; + }); + if (matched) break; + } + if (matched) continue; + throw new Error("Token stream not progressing"); + } + return tokens; + }; + + var parse = function (tokens, opt) { + var line = 1; + tokens = tokens.slice(); + + var FLOAT = "float" + , INT = "integer" + , ID = "identifier" + , STR = "string" + , OTHER = "other" + ; + + var WebIDLParseError = function (str, line, input, tokens) { + this.message = str; + this.line = line; + this.input = input; + this.tokens = tokens; + }; + WebIDLParseError.prototype.toString = function () { + return this.message + ", line " + this.line + " (tokens: '" + this.input + "')\n" + + JSON.stringify(this.tokens, null, 4); + }; + + var error = function (str) { + var tok = "", numTokens = 0, maxTokens = 5; + while (numTokens < maxTokens && tokens.length > numTokens) { + tok += tokens[numTokens].value; + numTokens++; + } + throw new WebIDLParseError(str, line, tok, tokens.slice(0, 5)); + }; + + var last_token = null; + + var consume = function (type, value) { + if (!tokens.length || tokens[0].type !== type) return; + if (typeof value === "undefined" || tokens[0].value === value) { + last_token = tokens.shift(); + if (type === ID) last_token.value = last_token.value.replace(/^_/, ""); + return last_token; + } + }; + + var ws = function () { + if (!tokens.length) return; + if (tokens[0].type === "whitespace") { + var t = tokens.shift(); + t.value.replace(/\n/g, function (m) { line++; return m; }); + return t; + } + }; + + var all_ws = function (store, pea) { // pea == post extended attribute, tpea = same for types + var t = { type: "whitespace", value: "" }; + while (true) { + var w = ws(); + if (!w) break; + t.value += w.value; + } + if (t.value.length > 0) { + if (store) { + var w = t.value + , re = { + "ws": /^([\t\n\r ]+)/ + , "line-comment": /^\/\/(.*)\n?/m + , "multiline-comment": /^\/\*((?:.|\n|\r)*?)\*\// + } + , wsTypes = [] + ; + for (var k in re) wsTypes.push(k); + while (w.length) { + var matched = false; + for (var i = 0, n = wsTypes.length; i < n; i++) { + var type = wsTypes[i]; + w = w.replace(re[type], function (tok, m1) { + store.push({ type: type + (pea ? ("-" + pea) : ""), value: m1 }); + matched = true; + return ""; + }); + if (matched) break; + } + if (matched) continue; + throw new Error("Surprising white space construct."); // this shouldn't happen + } + } + return t; + } + }; + + var integer_type = function () { + var ret = ""; + all_ws(); + if (consume(ID, "unsigned")) ret = "unsigned "; + all_ws(); + if (consume(ID, "short")) return ret + "short"; + if (consume(ID, "long")) { + ret += "long"; + all_ws(); + if (consume(ID, "long")) return ret + " long"; + return ret; + } + if (ret) error("Failed to parse integer type"); + }; + + var float_type = function () { + var ret = ""; + all_ws(); + if (consume(ID, "unrestricted")) ret = "unrestricted "; + all_ws(); + if (consume(ID, "float")) return ret + "float"; + if (consume(ID, "double")) return ret + "double"; + if (ret) error("Failed to parse float type"); + }; + + var primitive_type = function () { + var num_type = integer_type() || float_type(); + if (num_type) return num_type; + all_ws(); + if (consume(ID, "boolean")) return "boolean"; + if (consume(ID, "byte")) return "byte"; + if (consume(ID, "octet")) return "octet"; + }; + + var const_value = function () { + if (consume(ID, "true")) return { type: "boolean", value: true }; + if (consume(ID, "false")) return { type: "boolean", value: false }; + if (consume(ID, "null")) return { type: "null" }; + if (consume(ID, "Infinity")) return { type: "Infinity", negative: false }; + if (consume(ID, "NaN")) return { type: "NaN" }; + var ret = consume(FLOAT) || consume(INT); + if (ret) return { type: "number", value: 1 * ret.value }; + var tok = consume(OTHER, "-"); + if (tok) { + if (consume(ID, "Infinity")) return { type: "Infinity", negative: true }; + else tokens.unshift(tok); + } + }; + + var type_suffix = function (obj) { + while (true) { + all_ws(); + if (consume(OTHER, "?")) { + if (obj.nullable) error("Can't nullable more than once"); + obj.nullable = true; + } + else if (consume(OTHER, "[")) { + all_ws(); + consume(OTHER, "]") || error("Unterminated array type"); + if (!obj.array) { + obj.array = 1; + obj.nullableArray = [obj.nullable]; + } + else { + obj.array++; + obj.nullableArray.push(obj.nullable); + } + obj.nullable = false; + } + else return; + } + }; + + var single_type = function () { + var prim = primitive_type() + , ret = { sequence: false, generic: null, nullable: false, array: false, union: false } + , name + , value + ; + if (prim) { + ret.idlType = prim; + } + else if (name = consume(ID)) { + value = name.value; + all_ws(); + // Generic types + if (consume(OTHER, "<")) { + // backwards compat + if (value === "sequence") { + ret.sequence = true; + } + ret.generic = value; + ret.idlType = type() || error("Error parsing generic type " + value); + all_ws(); + if (!consume(OTHER, ">")) error("Unterminated generic type " + value); + all_ws(); + if (consume(OTHER, "?")) ret.nullable = true; + return ret; + } + else { + ret.idlType = value; + } + } + else { + return; + } + type_suffix(ret); + if (ret.nullable && !ret.array && ret.idlType === "any") error("Type any cannot be made nullable"); + return ret; + }; + + var union_type = function () { + all_ws(); + if (!consume(OTHER, "(")) return; + var ret = { sequence: false, generic: null, nullable: false, array: false, union: true, idlType: [] }; + var fst = type() || error("Union type with no content"); + ret.idlType.push(fst); + while (true) { + all_ws(); + if (!consume(ID, "or")) break; + var typ = type() || error("No type after 'or' in union type"); + ret.idlType.push(typ); + } + if (!consume(OTHER, ")")) error("Unterminated union type"); + type_suffix(ret); + return ret; + }; + + var type = function () { + return single_type() || union_type(); + }; + + var argument = function (store) { + var ret = { optional: false, variadic: false }; + ret.extAttrs = extended_attrs(store); + all_ws(store, "pea"); + var opt_token = consume(ID, "optional"); + if (opt_token) { + ret.optional = true; + all_ws(); + } + ret.idlType = type(); + if (!ret.idlType) { + if (opt_token) tokens.unshift(opt_token); + return; + } + var type_token = last_token; + if (!ret.optional) { + all_ws(); + if (tokens.length >= 3 && + tokens[0].type === "other" && tokens[0].value === "." && + tokens[1].type === "other" && tokens[1].value === "." && + tokens[2].type === "other" && tokens[2].value === "." + ) { + tokens.shift(); + tokens.shift(); + tokens.shift(); + ret.variadic = true; + } + } + all_ws(); + var name = consume(ID); + if (!name) { + if (opt_token) tokens.unshift(opt_token); + tokens.unshift(type_token); + return; + } + ret.name = name.value; + if (ret.optional) { + all_ws(); + ret["default"] = default_(); + } + return ret; + }; + + var argument_list = function (store) { + var ret = [] + , arg = argument(store ? ret : null) + ; + if (!arg) return; + ret.push(arg); + while (true) { + all_ws(store ? ret : null); + if (!consume(OTHER, ",")) return ret; + var nxt = argument(store ? ret : null) || error("Trailing comma in arguments list"); + ret.push(nxt); + } + }; + + var type_pair = function () { + all_ws(); + var k = type(); + if (!k) return; + all_ws() + if (!consume(OTHER, ",")) return; + all_ws(); + var v = type(); + if (!v) return; + return [k, v]; + }; + + var simple_extended_attr = function (store) { + all_ws(); + var name = consume(ID); + if (!name) return; + var ret = { + name: name.value + , "arguments": null + }; + all_ws(); + var eq = consume(OTHER, "="); + if (eq) { + all_ws(); + ret.rhs = consume(ID); + if (!ret.rhs) return error("No right hand side to extended attribute assignment"); + } + all_ws(); + if (consume(OTHER, "(")) { + var args, pair; + // [Constructor(DOMString str)] + if (args = argument_list(store)) { + ret["arguments"] = args; + } + // [MapClass(DOMString, DOMString)] + else if (pair = type_pair()) { + ret.typePair = pair; + } + // [Constructor()] + else { + ret["arguments"] = []; + } + all_ws(); + consume(OTHER, ")") || error("Unexpected token in extended attribute argument list or type pair"); + } + return ret; + }; + + // Note: we parse something simpler than the official syntax. It's all that ever + // seems to be used + var extended_attrs = function (store) { + var eas = []; + all_ws(store); + if (!consume(OTHER, "[")) return eas; + eas[0] = simple_extended_attr(store) || error("Extended attribute with not content"); + all_ws(); + while (consume(OTHER, ",")) { + eas.push(simple_extended_attr(store) || error("Trailing comma in extended attribute")); + all_ws(); + } + consume(OTHER, "]") || error("No end of extended attribute"); + return eas; + }; + + var default_ = function () { + all_ws(); + if (consume(OTHER, "=")) { + all_ws(); + var def = const_value(); + if (def) { + return def; + } + else { + var str = consume(STR) || error("No value for default"); + str.value = str.value.replace(/^"/, "").replace(/"$/, ""); + return str; + } + } + }; + + var const_ = function (store) { + all_ws(store, "pea"); + if (!consume(ID, "const")) return; + var ret = { type: "const", nullable: false }; + all_ws(); + var typ = primitive_type(); + if (!typ) { + typ = consume(ID) || error("No type for const"); + typ = typ.value; + } + ret.idlType = typ; + all_ws(); + if (consume(OTHER, "?")) { + ret.nullable = true; + all_ws(); + } + var name = consume(ID) || error("No name for const"); + ret.name = name.value; + all_ws(); + consume(OTHER, "=") || error("No value assignment for const"); + all_ws(); + var cnt = const_value(); + if (cnt) ret.value = cnt; + else error("No value for const"); + all_ws(); + consume(OTHER, ";") || error("Unterminated const"); + return ret; + }; + + var inheritance = function () { + all_ws(); + if (consume(OTHER, ":")) { + all_ws(); + var inh = consume(ID) || error ("No type in inheritance"); + return inh.value; + } + }; + + var operation_rest = function (ret, store) { + all_ws(); + if (!ret) ret = {}; + var name = consume(ID); + ret.name = name ? name.value : null; + all_ws(); + consume(OTHER, "(") || error("Invalid operation"); + ret["arguments"] = argument_list(store) || []; + all_ws(); + consume(OTHER, ")") || error("Unterminated operation"); + all_ws(); + consume(OTHER, ";") || error("Unterminated operation"); + return ret; + }; + + var callback = function (store) { + all_ws(store, "pea"); + var ret; + if (!consume(ID, "callback")) return; + all_ws(); + var tok = consume(ID, "interface"); + if (tok) { + tokens.unshift(tok); + ret = interface_(); + ret.type = "callback interface"; + return ret; + } + var name = consume(ID) || error("No name for callback"); + ret = { type: "callback", name: name.value }; + all_ws(); + consume(OTHER, "=") || error("No assignment in callback"); + all_ws(); + ret.idlType = return_type(); + all_ws(); + consume(OTHER, "(") || error("No arguments in callback"); + ret["arguments"] = argument_list(store) || []; + all_ws(); + consume(OTHER, ")") || error("Unterminated callback"); + all_ws(); + consume(OTHER, ";") || error("Unterminated callback"); + return ret; + }; + + var attribute = function (store) { + all_ws(store, "pea"); + var grabbed = [] + , ret = { + type: "attribute" + , "static": false + , stringifier: false + , inherit: false + , readonly: false + }; + if (consume(ID, "static")) { + ret["static"] = true; + grabbed.push(last_token); + } + else if (consume(ID, "stringifier")) { + ret.stringifier = true; + grabbed.push(last_token); + } + var w = all_ws(); + if (w) grabbed.push(w); + if (consume(ID, "inherit")) { + if (ret["static"] || ret.stringifier) error("Cannot have a static or stringifier inherit"); + ret.inherit = true; + grabbed.push(last_token); + var w = all_ws(); + if (w) grabbed.push(w); + } + if (consume(ID, "readonly")) { + ret.readonly = true; + grabbed.push(last_token); + var w = all_ws(); + if (w) grabbed.push(w); + } + if (!consume(ID, "attribute")) { + tokens = grabbed.concat(tokens); + return; + } + all_ws(); + ret.idlType = type() || error("No type in attribute"); + if (ret.idlType.sequence) error("Attributes cannot accept sequence types"); + all_ws(); + var name = consume(ID) || error("No name in attribute"); + ret.name = name.value; + all_ws(); + consume(OTHER, ";") || error("Unterminated attribute"); + return ret; + }; + + var return_type = function () { + var typ = type(); + if (!typ) { + if (consume(ID, "void")) { + return "void"; + } + else error("No return type"); + } + return typ; + }; + + var operation = function (store) { + all_ws(store, "pea"); + var ret = { + type: "operation" + , getter: false + , setter: false + , creator: false + , deleter: false + , legacycaller: false + , "static": false + , stringifier: false + }; + while (true) { + all_ws(); + if (consume(ID, "getter")) ret.getter = true; + else if (consume(ID, "setter")) ret.setter = true; + else if (consume(ID, "creator")) ret.creator = true; + else if (consume(ID, "deleter")) ret.deleter = true; + else if (consume(ID, "legacycaller")) ret.legacycaller = true; + else break; + } + if (ret.getter || ret.setter || ret.creator || ret.deleter || ret.legacycaller) { + all_ws(); + ret.idlType = return_type(); + operation_rest(ret, store); + return ret; + } + if (consume(ID, "static")) { + ret["static"] = true; + ret.idlType = return_type(); + operation_rest(ret, store); + return ret; + } + else if (consume(ID, "stringifier")) { + ret.stringifier = true; + all_ws(); + if (consume(OTHER, ";")) return ret; + ret.idlType = return_type(); + operation_rest(ret, store); + return ret; + } + ret.idlType = return_type(); + all_ws(); + if (consume(ID, "iterator")) { + all_ws(); + ret.type = "iterator"; + if (consume(ID, "object")) { + ret.iteratorObject = "object"; + } + else if (consume(OTHER, "=")) { + all_ws(); + var name = consume(ID) || error("No right hand side in iterator"); + ret.iteratorObject = name.value; + } + all_ws(); + consume(OTHER, ";") || error("Unterminated iterator"); + return ret; + } + else { + operation_rest(ret, store); + return ret; + } + }; + + var identifiers = function (arr) { + while (true) { + all_ws(); + if (consume(OTHER, ",")) { + all_ws(); + var name = consume(ID) || error("Trailing comma in identifiers list"); + arr.push(name.value); + } + else break; + } + }; + + var serialiser = function (store) { + all_ws(store, "pea"); + if (!consume(ID, "serializer")) return; + var ret = { type: "serializer" }; + all_ws(); + if (consume(OTHER, "=")) { + all_ws(); + if (consume(OTHER, "{")) { + ret.patternMap = true; + all_ws(); + var id = consume(ID); + if (id && id.value === "getter") { + ret.names = ["getter"]; + } + else if (id && id.value === "inherit") { + ret.names = ["inherit"]; + identifiers(ret.names); + } + else if (id) { + ret.names = [id.value]; + identifiers(ret.names); + } + else { + ret.names = []; + } + all_ws(); + consume(OTHER, "}") || error("Unterminated serializer pattern map"); + } + else if (consume(OTHER, "[")) { + ret.patternList = true; + all_ws(); + var id = consume(ID); + if (id && id.value === "getter") { + ret.names = ["getter"]; + } + else if (id) { + ret.names = [id.value]; + identifiers(ret.names); + } + else { + ret.names = []; + } + all_ws(); + consume(OTHER, "]") || error("Unterminated serializer pattern list"); + } + else { + var name = consume(ID) || error("Invalid serializer"); + ret.name = name.value; + } + all_ws(); + consume(OTHER, ";") || error("Unterminated serializer"); + return ret; + } + else if (consume(OTHER, ";")) { + // noop, just parsing + } + else { + ret.idlType = return_type(); + all_ws(); + ret.operation = operation_rest(null, store); + } + return ret; + }; + + var interface_ = function (isPartial, store) { + all_ws(isPartial ? null : store, "pea"); + if (!consume(ID, "interface")) return; + all_ws(); + var name = consume(ID) || error("No name for interface"); + var mems = [] + , ret = { + type: "interface" + , name: name.value + , partial: false + , members: mems + }; + if (!isPartial) ret.inheritance = inheritance() || null; + all_ws(); + consume(OTHER, "{") || error("Bodyless interface"); + while (true) { + all_ws(store ? mems : null); + if (consume(OTHER, "}")) { + all_ws(); + consume(OTHER, ";") || error("Missing semicolon after interface"); + return ret; + } + var ea = extended_attrs(store ? mems : null); + all_ws(); + var cnt = const_(store ? mems : null); + if (cnt) { + cnt.extAttrs = ea; + ret.members.push(cnt); + continue; + } + var mem = serialiser(store ? mems : null) || + attribute(store ? mems : null) || + operation(store ? mems : null) || + error("Unknown member"); + mem.extAttrs = ea; + ret.members.push(mem); + } + }; + + var partial = function (store) { + all_ws(store, "pea"); + if (!consume(ID, "partial")) return; + var thing = dictionary(true, store) || + interface_(true, store) || + error("Partial doesn't apply to anything"); + thing.partial = true; + return thing; + }; + + var dictionary = function (isPartial, store) { + all_ws(isPartial ? null : store, "pea"); + if (!consume(ID, "dictionary")) return; + all_ws(); + var name = consume(ID) || error("No name for dictionary"); + var mems = [] + , ret = { + type: "dictionary" + , name: name.value + , partial: false + , members: mems + }; + if (!isPartial) ret.inheritance = inheritance() || null; + all_ws(); + consume(OTHER, "{") || error("Bodyless dictionary"); + while (true) { + all_ws(store ? mems : null); + if (consume(OTHER, "}")) { + all_ws(); + consume(OTHER, ";") || error("Missing semicolon after dictionary"); + return ret; + } + var ea = extended_attrs(store ? mems : null); + all_ws(store ? mems : null, "pea"); + var typ = type() || error("No type for dictionary member"); + all_ws(); + var name = consume(ID) || error("No name for dictionary member"); + ret.members.push({ + type: "field" + , name: name.value + , idlType: typ + , extAttrs: ea + , "default": default_() + }); + all_ws(); + consume(OTHER, ";") || error("Unterminated dictionary member"); + } + }; + + var exception = function (store) { + all_ws(store, "pea"); + if (!consume(ID, "exception")) return; + all_ws(); + var name = consume(ID) || error("No name for exception"); + var mems = [] + , ret = { + type: "exception" + , name: name.value + , members: mems + }; + ret.inheritance = inheritance() || null; + all_ws(); + consume(OTHER, "{") || error("Bodyless exception"); + while (true) { + all_ws(store ? mems : null); + if (consume(OTHER, "}")) { + all_ws(); + consume(OTHER, ";") || error("Missing semicolon after exception"); + return ret; + } + var ea = extended_attrs(store ? mems : null); + all_ws(store ? mems : null, "pea"); + var cnt = const_(); + if (cnt) { + cnt.extAttrs = ea; + ret.members.push(cnt); + } + else { + var typ = type(); + all_ws(); + var name = consume(ID); + all_ws(); + if (!typ || !name || !consume(OTHER, ";")) error("Unknown member in exception body"); + ret.members.push({ + type: "field" + , name: name.value + , idlType: typ + , extAttrs: ea + }); + } + } + }; + + var enum_ = function (store) { + all_ws(store, "pea"); + if (!consume(ID, "enum")) return; + all_ws(); + var name = consume(ID) || error("No name for enum"); + var vals = [] + , ret = { + type: "enum" + , name: name.value + , values: vals + }; + all_ws(); + consume(OTHER, "{") || error("No curly for enum"); + var saw_comma = false; + while (true) { + all_ws(store ? vals : null); + if (consume(OTHER, "}")) { + all_ws(); + if (saw_comma) error("Trailing comma in enum"); + consume(OTHER, ";") || error("No semicolon after enum"); + return ret; + } + var val = consume(STR) || error("Unexpected value in enum"); + ret.values.push(val.value.replace(/"/g, "")); + all_ws(store ? vals : null); + if (consume(OTHER, ",")) { + if (store) vals.push({ type: "," }); + all_ws(store ? vals : null); + saw_comma = true; + } + else { + saw_comma = false; + } + } + }; + + var typedef = function (store) { + all_ws(store, "pea"); + if (!consume(ID, "typedef")) return; + var ret = { + type: "typedef" + }; + all_ws(); + ret.typeExtAttrs = extended_attrs(); + all_ws(store, "tpea"); + ret.idlType = type() || error("No type in typedef"); + all_ws(); + var name = consume(ID) || error("No name in typedef"); + ret.name = name.value; + all_ws(); + consume(OTHER, ";") || error("Unterminated typedef"); + return ret; + }; + + var implements_ = function (store) { + all_ws(store, "pea"); + var target = consume(ID); + if (!target) return; + var w = all_ws(); + if (consume(ID, "implements")) { + var ret = { + type: "implements" + , target: target.value + }; + all_ws(); + var imp = consume(ID) || error("Incomplete implements statement"); + ret["implements"] = imp.value; + all_ws(); + consume(OTHER, ";") || error("No terminating ; for implements statement"); + return ret; + } + else { + // rollback + tokens.unshift(w); + tokens.unshift(target); + } + }; + + var definition = function (store) { + return callback(store) || + interface_(false, store) || + partial(store) || + dictionary(false, store) || + exception(store) || + enum_(store) || + typedef(store) || + implements_(store) + ; + }; + + var definitions = function (store) { + if (!tokens.length) return []; + var defs = []; + while (true) { + var ea = extended_attrs(store ? defs : null) + , def = definition(store ? defs : null); + if (!def) { + if (ea.length) error("Stray extended attributes"); + break; + } + def.extAttrs = ea; + defs.push(def); + } + return defs; + }; + var res = definitions(opt.ws); + if (tokens.length) error("Unrecognised tokens"); + return res; + }; + + var inNode = typeof module !== "undefined" && module.exports + , obj = { + parse: function (str, opt) { + if (!opt) opt = {}; + var tokens = tokenise(str); + return parse(tokens, opt); + } + }; + + if (inNode) module.exports = obj; + else self.WebIDL2 = obj; +}()); diff --git a/dom/imptests/editing/mochitest.ini b/dom/imptests/editing/mochitest.ini new file mode 100644 index 000000000..b5c82f5d4 --- /dev/null +++ b/dom/imptests/editing/mochitest.ini @@ -0,0 +1,26 @@ +# THIS FILE IS AUTOGENERATED BY importTestsuite.py - DO NOT EDIT +[DEFAULT] +support-files = + conformancetest/data.js + css/reset.css + implementation.js + selecttest/common.js + selecttest/test-iframe.html + tests.js + +[conformancetest/test_event.html] +[conformancetest/test_runtest.html] +skip-if = toolkit == 'android' +[selecttest/test_Document-open.html] +[selecttest/test_addRange.html] +skip-if = toolkit == 'android' #android(bug 775227) +[selecttest/test_collapse.html] +[selecttest/test_collapseToStartEnd.html] +[selecttest/test_deleteFromDocument.html] +[selecttest/test_extend.html] +[selecttest/test_getRangeAt.html] +[selecttest/test_getSelection.html] +[selecttest/test_interfaces.html] +[selecttest/test_isCollapsed.html] +[selecttest/test_removeAllRanges.html] +[selecttest/test_selectAllChildren.html] diff --git a/dom/imptests/failures/html/dom/nodes/mochitest.ini b/dom/imptests/failures/html/dom/nodes/mochitest.ini new file mode 100644 index 000000000..4fb17f193 --- /dev/null +++ b/dom/imptests/failures/html/dom/nodes/mochitest.ini @@ -0,0 +1,13 @@ +# THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT +[DEFAULT] +support-files = + + +[test_Document-createElement-namespace.html.json] +[test_Document-createElementNS.html.json] +[test_Document-getElementsByTagName.html.json] +[test_Node-properties.html.json] +[test_attributes.html.json] +[test_case.html.json] +[test_getElementsByClassName-10.xml.json] +[test_getElementsByClassName-11.xml.json] diff --git a/dom/imptests/failures/html/dom/ranges/mochitest.ini b/dom/imptests/failures/html/dom/ranges/mochitest.ini new file mode 100644 index 000000000..da0d1a771 --- /dev/null +++ b/dom/imptests/failures/html/dom/ranges/mochitest.ini @@ -0,0 +1,7 @@ +# THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT +[DEFAULT] +support-files = + + +[test_Range-insertNode.html.json] +[test_Range-surroundContents.html.json] diff --git a/dom/imptests/failures/html/dom/ranges/test_Range-insertNode.html.json b/dom/imptests/failures/html/dom/ranges/test_Range-insertNode.html.json new file mode 100644 index 000000000..a5ac1621d --- /dev/null +++ b/dom/imptests/failures/html/dom/ranges/test_Range-insertNode.html.json @@ -0,0 +1,6 @@ +{ + "0,1: resulting range position for range [paras[0].firstChild, 0, paras[0].firstChild, 0], node paras[0].firstChild": true, + "4,2: resulting range position for range [paras[1].firstChild, 0, paras[1].firstChild, 0], node paras[1].firstChild": true, + "6,6: resulting range position for range [detachedPara1.firstChild, 0, detachedPara1.firstChild, 0], node detachedPara1.firstChild": true, + "8,4: resulting range position for range [foreignPara1.firstChild, 0, foreignPara1.firstChild, 0], node foreignPara1.firstChild": true +} diff --git a/dom/imptests/failures/html/dom/ranges/test_Range-surroundContents.html.json b/dom/imptests/failures/html/dom/ranges/test_Range-surroundContents.html.json new file mode 100644 index 000000000..720ee5edb --- /dev/null +++ b/dom/imptests/failures/html/dom/ranges/test_Range-surroundContents.html.json @@ -0,0 +1,44 @@ +{ + "0,1: resulting range position for range [paras[0].firstChild, 0, paras[0].firstChild, 0], node paras[0].firstChild": true, + "1,1: resulting range position for range [paras[0].firstChild, 0, paras[0].firstChild, 1], node paras[0].firstChild": true, + "2,1: resulting range position for range [paras[0].firstChild, 2, paras[0].firstChild, 8], node paras[0].firstChild": true, + "3,1: resulting range position for range [paras[0].firstChild, 2, paras[0].firstChild, 9], node paras[0].firstChild": true, + "4,2: resulting range position for range [paras[1].firstChild, 0, paras[1].firstChild, 0], node paras[1].firstChild": true, + "5,2: resulting range position for range [paras[1].firstChild, 2, paras[1].firstChild, 9], node paras[1].firstChild": true, + "6,6: resulting range position for range [detachedPara1.firstChild, 0, detachedPara1.firstChild, 0], node detachedPara1.firstChild": true, + "7,6: resulting range position for range [detachedPara1.firstChild, 2, detachedPara1.firstChild, 8], node detachedPara1.firstChild": true, + "8,4: resulting range position for range [foreignPara1.firstChild, 0, foreignPara1.firstChild, 0], node foreignPara1.firstChild": true, + "9,4: resulting range position for range [foreignPara1.firstChild, 2, foreignPara1.firstChild, 8], node foreignPara1.firstChild": true, + "37,0: resulting DOM for range [processingInstruction, 0, processingInstruction, 4], node paras[0]": true, + "37,0: resulting range position for range [processingInstruction, 0, processingInstruction, 4], node paras[0]": true, + "37,1: resulting DOM for range [processingInstruction, 0, processingInstruction, 4], node paras[0].firstChild": true, + "37,1: resulting range position for range [processingInstruction, 0, processingInstruction, 4], node paras[0].firstChild": true, + "37,2: resulting DOM for range [processingInstruction, 0, processingInstruction, 4], node paras[1].firstChild": true, + "37,2: resulting range position for range [processingInstruction, 0, processingInstruction, 4], node paras[1].firstChild": true, + "37,3: resulting DOM for range [processingInstruction, 0, processingInstruction, 4], node foreignPara1": true, + "37,3: resulting range position for range [processingInstruction, 0, processingInstruction, 4], node foreignPara1": true, + "37,4: resulting DOM for range [processingInstruction, 0, processingInstruction, 4], node foreignPara1.firstChild": true, + "37,4: resulting range position for range [processingInstruction, 0, processingInstruction, 4], node foreignPara1.firstChild": true, + "37,5: resulting DOM for range [processingInstruction, 0, processingInstruction, 4], node detachedPara1": true, + "37,5: resulting range position for range [processingInstruction, 0, processingInstruction, 4], node detachedPara1": true, + "37,6: resulting DOM for range [processingInstruction, 0, processingInstruction, 4], node detachedPara1.firstChild": true, + "37,6: resulting range position for range [processingInstruction, 0, processingInstruction, 4], node detachedPara1.firstChild": true, + "37,8: resulting DOM for range [processingInstruction, 0, processingInstruction, 4], node detachedDiv": true, + "37,8: resulting range position for range [processingInstruction, 0, processingInstruction, 4], node detachedDiv": true, + "37,10: resulting DOM for range [processingInstruction, 0, processingInstruction, 4], node foreignPara2": true, + "37,10: resulting range position for range [processingInstruction, 0, processingInstruction, 4], node foreignPara2": true, + "37,12: resulting DOM for range [processingInstruction, 0, processingInstruction, 4], node xmlElement": true, + "37,12: resulting range position for range [processingInstruction, 0, processingInstruction, 4], node xmlElement": true, + "37,13: resulting DOM for range [processingInstruction, 0, processingInstruction, 4], node detachedTextNode": true, + "37,13: resulting range position for range [processingInstruction, 0, processingInstruction, 4], node detachedTextNode": true, + "37,14: resulting DOM for range [processingInstruction, 0, processingInstruction, 4], node foreignTextNode": true, + "37,14: resulting range position for range [processingInstruction, 0, processingInstruction, 4], node foreignTextNode": true, + "37,15: resulting DOM for range [processingInstruction, 0, processingInstruction, 4], node processingInstruction": true, + "37,15: resulting range position for range [processingInstruction, 0, processingInstruction, 4], node processingInstruction": true, + "37,16: resulting DOM for range [processingInstruction, 0, processingInstruction, 4], node detachedProcessingInstruction": true, + "37,16: resulting range position for range [processingInstruction, 0, processingInstruction, 4], node detachedProcessingInstruction": true, + "37,17: resulting DOM for range [processingInstruction, 0, processingInstruction, 4], node comment": true, + "37,17: resulting range position for range [processingInstruction, 0, processingInstruction, 4], node comment": true, + "37,18: resulting DOM for range [processingInstruction, 0, processingInstruction, 4], node detachedComment": true, + "37,18: resulting range position for range [processingInstruction, 0, processingInstruction, 4], node detachedComment": true +} diff --git a/dom/imptests/failures/html/dom/test_historical.html.json b/dom/imptests/failures/html/dom/test_historical.html.json new file mode 100644 index 000000000..42e78b23d --- /dev/null +++ b/dom/imptests/failures/html/dom/test_historical.html.json @@ -0,0 +1,10 @@ +{ + "Historical DOM features must be removed: CDATASection": true, + "Historical DOM features must be removed: createCDATASection": true, + "Historical DOM features must be removed: createAttribute": true, + "Historical DOM features must be removed: createAttributeNS": true, + "Historical DOM features must be removed: getAttributeNode": true, + "Historical DOM features must be removed: getAttributeNodeNS": true, + "Historical DOM features must be removed: setAttributeNode": true, + "Historical DOM features must be removed: removeAttributeNode": true +} diff --git a/dom/imptests/failures/html/html/browsers/the-window-object/test_window-named-properties.html.json b/dom/imptests/failures/html/html/browsers/the-window-object/test_window-named-properties.html.json new file mode 100644 index 000000000..2c63c0851 --- /dev/null +++ b/dom/imptests/failures/html/html/browsers/the-window-object/test_window-named-properties.html.json @@ -0,0 +1,2 @@ +{ +} diff --git a/dom/imptests/failures/html/html/browsers/the-window-object/test_window-properties.html.json b/dom/imptests/failures/html/html/browsers/the-window-object/test_window-properties.html.json new file mode 100644 index 000000000..b34f6573f --- /dev/null +++ b/dom/imptests/failures/html/html/browsers/the-window-object/test_window-properties.html.json @@ -0,0 +1,31 @@ +{ + "EventTarget method: addEventListener": true, + "EventTarget method: removeEventListener": true, + "EventTarget method: dispatchEvent": true, + "Window readonly attribute: history": true, + "Window readonly attribute: parent": true, + "Window readonly attribute: frameElement": true, + "Window readonly attribute: navigator": true, + "Window readonly attribute: external": true, + "Window readonly attribute: applicationCache": true, + "Window readonly attribute: sessionStorage": true, + "Window readonly attribute: localStorage": true, + "Window readonly attribute: screen": true, + "Window readonly attribute: innerWidth": true, + "Window readonly attribute: innerHeight": true, + "Window readonly attribute: scrollX": true, + "Window readonly attribute: pageXOffset": true, + "Window readonly attribute: scrollY": true, + "Window readonly attribute: pageYOffset": true, + "Window readonly attribute: screenX": true, + "Window readonly attribute: screenY": true, + "Window readonly attribute: outerWidth": true, + "Window readonly attribute: outerHeight": true, + "Window attribute: oncancel": true, + "Window attribute: onclose": true, + "Window attribute: oncuechange": true, + "Window attribute: onmousewheel": true, + "Window unforgeable attribute: window": true, + "Window unforgeable attribute: document": true, + "Window unforgeable attribute: top": true +} diff --git a/dom/imptests/failures/html/html/dom/documents/dta/mochitest.ini b/dom/imptests/failures/html/html/dom/documents/dta/mochitest.ini new file mode 100644 index 000000000..6777d5816 --- /dev/null +++ b/dom/imptests/failures/html/html/dom/documents/dta/mochitest.ini @@ -0,0 +1,11 @@ +# THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT +[DEFAULT] +support-files = + + +[test_document.title-06.html.json] +[test_nameditem-02.html.json] +[test_nameditem-03.html.json] +[test_nameditem-04.html.json] +[test_nameditem-05.html.json] +[test_nameditem-06.html.json] diff --git a/dom/imptests/failures/html/html/dom/documents/dta/test_nameditem-06.html.json b/dom/imptests/failures/html/html/dom/documents/dta/test_nameditem-06.html.json new file mode 100644 index 000000000..504194194 --- /dev/null +++ b/dom/imptests/failures/html/html/dom/documents/dta/test_nameditem-06.html.json @@ -0,0 +1,3 @@ +{ + "If there are two imgs, a collection should be returned. (name)": true +} diff --git a/dom/imptests/failures/html/typedarrays/mochitest.ini b/dom/imptests/failures/html/typedarrays/mochitest.ini new file mode 100644 index 000000000..90d5fe802 --- /dev/null +++ b/dom/imptests/failures/html/typedarrays/mochitest.ini @@ -0,0 +1,6 @@ +# THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT +[DEFAULT] +support-files = + + +[test_constructors.html.json] diff --git a/dom/imptests/failures/html/typedarrays/test_constructors.html.json b/dom/imptests/failures/html/typedarrays/test_constructors.html.json new file mode 100644 index 000000000..046fde96d --- /dev/null +++ b/dom/imptests/failures/html/typedarrays/test_constructors.html.json @@ -0,0 +1,147 @@ +{ + "Constructing interface Int8Array with no arguments should throw.": true, + "Constructing interface Uint8Array with no arguments should throw.": true, + "Constructing interface Uint8ClampedArray with no arguments should throw.": true, + "Constructing interface Int16Array with no arguments should throw.": true, + "Constructing interface Uint16Array with no arguments should throw.": true, + "Constructing interface Int32Array with no arguments should throw.": true, + "Constructing interface Uint32Array with no arguments should throw.": true, + "Constructing interface Float32Array with no arguments should throw.": true, + "Constructing interface Float64Array with no arguments should throw.": true, + "Constructing interface ArrayBuffer with no arguments should throw.": true, + "The argument NaN (0) should be interpreted as 0 for interface Int8Array.": true, + "The argument Infinity (1) should be interpreted as 0 for interface Int8Array.": true, + "The argument -Infinity (2) should be interpreted as 0 for interface Int8Array.": true, + "The argument -0.4 (5) should be interpreted as 0 for interface Int8Array.": true, + "The argument -0.9 (6) should be interpreted as 0 for interface Int8Array.": true, + "The argument 1.1 (7) should be interpreted as 1 for interface Int8Array.": true, + "The argument 2.9 (8) should be interpreted as 2 for interface Int8Array.": true, + "The argument -4043309056 (10) should be interpreted as 251658240 for interface Int8Array.": true, + "The argument \"1\" (11) should be interpreted as 1 for interface Int8Array.": true, + "The argument \"1e2\" (12) should be interpreted as 100 for interface Int8Array.": true, + "The argument undefined (13) should be interpreted as 0 for interface Int8Array.": true, + "The argument null (14) should be interpreted as 0 for interface Int8Array.": true, + "The argument false (15) should be interpreted as 0 for interface Int8Array.": true, + "The argument true (16) should be interpreted as 1 for interface Int8Array.": true, + "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Int8Array.": true, + "The argument NaN (0) should be interpreted as 0 for interface Uint8Array.": true, + "The argument Infinity (1) should be interpreted as 0 for interface Uint8Array.": true, + "The argument -Infinity (2) should be interpreted as 0 for interface Uint8Array.": true, + "The argument -0.4 (5) should be interpreted as 0 for interface Uint8Array.": true, + "The argument -0.9 (6) should be interpreted as 0 for interface Uint8Array.": true, + "The argument 1.1 (7) should be interpreted as 1 for interface Uint8Array.": true, + "The argument 2.9 (8) should be interpreted as 2 for interface Uint8Array.": true, + "The argument -4043309056 (10) should be interpreted as 251658240 for interface Uint8Array.": true, + "The argument \"1\" (11) should be interpreted as 1 for interface Uint8Array.": true, + "The argument \"1e2\" (12) should be interpreted as 100 for interface Uint8Array.": true, + "The argument undefined (13) should be interpreted as 0 for interface Uint8Array.": true, + "The argument null (14) should be interpreted as 0 for interface Uint8Array.": true, + "The argument false (15) should be interpreted as 0 for interface Uint8Array.": true, + "The argument true (16) should be interpreted as 1 for interface Uint8Array.": true, + "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Uint8Array.": true, + "The argument NaN (0) should be interpreted as 0 for interface Uint8ClampedArray.": true, + "The argument Infinity (1) should be interpreted as 0 for interface Uint8ClampedArray.": true, + "The argument -Infinity (2) should be interpreted as 0 for interface Uint8ClampedArray.": true, + "The argument -0.4 (5) should be interpreted as 0 for interface Uint8ClampedArray.": true, + "The argument -0.9 (6) should be interpreted as 0 for interface Uint8ClampedArray.": true, + "The argument 1.1 (7) should be interpreted as 1 for interface Uint8ClampedArray.": true, + "The argument 2.9 (8) should be interpreted as 2 for interface Uint8ClampedArray.": true, + "The argument -4043309056 (10) should be interpreted as 251658240 for interface Uint8ClampedArray.": true, + "The argument \"1\" (11) should be interpreted as 1 for interface Uint8ClampedArray.": true, + "The argument \"1e2\" (12) should be interpreted as 100 for interface Uint8ClampedArray.": true, + "The argument undefined (13) should be interpreted as 0 for interface Uint8ClampedArray.": true, + "The argument null (14) should be interpreted as 0 for interface Uint8ClampedArray.": true, + "The argument false (15) should be interpreted as 0 for interface Uint8ClampedArray.": true, + "The argument true (16) should be interpreted as 1 for interface Uint8ClampedArray.": true, + "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Uint8ClampedArray.": true, + "The argument NaN (0) should be interpreted as 0 for interface Int16Array.": true, + "The argument Infinity (1) should be interpreted as 0 for interface Int16Array.": true, + "The argument -Infinity (2) should be interpreted as 0 for interface Int16Array.": true, + "The argument -0.4 (5) should be interpreted as 0 for interface Int16Array.": true, + "The argument -0.9 (6) should be interpreted as 0 for interface Int16Array.": true, + "The argument 1.1 (7) should be interpreted as 1 for interface Int16Array.": true, + "The argument 2.9 (8) should be interpreted as 2 for interface Int16Array.": true, + "The argument -4043309056 (10) should be interpreted as 251658240 for interface Int16Array.": true, + "The argument \"1\" (11) should be interpreted as 1 for interface Int16Array.": true, + "The argument \"1e2\" (12) should be interpreted as 100 for interface Int16Array.": true, + "The argument undefined (13) should be interpreted as 0 for interface Int16Array.": true, + "The argument null (14) should be interpreted as 0 for interface Int16Array.": true, + "The argument false (15) should be interpreted as 0 for interface Int16Array.": true, + "The argument true (16) should be interpreted as 1 for interface Int16Array.": true, + "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Int16Array.": true, + "The argument NaN (0) should be interpreted as 0 for interface Uint16Array.": true, + "The argument Infinity (1) should be interpreted as 0 for interface Uint16Array.": true, + "The argument -Infinity (2) should be interpreted as 0 for interface Uint16Array.": true, + "The argument -0.4 (5) should be interpreted as 0 for interface Uint16Array.": true, + "The argument -0.9 (6) should be interpreted as 0 for interface Uint16Array.": true, + "The argument 1.1 (7) should be interpreted as 1 for interface Uint16Array.": true, + "The argument 2.9 (8) should be interpreted as 2 for interface Uint16Array.": true, + "The argument -4043309056 (10) should be interpreted as 251658240 for interface Uint16Array.": true, + "The argument \"1\" (11) should be interpreted as 1 for interface Uint16Array.": true, + "The argument \"1e2\" (12) should be interpreted as 100 for interface Uint16Array.": true, + "The argument undefined (13) should be interpreted as 0 for interface Uint16Array.": true, + "The argument null (14) should be interpreted as 0 for interface Uint16Array.": true, + "The argument false (15) should be interpreted as 0 for interface Uint16Array.": true, + "The argument true (16) should be interpreted as 1 for interface Uint16Array.": true, + "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Uint16Array.": true, + "The argument NaN (0) should be interpreted as 0 for interface Int32Array.": true, + "The argument Infinity (1) should be interpreted as 0 for interface Int32Array.": true, + "The argument -Infinity (2) should be interpreted as 0 for interface Int32Array.": true, + "The argument -0.4 (5) should be interpreted as 0 for interface Int32Array.": true, + "The argument -0.9 (6) should be interpreted as 0 for interface Int32Array.": true, + "The argument 1.1 (7) should be interpreted as 1 for interface Int32Array.": true, + "The argument 2.9 (8) should be interpreted as 2 for interface Int32Array.": true, + "The argument -4043309056 (10) should be interpreted as 251658240 for interface Int32Array.": true, + "The argument \"1\" (11) should be interpreted as 1 for interface Int32Array.": true, + "The argument \"1e2\" (12) should be interpreted as 100 for interface Int32Array.": true, + "The argument undefined (13) should be interpreted as 0 for interface Int32Array.": true, + "The argument null (14) should be interpreted as 0 for interface Int32Array.": true, + "The argument false (15) should be interpreted as 0 for interface Int32Array.": true, + "The argument true (16) should be interpreted as 1 for interface Int32Array.": true, + "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Int32Array.": true, + "The argument NaN (0) should be interpreted as 0 for interface Uint32Array.": true, + "The argument Infinity (1) should be interpreted as 0 for interface Uint32Array.": true, + "The argument -Infinity (2) should be interpreted as 0 for interface Uint32Array.": true, + "The argument -0.4 (5) should be interpreted as 0 for interface Uint32Array.": true, + "The argument -0.9 (6) should be interpreted as 0 for interface Uint32Array.": true, + "The argument 1.1 (7) should be interpreted as 1 for interface Uint32Array.": true, + "The argument 2.9 (8) should be interpreted as 2 for interface Uint32Array.": true, + "The argument -4043309056 (10) should be interpreted as 251658240 for interface Uint32Array.": true, + "The argument \"1\" (11) should be interpreted as 1 for interface Uint32Array.": true, + "The argument \"1e2\" (12) should be interpreted as 100 for interface Uint32Array.": true, + "The argument undefined (13) should be interpreted as 0 for interface Uint32Array.": true, + "The argument null (14) should be interpreted as 0 for interface Uint32Array.": true, + "The argument false (15) should be interpreted as 0 for interface Uint32Array.": true, + "The argument true (16) should be interpreted as 1 for interface Uint32Array.": true, + "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Uint32Array.": true, + "The argument NaN (0) should be interpreted as 0 for interface Float32Array.": true, + "The argument Infinity (1) should be interpreted as 0 for interface Float32Array.": true, + "The argument -Infinity (2) should be interpreted as 0 for interface Float32Array.": true, + "The argument -0.4 (5) should be interpreted as 0 for interface Float32Array.": true, + "The argument -0.9 (6) should be interpreted as 0 for interface Float32Array.": true, + "The argument 1.1 (7) should be interpreted as 1 for interface Float32Array.": true, + "The argument 2.9 (8) should be interpreted as 2 for interface Float32Array.": true, + "The argument -4043309056 (10) should be interpreted as 251658240 for interface Float32Array.": true, + "The argument \"1\" (11) should be interpreted as 1 for interface Float32Array.": true, + "The argument \"1e2\" (12) should be interpreted as 100 for interface Float32Array.": true, + "The argument undefined (13) should be interpreted as 0 for interface Float32Array.": true, + "The argument null (14) should be interpreted as 0 for interface Float32Array.": true, + "The argument false (15) should be interpreted as 0 for interface Float32Array.": true, + "The argument true (16) should be interpreted as 1 for interface Float32Array.": true, + "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Float32Array.": true, + "The argument NaN (0) should be interpreted as 0 for interface Float64Array.": true, + "The argument Infinity (1) should be interpreted as 0 for interface Float64Array.": true, + "The argument -Infinity (2) should be interpreted as 0 for interface Float64Array.": true, + "The argument -0.4 (5) should be interpreted as 0 for interface Float64Array.": true, + "The argument -0.9 (6) should be interpreted as 0 for interface Float64Array.": true, + "The argument 1.1 (7) should be interpreted as 1 for interface Float64Array.": true, + "The argument 2.9 (8) should be interpreted as 2 for interface Float64Array.": true, + "The argument -4043309056 (10) should be interpreted as 251658240 for interface Float64Array.": true, + "The argument \"1\" (11) should be interpreted as 1 for interface Float64Array.": true, + "The argument \"1e2\" (12) should be interpreted as 100 for interface Float64Array.": true, + "The argument undefined (13) should be interpreted as 0 for interface Float64Array.": true, + "The argument null (14) should be interpreted as 0 for interface Float64Array.": true, + "The argument false (15) should be interpreted as 0 for interface Float64Array.": true, + "The argument true (16) should be interpreted as 1 for interface Float64Array.": true, + "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Float64Array.": true +} diff --git a/dom/imptests/failures/webapps/WebStorage/tests/submissions/Infraware/mochitest.ini b/dom/imptests/failures/webapps/WebStorage/tests/submissions/Infraware/mochitest.ini new file mode 100644 index 000000000..f17aa2160 --- /dev/null +++ b/dom/imptests/failures/webapps/WebStorage/tests/submissions/Infraware/mochitest.ini @@ -0,0 +1,6 @@ +# THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT +[DEFAULT] +support-files = + + +[test_storage_local_security.html.json] diff --git a/dom/imptests/failures/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_security.html.json b/dom/imptests/failures/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_security.html.json new file mode 100644 index 000000000..ea1a73ebf --- /dev/null +++ b/dom/imptests/failures/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_security.html.json @@ -0,0 +1,3 @@ +{ + "storage local security test": true +} diff --git a/dom/imptests/html.txt b/dom/imptests/html.txt new file mode 100644 index 000000000..2d06371d8 --- /dev/null +++ b/dom/imptests/html.txt @@ -0,0 +1,3 @@ +git|git://github.com/w3c/web-platform-tests.git|html +typedarrays +webgl diff --git a/dom/imptests/html/dom/common.js b/dom/imptests/html/dom/common.js new file mode 100644 index 000000000..dc7528a9a --- /dev/null +++ b/dom/imptests/html/dom/common.js @@ -0,0 +1,1087 @@ +"use strict"; +// Written by Aryeh Gregor <ayg@aryeh.name> + +// TODO: iframes, contenteditable/designMode + +// Everything is done in functions in this test harness, so we have to declare +// all the variables before use to make sure they can be reused. +var selection; +var testDiv, paras, detachedDiv, detachedPara1, detachedPara2, + foreignDoc, foreignPara1, foreignPara2, xmlDoc, xmlElement, + detachedXmlElement, detachedTextNode, foreignTextNode, + detachedForeignTextNode, xmlTextNode, detachedXmlTextNode, + processingInstruction, detachedProcessingInstruction, comment, + detachedComment, foreignComment, detachedForeignComment, xmlComment, + detachedXmlComment, docfrag, foreignDocfrag, xmlDocfrag, doctype, + foreignDoctype, xmlDoctype; +var testRangesShort, testRanges, testPoints, testNodesShort, testNodes; + +function setupRangeTests() { + selection = getSelection(); + testDiv = document.querySelector("#test"); + if (testDiv) { + testDiv.parentNode.removeChild(testDiv); + } + testDiv = document.createElement("div"); + testDiv.id = "test"; + document.body.insertBefore(testDiv, document.body.firstChild); + + paras = []; + paras.push(document.createElement("p")); + paras[0].setAttribute("id", "a"); + // Test some diacritics, to make sure browsers are using code units here + // and not something like grapheme clusters. + paras[0].textContent = "A\u0308b\u0308c\u0308d\u0308e\u0308f\u0308g\u0308h\u0308\n"; + testDiv.appendChild(paras[0]); + + paras.push(document.createElement("p")); + paras[1].setAttribute("id", "b"); + paras[1].setAttribute("style", "display:none"); + paras[1].textContent = "Ijklmnop\n"; + testDiv.appendChild(paras[1]); + + paras.push(document.createElement("p")); + paras[2].setAttribute("id", "c"); + paras[2].textContent = "Qrstuvwx"; + testDiv.appendChild(paras[2]); + + paras.push(document.createElement("p")); + paras[3].setAttribute("id", "d"); + paras[3].setAttribute("style", "display:none"); + paras[3].textContent = "Yzabcdef"; + testDiv.appendChild(paras[3]); + + paras.push(document.createElement("p")); + paras[4].setAttribute("id", "e"); + paras[4].setAttribute("style", "display:none"); + paras[4].textContent = "Ghijklmn"; + testDiv.appendChild(paras[4]); + + detachedDiv = document.createElement("div"); + detachedPara1 = document.createElement("p"); + detachedPara1.appendChild(document.createTextNode("Opqrstuv")); + detachedPara2 = document.createElement("p"); + detachedPara2.appendChild(document.createTextNode("Wxyzabcd")); + detachedDiv.appendChild(detachedPara1); + detachedDiv.appendChild(detachedPara2); + + // Opera doesn't automatically create a doctype for a new HTML document, + // contrary to spec. It also doesn't let you add doctypes to documents + // after the fact through any means I've tried. So foreignDoc in Opera + // will have no doctype, foreignDoctype will be null, and Opera will fail + // some tests somewhat mysteriously as a result. + foreignDoc = document.implementation.createHTMLDocument(""); + foreignPara1 = foreignDoc.createElement("p"); + foreignPara1.appendChild(foreignDoc.createTextNode("Efghijkl")); + foreignPara2 = foreignDoc.createElement("p"); + foreignPara2.appendChild(foreignDoc.createTextNode("Mnopqrst")); + foreignDoc.body.appendChild(foreignPara1); + foreignDoc.body.appendChild(foreignPara2); + + // Now we get to do really silly stuff, which nobody in the universe is + // ever going to actually do, but the spec defines behavior, so too bad. + // Testing is fun! + xmlDoctype = document.implementation.createDocumentType("qorflesnorf", "abcde", "x\"'y"); + xmlDoc = document.implementation.createDocument(null, null, xmlDoctype); + detachedXmlElement = xmlDoc.createElement("everyone-hates-hyphenated-element-names"); + detachedTextNode = document.createTextNode("Uvwxyzab"); + detachedForeignTextNode = foreignDoc.createTextNode("Cdefghij"); + detachedXmlTextNode = xmlDoc.createTextNode("Klmnopqr"); + // PIs only exist in XML documents, so don't bother with document or + // foreignDoc. + detachedProcessingInstruction = xmlDoc.createProcessingInstruction("whippoorwill", "chirp chirp chirp"); + detachedComment = document.createComment("Stuvwxyz"); + // Hurrah, we finally got to "z" at the end! + detachedForeignComment = foreignDoc.createComment("אריה יהודה"); + detachedXmlComment = xmlDoc.createComment("בן חיים אליעזר"); + + // We should also test with document fragments that actually contain stuff + // . . . but, maybe later. + docfrag = document.createDocumentFragment(); + foreignDocfrag = foreignDoc.createDocumentFragment(); + xmlDocfrag = xmlDoc.createDocumentFragment(); + + xmlElement = xmlDoc.createElement("igiveuponcreativenames"); + xmlTextNode = xmlDoc.createTextNode("do re mi fa so la ti"); + xmlElement.appendChild(xmlTextNode); + processingInstruction = xmlDoc.createProcessingInstruction("somePI", 'Did you know that ":syn sync fromstart" is very useful when using vim to edit large amounts of JavaScript embedded in HTML?'); + xmlDoc.appendChild(xmlElement); + xmlDoc.appendChild(processingInstruction); + xmlComment = xmlDoc.createComment("I maliciously created a comment that will break incautious XML serializers, but Firefox threw an exception, so all I got was this lousy T-shirt"); + xmlDoc.appendChild(xmlComment); + + comment = document.createComment("Alphabet soup?"); + testDiv.appendChild(comment); + + foreignComment = foreignDoc.createComment('"Commenter" and "commentator" mean different things. I\'ve seen non-native speakers trip up on this.'); + foreignDoc.appendChild(foreignComment); + foreignTextNode = foreignDoc.createTextNode("I admit that I harbor doubts about whether we really need so many things to test, but it's too late to stop now."); + foreignDoc.body.appendChild(foreignTextNode); + + doctype = document.doctype; + foreignDoctype = foreignDoc.doctype; + + testRangesShort = [ + // Various ranges within the text node children of different + // paragraphs. All should be valid. + "[paras[0].firstChild, 0, paras[0].firstChild, 0]", + "[paras[0].firstChild, 0, paras[0].firstChild, 1]", + "[paras[0].firstChild, 2, paras[0].firstChild, 8]", + "[paras[0].firstChild, 2, paras[0].firstChild, 9]", + "[paras[1].firstChild, 0, paras[1].firstChild, 0]", + "[paras[1].firstChild, 2, paras[1].firstChild, 9]", + "[detachedPara1.firstChild, 0, detachedPara1.firstChild, 0]", + "[detachedPara1.firstChild, 2, detachedPara1.firstChild, 8]", + "[foreignPara1.firstChild, 0, foreignPara1.firstChild, 0]", + "[foreignPara1.firstChild, 2, foreignPara1.firstChild, 8]", + // Now try testing some elements, not just text nodes. + "[document.documentElement, 0, document.documentElement, 1]", + "[document.documentElement, 0, document.documentElement, 2]", + "[document.documentElement, 1, document.documentElement, 2]", + "[document.head, 1, document.head, 1]", + "[document.body, 4, document.body, 5]", + "[foreignDoc.documentElement, 0, foreignDoc.documentElement, 1]", + "[paras[0], 0, paras[0], 1]", + "[detachedPara1, 0, detachedPara1, 1]", + // Now try some ranges that span elements. + "[paras[0].firstChild, 0, paras[1].firstChild, 0]", + "[paras[0].firstChild, 0, paras[1].firstChild, 8]", + "[paras[0].firstChild, 3, paras[3], 1]", + // How about something that spans a node and its descendant? + "[paras[0], 0, paras[0].firstChild, 7]", + "[testDiv, 2, paras[4], 1]", + // Then a few more interesting things just for good measure. + "[document, 0, document, 1]", + "[document, 0, document, 2]", + "[comment, 2, comment, 3]", + "[testDiv, 0, comment, 5]", + "[foreignDoc, 1, foreignComment, 2]", + "[foreignDoc.body, 0, foreignTextNode, 36]", + "[xmlDoc, 1, xmlComment, 0]", + "[detachedTextNode, 0, detachedTextNode, 8]", + "[detachedForeignTextNode, 0, detachedForeignTextNode, 8]", + "[detachedXmlTextNode, 0, detachedXmlTextNode, 8]", + "[detachedComment, 3, detachedComment, 4]", + "[detachedForeignComment, 0, detachedForeignComment, 1]", + "[detachedXmlComment, 2, detachedXmlComment, 6]", + "[docfrag, 0, docfrag, 0]", + ]; + + testRanges = testRangesShort.concat([ + "[paras[1].firstChild, 0, paras[1].firstChild, 1]", + "[paras[1].firstChild, 2, paras[1].firstChild, 8]", + "[detachedPara1.firstChild, 0, detachedPara1.firstChild, 1]", + "[foreignPara1.firstChild, 0, foreignPara1.firstChild, 1]", + "[foreignDoc.head, 1, foreignDoc.head, 1]", + "[foreignDoc.body, 0, foreignDoc.body, 0]", + "[paras[0], 0, paras[0], 0]", + "[detachedPara1, 0, detachedPara1, 0]", + "[testDiv, 1, paras[2].firstChild, 5]", + "[document.documentElement, 1, document.body, 0]", + "[foreignDoc.documentElement, 1, foreignDoc.body, 0]", + "[document, 1, document, 2]", + "[paras[2].firstChild, 4, comment, 2]", + "[paras[3], 1, comment, 8]", + "[foreignDoc, 0, foreignDoc, 0]", + "[xmlDoc, 0, xmlDoc, 0]", + "[detachedForeignTextNode, 7, detachedForeignTextNode, 7]", + "[detachedXmlTextNode, 7, detachedXmlTextNode, 7]", + "[detachedComment, 5, detachedComment, 5]", + "[detachedForeignComment, 4, detachedForeignComment, 4]", + "[foreignDocfrag, 0, foreignDocfrag, 0]", + "[xmlDocfrag, 0, xmlDocfrag, 0]", + ]); + + testPoints = [ + // Various positions within the page, some invalid. Remember that + // paras[0] is visible, and paras[1] is display: none. + "[paras[0].firstChild, -1]", + "[paras[0].firstChild, 0]", + "[paras[0].firstChild, 1]", + "[paras[0].firstChild, 2]", + "[paras[0].firstChild, 8]", + "[paras[0].firstChild, 9]", + "[paras[0].firstChild, 10]", + "[paras[0].firstChild, 65535]", + "[paras[1].firstChild, -1]", + "[paras[1].firstChild, 0]", + "[paras[1].firstChild, 1]", + "[paras[1].firstChild, 2]", + "[paras[1].firstChild, 8]", + "[paras[1].firstChild, 9]", + "[paras[1].firstChild, 10]", + "[paras[1].firstChild, 65535]", + "[detachedPara1.firstChild, 0]", + "[detachedPara1.firstChild, 1]", + "[detachedPara1.firstChild, 8]", + "[detachedPara1.firstChild, 9]", + "[foreignPara1.firstChild, 0]", + "[foreignPara1.firstChild, 1]", + "[foreignPara1.firstChild, 8]", + "[foreignPara1.firstChild, 9]", + // Now try testing some elements, not just text nodes. + "[document.documentElement, -1]", + "[document.documentElement, 0]", + "[document.documentElement, 1]", + "[document.documentElement, 2]", + "[document.documentElement, 7]", + "[document.head, 1]", + "[document.body, 3]", + "[foreignDoc.documentElement, 0]", + "[foreignDoc.documentElement, 1]", + "[foreignDoc.head, 0]", + "[foreignDoc.body, 1]", + "[paras[0], 0]", + "[paras[0], 1]", + "[paras[0], 2]", + "[paras[1], 0]", + "[paras[1], 1]", + "[paras[1], 2]", + "[detachedPara1, 0]", + "[detachedPara1, 1]", + "[testDiv, 0]", + "[testDiv, 3]", + // Then a few more interesting things just for good measure. + "[document, -1]", + "[document, 0]", + "[document, 1]", + "[document, 2]", + "[document, 3]", + "[comment, -1]", + "[comment, 0]", + "[comment, 4]", + "[comment, 96]", + "[foreignDoc, 0]", + "[foreignDoc, 1]", + "[foreignComment, 2]", + "[foreignTextNode, 0]", + "[foreignTextNode, 36]", + "[xmlDoc, -1]", + "[xmlDoc, 0]", + "[xmlDoc, 1]", + "[xmlDoc, 5]", + "[xmlComment, 0]", + "[xmlComment, 4]", + "[processingInstruction, 0]", + "[processingInstruction, 5]", + "[processingInstruction, 9]", + "[detachedTextNode, 0]", + "[detachedTextNode, 8]", + "[detachedForeignTextNode, 0]", + "[detachedForeignTextNode, 8]", + "[detachedXmlTextNode, 0]", + "[detachedXmlTextNode, 8]", + "[detachedProcessingInstruction, 12]", + "[detachedComment, 3]", + "[detachedComment, 5]", + "[detachedForeignComment, 0]", + "[detachedForeignComment, 4]", + "[detachedXmlComment, 2]", + "[docfrag, 0]", + "[foreignDocfrag, 0]", + "[xmlDocfrag, 0]", + "[doctype, 0]", + "[doctype, -17]", + "[doctype, 1]", + "[foreignDoctype, 0]", + "[xmlDoctype, 0]", + ]; + + testNodesShort = [ + "paras[0]", + "paras[0].firstChild", + "paras[1].firstChild", + "foreignPara1", + "foreignPara1.firstChild", + "detachedPara1", + "detachedPara1.firstChild", + "document", + "detachedDiv", + "foreignDoc", + "foreignPara2", + "xmlDoc", + "xmlElement", + "detachedTextNode", + "foreignTextNode", + "processingInstruction", + "detachedProcessingInstruction", + "comment", + "detachedComment", + "docfrag", + "doctype", + "foreignDoctype", + ]; + + testNodes = testNodesShort.concat([ + "paras[1]", + "detachedPara2", + "detachedPara2.firstChild", + "testDiv", + "detachedXmlElement", + "detachedForeignTextNode", + "xmlTextNode", + "detachedXmlTextNode", + "xmlComment", + "foreignComment", + "detachedForeignComment", + "detachedXmlComment", + "foreignDocfrag", + "xmlDocfrag", + "xmlDoctype", + ]); +} +if ("setup" in window) { + setup(setupRangeTests); +} else { + // Presumably we're running from within an iframe or something + setupRangeTests(); +} + +/** + * The "length" of a node as defined by the Ranges section of DOM4. + */ +function nodeLength(node) { + // "The length of a node node depends on node: + // + // "DocumentType + // "0." + if (node.nodeType == Node.DOCUMENT_TYPE_NODE) { + return 0; + } + // "Text + // "ProcessingInstruction + // "Comment + // "Its length attribute value." + // Browsers don't historically support the length attribute on + // ProcessingInstruction, so to avoid spurious failures, do + // node.data.length instead of node.length. + if (node.nodeType == Node.TEXT_NODE || node.nodeType == Node.PROCESSING_INSTRUCTION_NODE || node.nodeType == Node.COMMENT_NODE) { + return node.data.length; + } + // "Any other node + // "Its number of children." + return node.childNodes.length; +} + +/** + * Returns the furthest ancestor of a Node as defined by the spec. + */ +function furthestAncestor(node) { + var root = node; + while (root.parentNode != null) { + root = root.parentNode; + } + return root; +} + +/** + * "The ancestor containers of a Node are the Node itself and all its + * ancestors." + * + * Is node1 an ancestor container of node2? + */ +function isAncestorContainer(node1, node2) { + return node1 == node2 || + (node2.compareDocumentPosition(node1) & Node.DOCUMENT_POSITION_CONTAINS); +} + +/** + * Returns the first Node that's after node in tree order, or null if node is + * the last Node. + */ +function nextNode(node) { + if (node.hasChildNodes()) { + return node.firstChild; + } + return nextNodeDescendants(node); +} + +/** + * Returns the last Node that's before node in tree order, or null if node is + * the first Node. + */ +function previousNode(node) { + if (node.previousSibling) { + node = node.previousSibling; + while (node.hasChildNodes()) { + node = node.lastChild; + } + return node; + } + return node.parentNode; +} + +/** + * Returns the next Node that's after node and all its descendants in tree + * order, or null if node is the last Node or an ancestor of it. + */ +function nextNodeDescendants(node) { + while (node && !node.nextSibling) { + node = node.parentNode; + } + if (!node) { + return null; + } + return node.nextSibling; +} + +/** + * Returns the ownerDocument of the Node, or the Node itself if it's a + * Document. + */ +function ownerDocument(node) { + return node.nodeType == Node.DOCUMENT_NODE + ? node + : node.ownerDocument; +} + +/** + * Returns true if ancestor is an ancestor of descendant, false otherwise. + */ +function isAncestor(ancestor, descendant) { + if (!ancestor || !descendant) { + return false; + } + while (descendant && descendant != ancestor) { + descendant = descendant.parentNode; + } + return descendant == ancestor; +} + +/** + * Returns true if ancestor is an inclusive ancestor of descendant, false + * otherwise. + */ +function isInclusiveAncestor(ancestor, descendant) { + return ancestor === descendant || isAncestor(ancestor, descendant); +} + +/** + * Returns true if descendant is a descendant of ancestor, false otherwise. + */ +function isDescendant(descendant, ancestor) { + return isAncestor(ancestor, descendant); +} + +/** + * Returns true if descendant is an inclusive descendant of ancestor, false + * otherwise. + */ +function isInclusiveDescendant(descendant, ancestor) { + return descendant === ancestor || isDescendant(descendant, ancestor); +} + +/** + * The position of two boundary points relative to one another, as defined by + * the spec. + */ +function getPosition(nodeA, offsetA, nodeB, offsetB) { + // "If node A is the same as node B, return equal if offset A equals offset + // B, before if offset A is less than offset B, and after if offset A is + // greater than offset B." + if (nodeA == nodeB) { + if (offsetA == offsetB) { + return "equal"; + } + if (offsetA < offsetB) { + return "before"; + } + if (offsetA > offsetB) { + return "after"; + } + } + + // "If node A is after node B in tree order, compute the position of (node + // B, offset B) relative to (node A, offset A). If it is before, return + // after. If it is after, return before." + if (nodeB.compareDocumentPosition(nodeA) & Node.DOCUMENT_POSITION_FOLLOWING) { + var pos = getPosition(nodeB, offsetB, nodeA, offsetA); + if (pos == "before") { + return "after"; + } + if (pos == "after") { + return "before"; + } + } + + // "If node A is an ancestor of node B:" + if (nodeB.compareDocumentPosition(nodeA) & Node.DOCUMENT_POSITION_CONTAINS) { + // "Let child equal node B." + var child = nodeB; + + // "While child is not a child of node A, set child to its parent." + while (child.parentNode != nodeA) { + child = child.parentNode; + } + + // "If the index of child is less than offset A, return after." + if (indexOf(child) < offsetA) { + return "after"; + } + } + + // "Return before." + return "before"; +} + +/** + * "contained" as defined by DOM Range: "A Node node is contained in a range + * range if node's furthest ancestor is the same as range's root, and (node, 0) + * is after range's start, and (node, length of node) is before range's end." + */ +function isContained(node, range) { + var pos1 = getPosition(node, 0, range.startContainer, range.startOffset); + var pos2 = getPosition(node, nodeLength(node), range.endContainer, range.endOffset); + + return furthestAncestor(node) == furthestAncestor(range.startContainer) + && pos1 == "after" + && pos2 == "before"; +} + +/** + * "partially contained" as defined by DOM Range: "A Node is partially + * contained in a range if it is an ancestor container of the range's start but + * not its end, or vice versa." + */ +function isPartiallyContained(node, range) { + var cond1 = isAncestorContainer(node, range.startContainer); + var cond2 = isAncestorContainer(node, range.endContainer); + return (cond1 && !cond2) || (cond2 && !cond1); +} + +/** + * Index of a node as defined by the spec. + */ +function indexOf(node) { + if (!node.parentNode) { + // No preceding sibling nodes, right? + return 0; + } + var i = 0; + while (node != node.parentNode.childNodes[i]) { + i++; + } + return i; +} + +/** + * extractContents() implementation, following the spec. If an exception is + * supposed to be thrown, will return a string with the name (e.g., + * "HIERARCHY_REQUEST_ERR") instead of a document fragment. It might also + * return an arbitrary human-readable string if a condition is hit that implies + * a spec bug. + */ +function myExtractContents(range) { + // "If the context object's detached flag is set, raise an + // INVALID_STATE_ERR exception and abort these steps." + try { + range.collapsed; + } catch (e) { + return "INVALID_STATE_ERR"; + } + + // "Let frag be a new DocumentFragment whose ownerDocument is the same as + // the ownerDocument of the context object's start node." + var ownerDoc = range.startContainer.nodeType == Node.DOCUMENT_NODE + ? range.startContainer + : range.startContainer.ownerDocument; + var frag = ownerDoc.createDocumentFragment(); + + // "If the context object's start and end are the same, abort this method, + // returning frag." + if (range.startContainer == range.endContainer + && range.startOffset == range.endOffset) { + return frag; + } + + // "Let original start node, original start offset, original end node, and + // original end offset be the context object's start and end nodes and + // offsets, respectively." + var originalStartNode = range.startContainer; + var originalStartOffset = range.startOffset; + var originalEndNode = range.endContainer; + var originalEndOffset = range.endOffset; + + // "If original start node and original end node are the same, and they are + // a Text or Comment node:" + if (range.startContainer == range.endContainer + && (range.startContainer.nodeType == Node.TEXT_NODE + || range.startContainer.nodeType == Node.COMMENT_NODE)) { + // "Let clone be the result of calling cloneNode(false) on original + // start node." + var clone = originalStartNode.cloneNode(false); + + // "Set the data of clone to the result of calling + // substringData(original start offset, original end offset − original + // start offset) on original start node." + clone.data = originalStartNode.substringData(originalStartOffset, + originalEndOffset - originalStartOffset); + + // "Append clone as the last child of frag." + frag.appendChild(clone); + + // "Call deleteData(original start offset, original end offset − + // original start offset) on original start node." + originalStartNode.deleteData(originalStartOffset, + originalEndOffset - originalStartOffset); + + // "Abort this method, returning frag." + return frag; + } + + // "Let common ancestor equal original start node." + var commonAncestor = originalStartNode; + + // "While common ancestor is not an ancestor container of original end + // node, set common ancestor to its own parent." + while (!isAncestorContainer(commonAncestor, originalEndNode)) { + commonAncestor = commonAncestor.parentNode; + } + + // "If original start node is an ancestor container of original end node, + // let first partially contained child be null." + var firstPartiallyContainedChild; + if (isAncestorContainer(originalStartNode, originalEndNode)) { + firstPartiallyContainedChild = null; + // "Otherwise, let first partially contained child be the first child of + // common ancestor that is partially contained in the context object." + } else { + for (var i = 0; i < commonAncestor.childNodes.length; i++) { + if (isPartiallyContained(commonAncestor.childNodes[i], range)) { + firstPartiallyContainedChild = commonAncestor.childNodes[i]; + break; + } + } + if (!firstPartiallyContainedChild) { + throw "Spec bug: no first partially contained child!"; + } + } + + // "If original end node is an ancestor container of original start node, + // let last partially contained child be null." + var lastPartiallyContainedChild; + if (isAncestorContainer(originalEndNode, originalStartNode)) { + lastPartiallyContainedChild = null; + // "Otherwise, let last partially contained child be the last child of + // common ancestor that is partially contained in the context object." + } else { + for (var i = commonAncestor.childNodes.length - 1; i >= 0; i--) { + if (isPartiallyContained(commonAncestor.childNodes[i], range)) { + lastPartiallyContainedChild = commonAncestor.childNodes[i]; + break; + } + } + if (!lastPartiallyContainedChild) { + throw "Spec bug: no last partially contained child!"; + } + } + + // "Let contained children be a list of all children of common ancestor + // that are contained in the context object, in tree order." + // + // "If any member of contained children is a DocumentType, raise a + // HIERARCHY_REQUEST_ERR exception and abort these steps." + var containedChildren = []; + for (var i = 0; i < commonAncestor.childNodes.length; i++) { + if (isContained(commonAncestor.childNodes[i], range)) { + if (commonAncestor.childNodes[i].nodeType + == Node.DOCUMENT_TYPE_NODE) { + return "HIERARCHY_REQUEST_ERR"; + } + containedChildren.push(commonAncestor.childNodes[i]); + } + } + + // "If original start node is an ancestor container of original end node, + // set new node to original start node and new offset to original start + // offset." + var newNode, newOffset; + if (isAncestorContainer(originalStartNode, originalEndNode)) { + newNode = originalStartNode; + newOffset = originalStartOffset; + // "Otherwise:" + } else { + // "Let reference node equal original start node." + var referenceNode = originalStartNode; + + // "While reference node's parent is not null and is not an ancestor + // container of original end node, set reference node to its parent." + while (referenceNode.parentNode + && !isAncestorContainer(referenceNode.parentNode, originalEndNode)) { + referenceNode = referenceNode.parentNode; + } + + // "Set new node to the parent of reference node, and new offset to one + // plus the index of reference node." + newNode = referenceNode.parentNode; + newOffset = 1 + indexOf(referenceNode); + } + + // "If first partially contained child is a Text or Comment node:" + if (firstPartiallyContainedChild + && (firstPartiallyContainedChild.nodeType == Node.TEXT_NODE + || firstPartiallyContainedChild.nodeType == Node.COMMENT_NODE)) { + // "Let clone be the result of calling cloneNode(false) on original + // start node." + var clone = originalStartNode.cloneNode(false); + + // "Set the data of clone to the result of calling substringData() on + // original start node, with original start offset as the first + // argument and (length of original start node − original start offset) + // as the second." + clone.data = originalStartNode.substringData(originalStartOffset, + nodeLength(originalStartNode) - originalStartOffset); + + // "Append clone as the last child of frag." + frag.appendChild(clone); + + // "Call deleteData() on original start node, with original start + // offset as the first argument and (length of original start node − + // original start offset) as the second." + originalStartNode.deleteData(originalStartOffset, + nodeLength(originalStartNode) - originalStartOffset); + // "Otherwise, if first partially contained child is not null:" + } else if (firstPartiallyContainedChild) { + // "Let clone be the result of calling cloneNode(false) on first + // partially contained child." + var clone = firstPartiallyContainedChild.cloneNode(false); + + // "Append clone as the last child of frag." + frag.appendChild(clone); + + // "Let subrange be a new Range whose start is (original start node, + // original start offset) and whose end is (first partially contained + // child, length of first partially contained child)." + var subrange = ownerDoc.createRange(); + subrange.setStart(originalStartNode, originalStartOffset); + subrange.setEnd(firstPartiallyContainedChild, + nodeLength(firstPartiallyContainedChild)); + + // "Let subfrag be the result of calling extractContents() on + // subrange." + var subfrag = myExtractContents(subrange); + + // "For each child of subfrag, in order, append that child to clone as + // its last child." + for (var i = 0; i < subfrag.childNodes.length; i++) { + clone.appendChild(subfrag.childNodes[i]); + } + } + + // "For each contained child in contained children, append contained child + // as the last child of frag." + for (var i = 0; i < containedChildren.length; i++) { + frag.appendChild(containedChildren[i]); + } + + // "If last partially contained child is a Text or Comment node:" + if (lastPartiallyContainedChild + && (lastPartiallyContainedChild.nodeType == Node.TEXT_NODE + || lastPartiallyContainedChild.nodeType == Node.COMMENT_NODE)) { + // "Let clone be the result of calling cloneNode(false) on original + // end node." + var clone = originalEndNode.cloneNode(false); + + // "Set the data of clone to the result of calling substringData(0, + // original end offset) on original end node." + clone.data = originalEndNode.substringData(0, originalEndOffset); + + // "Append clone as the last child of frag." + frag.appendChild(clone); + + // "Call deleteData(0, original end offset) on original end node." + originalEndNode.deleteData(0, originalEndOffset); + // "Otherwise, if last partially contained child is not null:" + } else if (lastPartiallyContainedChild) { + // "Let clone be the result of calling cloneNode(false) on last + // partially contained child." + var clone = lastPartiallyContainedChild.cloneNode(false); + + // "Append clone as the last child of frag." + frag.appendChild(clone); + + // "Let subrange be a new Range whose start is (last partially + // contained child, 0) and whose end is (original end node, original + // end offset)." + var subrange = ownerDoc.createRange(); + subrange.setStart(lastPartiallyContainedChild, 0); + subrange.setEnd(originalEndNode, originalEndOffset); + + // "Let subfrag be the result of calling extractContents() on + // subrange." + var subfrag = myExtractContents(subrange); + + // "For each child of subfrag, in order, append that child to clone as + // its last child." + for (var i = 0; i < subfrag.childNodes.length; i++) { + clone.appendChild(subfrag.childNodes[i]); + } + } + + // "Set the context object's start and end to (new node, new offset)." + range.setStart(newNode, newOffset); + range.setEnd(newNode, newOffset); + + // "Return frag." + return frag; +} + +/** + * insertNode() implementation, following the spec. If an exception is meant + * to be thrown, will return a string with the expected exception name, for + * instance "HIERARCHY_REQUEST_ERR". + */ +function myInsertNode(range, node) { + // "If range's start node is either a ProcessingInstruction or Comment + // node, or a Text node whose parent is null, throw an + // "HierarchyRequestError" exception and terminate these steps." + if (range.startContainer.nodeType == Node.PROCESSING_INSTRUCTION_NODE + || range.startContainer.nodeType == Node.COMMENT_NODE + || (range.startContainer.nodeType == Node.TEXT_NODE + && !range.startContainer.parentNode)) { + return "HIERARCHY_REQUEST_ERR"; + } + + // "Let referenceNode be null." + var referenceNode = null; + + // "If range's start node is a Text node, set referenceNode to that Text node." + if (range.startContainer.nodeType == Node.TEXT_NODE) { + referenceNode = range.startContainer; + + // "Otherwise, set referenceNode to the child of start node whose index is + // start offset, and null if there is no such child." + } else { + if (range.startOffset < range.startContainer.childNodes.length) { + referenceNode = range.startContainer.childNodes[range.startOffset]; + } else { + referenceNode = null; + } + } + + // "Let parent be range's start node if referenceNode is null, and + // referenceNode's parent otherwise." + var parent_ = referenceNode === null ? range.startContainer : + referenceNode.parentNode; + + // "Ensure pre-insertion validity of node into parent before + // referenceNode." + var error = ensurePreInsertionValidity(node, parent_, referenceNode); + if (error) { + return error; + } + + // "If range's start node is a Text node, set referenceNode to the result + // of splitting it with offset range's start offset." + if (range.startContainer.nodeType == Node.TEXT_NODE) { + referenceNode = range.startContainer.splitText(range.startOffset); + } + + // "If node is referenceNode, set referenceNode to its next sibling." + if (node == referenceNode) { + referenceNode = referenceNode.nextSibling; + } + + // "If node's parent is not null, remove node from its parent." + if (node.parentNode) { + node.parentNode.removeChild(node); + } + + // "Let newOffset be parent's length if referenceNode is null, and + // referenceNode's index otherwise." + var newOffset = referenceNode === null ? nodeLength(parent_) : + indexOf(referenceNode); + + // "Increase newOffset by node's length if node is a DocumentFragment node, + // and one otherwise." + newOffset += node.nodeType == Node.DOCUMENT_FRAGMENT_NODE ? + nodeLength(node) : 1; + + // "Pre-insert node into parent before referenceNode." + parent_.insertBefore(node, referenceNode); + + // "If range's start and end are the same, set range's end to (parent, + // newOffset)." + if (range.startContainer == range.endContainer + && range.startOffset == range.endOffset) { + range.setEnd(parent_, newOffset); + } +} + +// To make filter() calls more readable +function isElement(node) { + return node.nodeType == Node.ELEMENT_NODE; +} + +function isText(node) { + return node.nodeType == Node.TEXT_NODE; +} + +function isDoctype(node) { + return node.nodeType == Node.DOCUMENT_TYPE_NODE; +} + +function ensurePreInsertionValidity(node, parent_, child) { + // "If parent is not a Document, DocumentFragment, or Element node, throw a + // HierarchyRequestError." + if (parent_.nodeType != Node.DOCUMENT_NODE + && parent_.nodeType != Node.DOCUMENT_FRAGMENT_NODE + && parent_.nodeType != Node.ELEMENT_NODE) { + return "HIERARCHY_REQUEST_ERR"; + } + + // "If node is a host-including inclusive ancestor of parent, throw a + // HierarchyRequestError." + // + // XXX Does not account for host + if (isInclusiveAncestor(node, parent_)) { + return "HIERARCHY_REQUEST_ERR"; + } + + // "If child is not null and its parent is not parent, throw a NotFoundError + // exception." + if (child && child.parentNode != parent_) { + return "NOT_FOUND_ERR"; + } + + // "If node is not a DocumentFragment, DocumentType, Element, Text, + // ProcessingInstruction, or Comment node, throw a HierarchyRequestError." + if (node.nodeType != Node.DOCUMENT_FRAGMENT_NODE + && node.nodeType != Node.DOCUMENT_TYPE_NODE + && node.nodeType != Node.ELEMENT_NODE + && node.nodeType != Node.TEXT_NODE + && node.nodeType != Node.PROCESSING_INSTRUCTION_NODE + && node.nodeType != Node.COMMENT_NODE) { + return "HIERARCHY_REQUEST_ERR"; + } + + // "If either node is a Text node and parent is a document, or node is a + // doctype and parent is not a document, throw a HierarchyRequestError." + if ((node.nodeType == Node.TEXT_NODE + && parent_.nodeType == Node.DOCUMENT_NODE) + || (node.nodeType == Node.DOCUMENT_TYPE_NODE + && parent_.nodeType != Node.DOCUMENT_NODE)) { + return "HIERARCHY_REQUEST_ERR"; + } + + // "If parent is a document, and any of the statements below, switched on + // node, are true, throw a HierarchyRequestError." + if (parent_.nodeType == Node.DOCUMENT_NODE) { + switch (node.nodeType) { + case Node.DOCUMENT_FRAGMENT_NODE: + // "If node has more than one element child or has a Text node + // child. Otherwise, if node has one element child and either + // parent has an element child, child is a doctype, or child is not + // null and a doctype is following child." + if ([].filter.call(node.childNodes, isElement).length > 1) { + return "HIERARCHY_REQUEST_ERR"; + } + + if ([].some.call(node.childNodes, isText)) { + return "HIERARCHY_REQUEST_ERR"; + } + + if ([].filter.call(node.childNodes, isElement).length == 1) { + if ([].some.call(parent_.childNodes, isElement)) { + return "HIERARCHY_REQUEST_ERR"; + } + + if (child && child.nodeType == Node.DOCUMENT_TYPE_NODE) { + return "HIERARCHY_REQUEST_ERR"; + } + + if (child && [].slice.call(parent_.childNodes, indexOf(child) + 1) + .filter(isDoctype)) { + return "HIERARCHY_REQUEST_ERR"; + } + } + break; + + case Node.ELEMENT_NODE: + // "parent has an element child, child is a doctype, or child is + // not null and a doctype is following child." + if ([].some.call(parent_.childNodes, isElement)) { + return "HIERARCHY_REQUEST_ERR"; + } + + if (child.nodeType == Node.DOCUMENT_TYPE_NODE) { + return "HIERARCHY_REQUEST_ERR"; + } + + if (child && [].slice.call(parent_.childNodes, indexOf(child) + 1) + .filter(isDoctype)) { + return "HIERARCHY_REQUEST_ERR"; + } + break; + + case Node.DOCUMENT_TYPE_NODE: + // "parent has a doctype child, an element is preceding child, or + // child is null and parent has an element child." + if ([].some.call(parent_.childNodes, isDoctype)) { + return "HIERARCHY_REQUEST_ERR"; + } + + if (child && [].slice.call(parent_.childNodes, 0, indexOf(child)) + .some(isElement)) { + return "HIERARCHY_REQUEST_ERR"; + } + + if (!child && [].some.call(parent_.childNodes, isElement)) { + return "HIERARCHY_REQUEST_ERR"; + } + break; + } + } +} + +/** + * Asserts that two nodes are equal, in the sense of isEqualNode(). If they + * aren't, tries to print a relatively informative reason why not. TODO: Move + * this to testharness.js? + */ +function assertNodesEqual(actual, expected, msg) { + if (!actual.isEqualNode(expected)) { + msg = "Actual and expected mismatch for " + msg + ". "; + + while (actual && expected) { + assert_true(actual.nodeType === expected.nodeType + && actual.nodeName === expected.nodeName + && actual.nodeValue === expected.nodeValue + && actual.childNodes.length === expected.childNodes.length, + "First differing node: expected " + format_value(expected) + + ", got " + format_value(actual)); + actual = nextNode(actual); + expected = nextNode(expected); + } + + assert_unreached("DOMs were not equal but we couldn't figure out why"); + } +} + +/** + * Given a DOMException, return the name (e.g., "HIERARCHY_REQUEST_ERR"). + */ +function getDomExceptionName(e) { + var ret = null; + for (var prop in e) { + if (/^[A-Z_]+_ERR$/.test(prop) && e[prop] == e.code) { + return prop; + } + } + + throw "Exception seems to not be a DOMException? " + e; +} + +/** + * Given an array of endpoint data [start container, start offset, end + * container, end offset], returns a Range with those endpoints. + */ +function rangeFromEndpoints(endpoints) { + // If we just use document instead of the ownerDocument of endpoints[0], + // WebKit will throw on setStart/setEnd. This is a WebKit bug, but it's in + // range, not selection, so we don't want to fail anything for it. + var range = ownerDocument(endpoints[0]).createRange(); + range.setStart(endpoints[0], endpoints[1]); + range.setEnd(endpoints[2], endpoints[3]); + return range; +} diff --git a/dom/imptests/html/dom/test_interface-objects.html b/dom/imptests/html/dom/test_interface-objects.html new file mode 100644 index 000000000..0e2bf1acd --- /dev/null +++ b/dom/imptests/html/dom/test_interface-objects.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<title>Interfaces</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<script> +function testInterfaceDeletable(iface) { + test(function() { + assert_true(!!window[iface], "Interface should exist.") + assert_true(delete window[iface], "The delete operator should return true.") + assert_equals(window[iface], undefined, "Interface should be gone.") + }, "Should be able to delete " + iface + ".") +} +var interfaces = [ + "Event", + "CustomEvent", + "EventTarget", + "Node", + "Document", + "DOMImplementation", + "DocumentFragment", + "ProcessingInstruction", + "DocumentType", + "Element", + "Attr", + "CharacterData", + "Text", + "Comment", + "NodeIterator", + "TreeWalker", + "NodeFilter", + "NodeList", + "HTMLCollection", + "DOMStringList", + "DOMTokenList", + ]; +test(function() { + for (var p in window) { + interfaces.forEach(function(i) { + assert_not_equals(p, i) + }) + } +}, "Interface objects properties should not be Enumerable") +interfaces.forEach(testInterfaceDeletable); +</script> diff --git a/dom/imptests/html/dom/test_interfaces.html b/dom/imptests/html/dom/test_interfaces.html new file mode 100644 index 000000000..9299ec8b7 --- /dev/null +++ b/dom/imptests/html/dom/test_interfaces.html @@ -0,0 +1,455 @@ +<!doctype html> +<meta charset=utf-8> +<title>DOM IDL tests</title> +<script src=/resources/testharness.js></script> +<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> +[Constructor(DOMString name)] +interface DOMError { + readonly attribute DOMString name; +}; + +[Constructor(DOMString type, optional EventInit eventInitDict)] +interface Event { + readonly attribute DOMString type; + readonly attribute EventTarget? target; + readonly attribute EventTarget? currentTarget; + + const unsigned short NONE = 0; + const unsigned short CAPTURING_PHASE = 1; + const unsigned short AT_TARGET = 2; + const unsigned short BUBBLING_PHASE = 3; + readonly attribute unsigned short eventPhase; + + void stopPropagation(); + void stopImmediatePropagation(); + + readonly attribute boolean bubbles; + readonly attribute boolean cancelable; + void preventDefault(); + readonly attribute boolean defaultPrevented; + + [Unforgeable] readonly attribute boolean isTrusted; + readonly attribute /* DOMTimeStamp */ unsigned long long timeStamp; + + void initEvent(DOMString type, boolean bubbles, boolean cancelable); +}; + +dictionary EventInit { + boolean bubbles = false; + boolean cancelable = false; +}; + +[Constructor(DOMString type, optional CustomEventInit eventInitDict)] +interface CustomEvent : Event { + readonly attribute any detail; + + void initCustomEvent(DOMString type, boolean bubbles, boolean cancelable, any details); +}; + +dictionary CustomEventInit : EventInit { + any detail = null; +}; + +interface EventTarget { + void addEventListener(DOMString type, EventListener? callback, optional boolean capture); + void removeEventListener(DOMString type, EventListener? callback, optional boolean capture); + boolean dispatchEvent(Event event); +}; + +[Callback] +interface EventListener { + void handleEvent(Event event); +}; + +[NoInterfaceObject] +interface ParentNode { + readonly attribute HTMLCollection children; + readonly attribute Element? firstElementChild; + readonly attribute Element? lastElementChild; + readonly attribute unsigned long childElementCount; + + void prepend((Node or DOMString)... nodes); + void append((Node or DOMString)... nodes); +}; +Document implements ParentNode; +DocumentFragment implements ParentNode; +Element implements ParentNode; + +[NoInterfaceObject] +interface ChildNode { + void before((Node or DOMString)... nodes); + void after((Node or DOMString)... nodes); + void replace((Node or DOMString)... nodes); + void remove(); +}; +DocumentType implements ChildNode; +Element implements ChildNode; +CharacterData implements ChildNode; + +[NoInterfaceObject] +interface NonDocumentTypeChildNode { + readonly attribute Element? previousElementSibling; + readonly attribute Element? nextElementSibling; +}; +Element implements NonDocumentTypeChildNode; +CharacterData implements NonDocumentTypeChildNode; + +[Constructor(MutationCallback callback)] +interface MutationObserver { + void observe(Node target, MutationObserverInit options); + void disconnect(); + sequence<MutationRecord> takeRecords(); +}; + +callback MutationCallback = void (sequence<MutationRecord> mutations, MutationObserver observer); + +dictionary MutationObserverInit { + boolean childList = false; + boolean attributes = false; + boolean characterData = false; + boolean subtree = false; + boolean attributeOldValue = false; + boolean characterDataOldValue = false; + sequence<DOMString> attributeFilter; +}; + +interface MutationRecord { + readonly attribute DOMString type; + readonly attribute Node target; + readonly attribute NodeList addedNodes; + readonly attribute NodeList removedNodes; + readonly attribute Node? previousSibling; + readonly attribute Node? nextSibling; + readonly attribute DOMString? attributeName; + readonly attribute DOMString? attributeNamespace; + readonly attribute DOMString? oldValue; +}; + +interface Node : EventTarget { + const unsigned short ELEMENT_NODE = 1; + const unsigned short ATTRIBUTE_NODE = 2; // historical + const unsigned short TEXT_NODE = 3; + const unsigned short CDATA_SECTION_NODE = 4; // historical + const unsigned short ENTITY_REFERENCE_NODE = 5; // historical + const unsigned short ENTITY_NODE = 6; // historical + const unsigned short PROCESSING_INSTRUCTION_NODE = 7; + const unsigned short COMMENT_NODE = 8; + const unsigned short DOCUMENT_NODE = 9; + const unsigned short DOCUMENT_TYPE_NODE = 10; + const unsigned short DOCUMENT_FRAGMENT_NODE = 11; + const unsigned short NOTATION_NODE = 12; // historical + readonly attribute unsigned short nodeType; + readonly attribute DOMString nodeName; + + readonly attribute DOMString? baseURI; + + readonly attribute Document? ownerDocument; + readonly attribute Node? parentNode; + readonly attribute Element? parentElement; + boolean hasChildNodes(); + readonly attribute NodeList childNodes; + readonly attribute Node? firstChild; + readonly attribute Node? lastChild; + readonly attribute Node? previousSibling; + readonly attribute Node? nextSibling; + + attribute DOMString? nodeValue; + attribute DOMString? textContent; + void normalize(); + + Node cloneNode(optional boolean deep); + boolean isEqualNode(Node? node); + + const unsigned short DOCUMENT_POSITION_DISCONNECTED = 0x01; + const unsigned short DOCUMENT_POSITION_PRECEDING = 0x02; + const unsigned short DOCUMENT_POSITION_FOLLOWING = 0x04; + const unsigned short DOCUMENT_POSITION_CONTAINS = 0x08; + const unsigned short DOCUMENT_POSITION_CONTAINED_BY = 0x10; + const unsigned short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20; + unsigned short compareDocumentPosition(Node other); + boolean contains(Node? other); + + DOMString? lookupPrefix(DOMString? namespace); + DOMString? lookupNamespaceURI(DOMString? prefix); + boolean isDefaultNamespace(DOMString? namespace); + + Node insertBefore(Node node, Node? child); + Node appendChild(Node node); + Node replaceChild(Node node, Node child); + Node removeChild(Node child); +}; + +[Constructor] +interface Document : Node { + readonly attribute DOMImplementation implementation; + readonly attribute DOMString URL; + readonly attribute DOMString documentURI; + readonly attribute DOMString compatMode; + readonly attribute DOMString characterSet; + readonly attribute DOMString contentType; + + readonly attribute DocumentType? doctype; + readonly attribute Element? documentElement; + HTMLCollection getElementsByTagName(DOMString localName); + HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName); + HTMLCollection getElementsByClassName(DOMString classNames); + Element? getElementById(DOMString elementId); + + Element createElement(DOMString localName); + Element createElementNS(DOMString? namespace, DOMString qualifiedName); + DocumentFragment createDocumentFragment(); + Text createTextNode(DOMString data); + Comment createComment(DOMString data); + ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data); + + Node importNode(Node node, optional boolean deep); + Node adoptNode(Node node); + + Event createEvent(DOMString interface); + + Range createRange(); + + // NodeFilter.SHOW_ALL = 0xFFFFFFFF + NodeIterator createNodeIterator(Node root, optional unsigned long whatToShow, optional NodeFilter? filter); + TreeWalker createTreeWalker(Node root, optional unsigned long whatToShow, optional NodeFilter? filter); +}; + +interface XMLDocument : Document {}; + +interface DOMImplementation { + DocumentType createDocumentType(DOMString qualifiedName, DOMString publicId, DOMString systemId); + XMLDocument createDocument(DOMString? namespace, [TreatNullAs=EmptyString] DOMString qualifiedName, optional DocumentType? doctype = null); + Document createHTMLDocument(optional DOMString title); + + boolean hasFeature(DOMString feature, [TreatNullAs=EmptyString] DOMString version); +}; + +[Constructor] +interface DocumentFragment : Node { +}; + +interface DocumentType : Node { + readonly attribute DOMString name; + readonly attribute DOMString publicId; + readonly attribute DOMString systemId; +}; + +interface Element : Node { + readonly attribute DOMString? namespaceURI; + readonly attribute DOMString? prefix; + readonly attribute DOMString localName; + readonly attribute DOMString tagName; + + attribute DOMString id; + attribute DOMString className; + [PutForwards=value] + readonly attribute DOMTokenList classList; + + readonly attribute Attr[] attributes; + DOMString? getAttribute(DOMString name); + DOMString? getAttributeNS(DOMString? namespace, DOMString localName); + void setAttribute(DOMString name, DOMString value); + void setAttributeNS(DOMString? namespace, DOMString name, DOMString value); + void removeAttribute(DOMString name); + void removeAttributeNS(DOMString? namespace, DOMString localName); + boolean hasAttribute(DOMString name); + boolean hasAttributeNS(DOMString? namespace, DOMString localName); + + HTMLCollection getElementsByTagName(DOMString localName); + HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName); + HTMLCollection getElementsByClassName(DOMString classNames); +}; + +interface Attr { + readonly attribute DOMString localName; + attribute DOMString value; + + readonly attribute DOMString name; + readonly attribute DOMString? namespaceURI; + readonly attribute DOMString? prefix; +}; + +interface CharacterData : Node { + [TreatNullAs=EmptyString] attribute DOMString data; + readonly attribute unsigned long length; + DOMString substringData(unsigned long offset, unsigned long count); + void appendData(DOMString data); + void insertData(unsigned long offset, DOMString data); + void deleteData(unsigned long offset, unsigned long count); + void replaceData(unsigned long offset, unsigned long count, DOMString data); +}; + +[Constructor(optional DOMString data)] +interface Text : CharacterData { + Text splitText(unsigned long offset); + readonly attribute DOMString wholeText; +}; + +interface ProcessingInstruction : CharacterData { + readonly attribute DOMString target; +}; + +[Constructor(optional DOMString data)] +interface Comment : CharacterData { +}; + +[Constructor] +interface Range { + readonly attribute Node startContainer; + readonly attribute unsigned long startOffset; + readonly attribute Node endContainer; + readonly attribute unsigned long endOffset; + readonly attribute boolean collapsed; + readonly attribute Node commonAncestorContainer; + + void setStart(Node refNode, unsigned long offset); + void setEnd(Node refNode, unsigned long offset); + void setStartBefore(Node refNode); + void setStartAfter(Node refNode); + void setEndBefore(Node refNode); + void setEndAfter(Node refNode); + void collapse(optional boolean toStart = false); + void selectNode(Node refNode); + void selectNodeContents(Node refNode); + + const unsigned short START_TO_START = 0; + const unsigned short START_TO_END = 1; + const unsigned short END_TO_END = 2; + const unsigned short END_TO_START = 3; + short compareBoundaryPoints(unsigned short how, Range sourceRange); + + void deleteContents(); + DocumentFragment extractContents(); + DocumentFragment cloneContents(); + void insertNode(Node node); + void surroundContents(Node newParent); + + Range cloneRange(); + void detach(); + + boolean isPointInRange(Node node, unsigned long offset); + short comparePoint(Node node, unsigned long offset); + + boolean intersectsNode(Node node); + + stringifier; +}; + +interface NodeIterator { + readonly attribute Node root; + readonly attribute Node? referenceNode; + readonly attribute boolean pointerBeforeReferenceNode; + readonly attribute unsigned long whatToShow; + readonly attribute NodeFilter? filter; + + Node? nextNode(); + Node? previousNode(); + + void detach(); +}; + +interface TreeWalker { + readonly attribute Node root; + readonly attribute unsigned long whatToShow; + readonly attribute NodeFilter? filter; + attribute Node currentNode; + + Node? parentNode(); + Node? firstChild(); + Node? lastChild(); + Node? previousSibling(); + Node? nextSibling(); + Node? previousNode(); + Node? nextNode(); +}; + +[Callback] +interface NodeFilter { + // Constants for acceptNode() + const unsigned short FILTER_ACCEPT = 1; + const unsigned short FILTER_REJECT = 2; + const unsigned short FILTER_SKIP = 3; + + // Constants for whatToShow + const unsigned long SHOW_ALL = 0xFFFFFFFF; + const unsigned long SHOW_ELEMENT = 0x1; + const unsigned long SHOW_ATTRIBUTE = 0x2; // historical + const unsigned long SHOW_TEXT = 0x4; + const unsigned long SHOW_CDATA_SECTION = 0x8; // historical + const unsigned long SHOW_ENTITY_REFERENCE = 0x10; // historical + const unsigned long SHOW_ENTITY = 0x20; // historical + const unsigned long SHOW_PROCESSING_INSTRUCTION = 0x40; + const unsigned long SHOW_COMMENT = 0x80; + const unsigned long SHOW_DOCUMENT = 0x100; + const unsigned long SHOW_DOCUMENT_TYPE = 0x200; + const unsigned long SHOW_DOCUMENT_FRAGMENT = 0x400; + const unsigned long SHOW_NOTATION = 0x800; // historical + + unsigned short acceptNode(Node node); +}; + +[ArrayClass] +interface NodeList { + getter Node? item(unsigned long index); + readonly attribute unsigned long length; +}; + +interface HTMLCollection { + readonly attribute unsigned long length; + getter Element? item(unsigned long index); + getter object? namedItem(DOMString name); // only returns Element +}; + +interface DOMTokenList { + readonly attribute unsigned long length; + getter DOMString? item(unsigned long index); + boolean contains(DOMString token); + void add(DOMString... tokens); + void remove(DOMString... tokens); + boolean toggle(DOMString token, optional boolean force); + stringifier; + attribute DOMString value; +}; +</script> +<script> +"use strict"; +var xmlDoc, detachedRange, element; +var idlArray; +setup(function() { + xmlDoc = document.implementation.createDocument(null, "", null); + 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({ + 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]'], + Text: ['document.createTextNode("abc")'], + ProcessingInstruction: ['xmlDoc.createProcessingInstruction("abc", "def")'], + Comment: ['document.createComment("abc")'], + Range: ['document.createRange()', 'detachedRange'], + NodeIterator: ['document.createNodeIterator(document.body, NodeFilter.SHOW_ALL, null, false)'], + TreeWalker: ['document.createTreeWalker(document.body, NodeFilter.SHOW_ALL, null, false)'], + NodeList: ['document.querySelectorAll("script")'], + HTMLCollection: ['document.body.children'], + DOMTokenList: ['document.body.classList'], + }); +}); +idlArray.test(); +</script> diff --git a/dom/imptests/html/html/browsers/the-window-object/test_window-named-properties.html b/dom/imptests/html/html/browsers/the-window-object/test_window-named-properties.html new file mode 100644 index 000000000..b01e24da9 --- /dev/null +++ b/dom/imptests/html/html/browsers/the-window-object/test_window-named-properties.html @@ -0,0 +1,69 @@ +<!doctype html> +<meta charset=utf-8> +<title>Changes to named properties of the window object</title> +<link rel="author" title="Ms2ger" href="ms2ger@gmail.com"> +<link rel="author" title="Boris Zbarsky" href="bzbarsky@mit.edu"> +<link rel="help" href="http://www.whatwg.org/html/#window"> +<link rel="help" href="http://www.whatwg.org/html/#dom-window-nameditem"> +<link rel="help" href="http://dev.w3.org/2006/webapi/WebIDL/#named-properties-object"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id=log></div> +<iframe name="bar"></iframe> +<iframe name="constructor"></iframe> +<script> +function assert_data_propdesc(pd, Writable, Enumerable, Configurable) { + assert_equals(typeof pd, "object"); + assert_equals(pd.writable, Writable); + assert_equals(pd.enumerable, Enumerable); + assert_equals(pd.configurable, Configurable); +} +test(function() { + assert_true("bar" in window, "bar not in window"); + assert_equals(window["bar"], + document.getElementsByTagName("iframe")[0].contentWindow); +}, "Static name"); +test(function() { + assert_true("bar" in Window.prototype, "bar in Window.prototype"); + assert_false(Window.prototype.hasOwnProperty("bar"), "Window.prototype.hasOwnProperty(\"bar\")"); + + var gsp = Object.getPrototypeOf(Object.getPrototypeOf(window)); + assert_true("bar" in gsp, "bar in gsp"); + assert_true(gsp.hasOwnProperty("bar"), "gsp.hasOwnProperty(\"bar\")"); + assert_data_propdesc(Object.getOwnPropertyDescriptor(gsp, "bar"), + true, false, true); +}, "Static name on the prototype"); +test(function() { + assert_equals(window.constructor, Window); + assert_false(window.hasOwnProperty("constructor"), "window.constructor should not be an own property."); + + var proto = Object.getPrototypeOf(window); + assert_equals(proto.constructor, Window); + assert_true("constructor" in proto, "constructor in proto"); + assert_data_propdesc(Object.getOwnPropertyDescriptor(proto, "constructor"), + true, false, true); + + var gsp = Object.getPrototypeOf(proto); + assert_true("constructor" in gsp, "constructor in gsp"); + assert_false(gsp.hasOwnProperty("constructor"), "gsp.hasOwnProperty(\"constructor\")"); + assert_equals(Object.getOwnPropertyDescriptor(gsp, "constructor"), undefined) +}, "constructor"); +var t = async_test("Dynamic name") +var t2 = async_test("Ghost name") +t.step(function() { + var iframe = document.getElementsByTagName("iframe")[0]; + iframe.setAttribute("src", "data:text/html,<script>window.name='foo'<\/script>"); + iframe.onload = function() { + t.step(function() { + assert_true("foo" in window, "foo not in window"); + assert_equals(window["foo"], iframe.contentWindow); + }); + t.done(); + t2.step(function() { + assert_false("bar" in window, "bar still in window"); + assert_equals(window["bar"], undefined); + }); + t2.done(); + }; +}); +</script> diff --git a/dom/imptests/html/html/dom/documents/dta/test_nameditem-06.html b/dom/imptests/html/html/dom/documents/dta/test_nameditem-06.html new file mode 100644 index 000000000..3fd3d474a --- /dev/null +++ b/dom/imptests/html/html/dom/documents/dta/test_nameditem-06.html @@ -0,0 +1,104 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Named items: imgs</title> +<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"> +<link rel="help" href="http://www.whatwg.org/html/#dom-document-nameditem"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<div id="test"> +<img name=test1> + +<img name=test2> +<img name=test2> + +<img id=test3> + +<img id=test4> +<img id=test4> + +<img name=test5> +<img id=test5> + +<img id=test6> +<img name=test6> + +<img id=test7 name=fail> + +<img name=test8 id=fail> +</div> +<script> +test(function() { + var img = document.getElementsByTagName("img")[0]; + assert_equals(img.name, "test1"); + + assert_true("test1" in document, '"test1" in document should be true'); + assert_equals(document.test1, img); +}, "If there is one img, it should be returned (name)"); + +test(function() { + var img1 = document.getElementsByTagName("img")[1]; + assert_equals(img1.name, "test2"); + var img2 = document.getElementsByTagName("img")[2]; + assert_equals(img2.name, "test2"); + + assert_true("test2" in document, '"test2" in document should be true'); + var collection = document.test2; + assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection"); + assert_array_equals(collection, [img1, img2]); +}, "If there are two imgs, a collection should be returned. (name)"); + +test(function() { + var img = document.getElementsByTagName("img")[3]; + assert_equals(img.id, "test3"); + + assert_false("test3" in document, '"test3" in document should be false'); + assert_equals(document.test3, undefined); +}, "If there is one img, it should not be returned (id)"); + +test(function() { + var img1 = document.getElementsByTagName("img")[4]; + assert_equals(img1.id, "test4"); + var img2 = document.getElementsByTagName("img")[5]; + assert_equals(img2.id, "test4"); + + assert_false("test4" in document, '"test4" in document should be false'); + assert_equals(document.test4, undefined); +}, "If there are two imgs, nothing should be returned. (id)"); + +test(function() { + var img1 = document.getElementsByTagName("img")[6]; + assert_equals(img1.name, "test5"); + var img2 = document.getElementsByTagName("img")[7]; + assert_equals(img2.id, "test5"); + + assert_true("test5" in document, '"test5" in document should be true'); + assert_equals(document.test5, img1); +}, "If there are two imgs, the one with a name should be returned. (name and id)"); + +test(function() { + var img1 = document.getElementsByTagName("img")[8]; + assert_equals(img1.id, "test6"); + var img2 = document.getElementsByTagName("img")[9]; + assert_equals(img2.name, "test6"); + + assert_true("test6" in document, '"test6" in document should be true'); + assert_equals(document.test6, img2); +}, "If there are two imgs, the one with a name should be returned. (id and name)"); + +test(function() { + var img = document.getElementsByTagName("img")[10]; + assert_equals(img.id, "test7"); + + assert_true("test7" in document, '"test7" in document should be true'); + assert_equals(document.test7, img); +}, "A name should affect getting an img by id"); + +test(function() { + var img = document.getElementsByTagName("img")[11]; + assert_equals(img.name, "test8"); + + assert_true("test8" in document, '"test8" in document should be true'); + assert_equals(document.test8, img); +}, "An id shouldn't affect getting an img by name"); +</script> diff --git a/dom/imptests/html/html/dom/elements/global-attributes/reftest-stylo.list b/dom/imptests/html/html/dom/elements/global-attributes/reftest-stylo.list new file mode 100644 index 000000000..4e76b8532 --- /dev/null +++ b/dom/imptests/html/html/dom/elements/global-attributes/reftest-stylo.list @@ -0,0 +1,59 @@ +# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing +# THIS FILE IS AUTOGENERATED BY importTestsuite.py - DO NOT EDIT + +== dir_auto-contained-bdi-L.html dir_auto-contained-bdi-L.html +== dir_auto-contained-bdi-R.html dir_auto-contained-bdi-R.html +== dir_auto-contained-dir_auto-L.html dir_auto-contained-dir_auto-L.html +== dir_auto-contained-dir_auto-R.html dir_auto-contained-dir_auto-R.html +== dir_auto-contained-dir-L.html dir_auto-contained-dir-L.html +== dir_auto-contained-dir-R.html dir_auto-contained-dir-R.html +== dir_auto-contained-L.html dir_auto-contained-L.html +== dir_auto-contained-R.html dir_auto-contained-R.html +== dir_auto-contained-script-L.html dir_auto-contained-script-L.html +== dir_auto-contained-script-R.html dir_auto-contained-script-R.html +== dir_auto-contained-style-L.html dir_auto-contained-style-L.html +== dir_auto-contained-style-R.html dir_auto-contained-style-R.html +== dir_auto-contained-textarea-L.html dir_auto-contained-textarea-L.html +== dir_auto-contained-textarea-R.html dir_auto-contained-textarea-R.html +== dir_auto-EN-L.html dir_auto-EN-L.html +== dir_auto-EN-R.html dir_auto-EN-R.html +== dir_auto-input-EN-L.html dir_auto-input-EN-L.html +== dir_auto-input-EN-R.html dir_auto-input-EN-R.html +== dir_auto-input-L.html dir_auto-input-L.html +== dir_auto-input-N-EN.html dir_auto-input-N-EN.html +== dir_auto-input-N-EN-L.html dir_auto-input-N-EN-L.html +== dir_auto-input-N-EN-R.html dir_auto-input-N-EN-R.html +== dir_auto-input-N-L.html dir_auto-input-N-L.html +== dir_auto-input-N-R.html dir_auto-input-N-R.html +== dir_auto-input-R.html dir_auto-input-R.html +== dir_auto-input-script-EN-L.html dir_auto-input-script-EN-L.html +== dir_auto-input-script-EN-R.html dir_auto-input-script-EN-R.html +== dir_auto-input-script-L.html dir_auto-input-script-L.html +== dir_auto-input-script-N-EN.html dir_auto-input-script-N-EN.html +== dir_auto-input-script-N-EN-L.html dir_auto-input-script-N-EN-L.html +== dir_auto-input-script-N-EN-R.html dir_auto-input-script-N-EN-R.html +== dir_auto-input-script-N-L.html dir_auto-input-script-N-L.html +== dir_auto-input-script-N-R.html dir_auto-input-script-N-R.html +== dir_auto-input-script-R.html dir_auto-input-script-R.html +== dir_auto-isolate.html dir_auto-isolate.html +== dir_auto-L.html dir_auto-L.html +== dir_auto-N-EN.html dir_auto-N-EN.html +== dir_auto-N-EN-L.html dir_auto-N-EN-L.html +== dir_auto-N-EN-R.html dir_auto-N-EN-R.html +== dir_auto-N-L.html dir_auto-N-L.html +== dir_auto-N-R.html dir_auto-N-R.html +== dir_auto-pre-mixed.html dir_auto-pre-mixed.html +== dir_auto-pre-N-between-Rs.html dir_auto-pre-N-between-Rs.html +== dir_auto-pre-N-EN.html dir_auto-pre-N-EN.html +== dir_auto-R.html dir_auto-R.html +== dir_auto-textarea-mixed.html dir_auto-textarea-mixed.html +fails-if(B2G||Mulet||(Android&&asyncPan)) == dir_auto-textarea-N-between-Rs.html dir_auto-textarea-N-between-Rs.html +# B2G scrollbar on opposite side +== dir_auto-textarea-N-EN.html dir_auto-textarea-N-EN.html +== dir_auto-textarea-script-mixed.html dir_auto-textarea-script-mixed.html +fails-if(B2G||Mulet||(Android&&asyncPan)) == dir_auto-textarea-script-N-between-Rs.html dir_auto-textarea-script-N-between-Rs.html +# B2G scrollbar on reference only +== dir_auto-textarea-script-N-EN.html dir_auto-textarea-script-N-EN.html +== lang-xyzzy.html lang-xyzzy.html +== lang-xmllang-01.html lang-xmllang-01.html +== style-01.html style-01.html diff --git a/dom/imptests/html/html/dom/elements/global-attributes/reftest.list b/dom/imptests/html/html/dom/elements/global-attributes/reftest.list new file mode 100644 index 000000000..a58929e92 --- /dev/null +++ b/dom/imptests/html/html/dom/elements/global-attributes/reftest.list @@ -0,0 +1,56 @@ +# THIS FILE IS AUTOGENERATED BY importTestsuite.py - DO NOT EDIT + +== dir_auto-contained-bdi-L.html dir_auto-contained-bdi-L-ref.html +== dir_auto-contained-bdi-R.html dir_auto-contained-bdi-R-ref.html +== dir_auto-contained-dir_auto-L.html dir_auto-contained-dir_auto-L-ref.html +== dir_auto-contained-dir_auto-R.html dir_auto-contained-dir_auto-R-ref.html +== dir_auto-contained-dir-L.html dir_auto-contained-dir-L-ref.html +== dir_auto-contained-dir-R.html dir_auto-contained-dir-R-ref.html +== dir_auto-contained-L.html dir_auto-contained-L-ref.html +== dir_auto-contained-R.html dir_auto-contained-R-ref.html +== dir_auto-contained-script-L.html dir_auto-contained-script-L-ref.html +== dir_auto-contained-script-R.html dir_auto-contained-script-R-ref.html +== dir_auto-contained-style-L.html dir_auto-contained-style-L-ref.html +== dir_auto-contained-style-R.html dir_auto-contained-style-R-ref.html +== dir_auto-contained-textarea-L.html dir_auto-contained-textarea-L-ref.html +== dir_auto-contained-textarea-R.html dir_auto-contained-textarea-R-ref.html +== dir_auto-EN-L.html dir_auto-EN-L-ref.html +== dir_auto-EN-R.html dir_auto-EN-R-ref.html +== dir_auto-input-EN-L.html dir_auto-input-EN-L-ref.html +== dir_auto-input-EN-R.html dir_auto-input-EN-R-ref.html +== dir_auto-input-L.html dir_auto-input-L-ref.html +== dir_auto-input-N-EN.html dir_auto-input-N-EN-ref.html +== dir_auto-input-N-EN-L.html dir_auto-input-N-EN-L-ref.html +== dir_auto-input-N-EN-R.html dir_auto-input-N-EN-R-ref.html +== dir_auto-input-N-L.html dir_auto-input-N-L-ref.html +== dir_auto-input-N-R.html dir_auto-input-N-R-ref.html +== dir_auto-input-R.html dir_auto-input-R-ref.html +== dir_auto-input-script-EN-L.html dir_auto-input-script-EN-L-ref.html +== dir_auto-input-script-EN-R.html dir_auto-input-script-EN-R-ref.html +== dir_auto-input-script-L.html dir_auto-input-script-L-ref.html +== dir_auto-input-script-N-EN.html dir_auto-input-script-N-EN-ref.html +== dir_auto-input-script-N-EN-L.html dir_auto-input-script-N-EN-L-ref.html +== dir_auto-input-script-N-EN-R.html dir_auto-input-script-N-EN-R-ref.html +== dir_auto-input-script-N-L.html dir_auto-input-script-N-L-ref.html +== dir_auto-input-script-N-R.html dir_auto-input-script-N-R-ref.html +== dir_auto-input-script-R.html dir_auto-input-script-R-ref.html +== dir_auto-isolate.html dir_auto-isolate-ref.html +== dir_auto-L.html dir_auto-L-ref.html +== dir_auto-N-EN.html dir_auto-N-EN-ref.html +== dir_auto-N-EN-L.html dir_auto-N-EN-L-ref.html +== dir_auto-N-EN-R.html dir_auto-N-EN-R-ref.html +== dir_auto-N-L.html dir_auto-N-L-ref.html +== dir_auto-N-R.html dir_auto-N-R-ref.html +== dir_auto-pre-mixed.html dir_auto-pre-mixed-ref.html +== dir_auto-pre-N-between-Rs.html dir_auto-pre-N-between-Rs-ref.html +== dir_auto-pre-N-EN.html dir_auto-pre-N-EN-ref.html +== dir_auto-R.html dir_auto-R-ref.html +== dir_auto-textarea-mixed.html dir_auto-textarea-mixed-ref.html +fails-if(Android&&asyncPan) == dir_auto-textarea-N-between-Rs.html dir_auto-textarea-N-between-Rs-ref.html +== dir_auto-textarea-N-EN.html dir_auto-textarea-N-EN-ref.html +== dir_auto-textarea-script-mixed.html dir_auto-textarea-script-mixed-ref.html +fails-if(Android&&asyncPan) == dir_auto-textarea-script-N-between-Rs.html dir_auto-textarea-script-N-between-Rs-ref.html +== dir_auto-textarea-script-N-EN.html dir_auto-textarea-script-N-EN-ref.html +== lang-xyzzy.html lang-xyzzy-ref.html +== lang-xmllang-01.html lang-xmllang-01-ref.html +== style-01.html style-01-ref.html diff --git a/dom/imptests/html/js/builtins/test_WeakMap.prototype-properties.html b/dom/imptests/html/js/builtins/test_WeakMap.prototype-properties.html new file mode 100644 index 000000000..0bdb6bb86 --- /dev/null +++ b/dom/imptests/html/js/builtins/test_WeakMap.prototype-properties.html @@ -0,0 +1,103 @@ +<!doctype html> +<title>WeakMap.prototype</title> +<link rel=author href=mailto:Ms2ger@gmail.com title=Ms2ger> +<link rel=help href=http://people.mozilla.org/~jorendorff/es6-draft.html#sec-15.15.5> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<div id=log></div> +<script> +function assert_propdesc(obj, prop, Writable, Enumerable, Configurable) { + var propdesc = Object.getOwnPropertyDescriptor(obj, prop); + assert_equals(typeof propdesc, "object"); + assert_equals(propdesc.writable, Writable, "[[Writable]]"); + assert_equals(propdesc.enumerable, Enumerable, "[[Enumerable]]"); + assert_equals(propdesc.configurable, Configurable, "[[Configurable]]"); +} + +function test_length(fun, expected) { + test(function() { + assert_propdesc(WeakMap.prototype[fun], "length", false, false, false); + assert_equals(WeakMap.prototype[fun].length, expected); + }, "WeakMap.prototype." + fun + ".length") +} + +function test_thisval(fun, args) { + // Step 1-2 + test(function() { + assert_throws(new TypeError(), function() { + WeakMap.prototype[fun].apply(null, args); + }); + assert_throws(new TypeError(), function() { + WeakMap.prototype[fun].apply(undefined, args); + }); + }, "WeakMap.prototype." + fun + ": ToObject on this") + // Step 3 + test(function() { + assert_throws(new TypeError(), function() { + WeakMap.prototype[fun].apply({}, args); + }); + }, "WeakMap.prototype." + fun + ": this has no [[WeakMapData]] internal property") +} + +// In every case, the length property of a built-in Function object described +// in this clause has the attributes { [[Writable]]: false, [[Enumerable]]: +// false, [[Configurable]]: false }. Every other property described in this +// clause has the attributes { [[Writable]]: true, [[Enumerable]]: false, +// [[Configurable]]: true } unless otherwise specified. + +test(function() { + assert_equals(Object.getPrototypeOf(WeakMap.prototype), Object.prototype); +}, "The value of the [[Prototype]] internal property of the WeakMap prototype object is the standard built-in Object prototype object (15.2.4).") + +// 15.15.5.1 WeakMap.prototype.constructor +test(function() { + assert_equals(WeakMap.prototype.constructor, WeakMap); + assert_propdesc(WeakMap.prototype, "constructor", true, false, true); +}, "The initial value of WeakMap.prototype.constructor is the built-in WeakMap constructor.") + +// 15.15.5.2 WeakMap.prototype.delete ( key ) +test(function() { + assert_propdesc(WeakMap.prototype, "delete", true, false, true); + test_length("delete", 1); + // Step 1-3 + test_thisval("delete", [{}]); +}, "WeakMap.prototype.delete") + +// 15.15.5.3 WeakMap.prototype.get ( key ) +test(function() { + assert_propdesc(WeakMap.prototype, "get", true, false, true); + test_length("get", 1); + // Step 1-3 + test_thisval("get", [{}]); + + // Step 8 + test(function() { + var wm = new WeakMap(); + var key = {}; + var res = wm.get({}, {}); + assert_equals(res, undefined); + }, "WeakMap.prototype.get: return undefined"); +}, "WeakMap.prototype.get") + +// 15.14.5.4 Map.prototype.has ( key ) +test(function() { + assert_propdesc(WeakMap.prototype, "has", true, false, true); + test_length("has", 1); + // Step 1-3 + test_thisval("has", [{}]); +}, "WeakMap.prototype.has") + +// 15.14.5.5 Map.prototype.set ( key , value ) +test(function() { + assert_propdesc(WeakMap.prototype, "set", true, false, true); + test_length("set", 2); + // Step 1-3 + test_thisval("set", [{}, {}]); +}, "WeakMap.prototype.set") + +// 15.15.5.6 Map.prototype.@@toStringTag +test(function() { + assert_class_string(new WeakMap(), "WeakMap"); + assert_class_string(WeakMap.prototype, "WeakMap"); +}, "WeakMap.prototype.@@toStringTag") +</script> diff --git a/dom/imptests/html/mochitest.ini b/dom/imptests/html/mochitest.ini new file mode 100644 index 000000000..87b958f0e --- /dev/null +++ b/dom/imptests/html/mochitest.ini @@ -0,0 +1,24 @@ +# THIS FILE IS AUTOGENERATED BY importTestsuite.py - DO NOT EDIT +[DEFAULT] +support-files = + webgl/common.js + +[typedarrays/test_constructors.html] +[webgl/test_bufferSubData.html] +subsuite = gpu +skip-if = toolkit == 'android' #android(WebGL) +[webgl/test_compressedTexImage2D.html] +subsuite = gpu +skip-if = toolkit == 'android' #android(WebGL) +[webgl/test_compressedTexSubImage2D.html] +subsuite = gpu +skip-if = true # Bug 1226336 +[webgl/test_texImage2D.html] +subsuite = gpu +skip-if = toolkit == 'android' #android(WebGL) +[webgl/test_texSubImage2D.html] +subsuite = gpu +skip-if = toolkit == 'android' #android(WebGL) +[webgl/test_uniformMatrixNfv.html] +subsuite = gpu +skip-if = toolkit == 'android' #android(WebGL) diff --git a/dom/imptests/html/typedarrays/test_constructors.html b/dom/imptests/html/typedarrays/test_constructors.html new file mode 100644 index 000000000..60e0dc8fe --- /dev/null +++ b/dom/imptests/html/typedarrays/test_constructors.html @@ -0,0 +1,48 @@ +<!doctype html> +<title>Typed Array constructors</title> +<link rel=author href=mailto:Ms2ger@gmail.com title=Ms2ger> +<link rel=help href=https://www.khronos.org/registry/typedarray/specs/latest/#7> +<link rel=help href=http://dev.w3.org/2006/webapi/WebIDL/#dfn-overload-resolution-algorithm> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> + +<div id=log></div> +<script> +var args = [ + /* numbers */ + [NaN, 0], [+Infinity, 0], [-Infinity, 0], [+0, 0], [-0, 0], // Step 2 + [-0.4, 0], [-0.9, 0], [1.1, 1], [2.9, 2], // Step 3 + [1, 1], [-0xF1000000, 0xF000000], // Step 4 + /* strings */ + ["1", 1], ["1e2", 100], + /* null, undefined, booleans */ + [undefined, 0], [null, 0], [false, 0], [true, 1], + /* objects */ + [{}, 0], [{ length: 2, 0: 0, 1: 0 }, 0], [[0, 0], 2] +]; +var interfaces = [ + "Int8Array", "Uint8Array", "Uint8ClampedArray", "Int16Array", "Uint16Array", + "Int32Array", "Uint32Array", "Float32Array", "Float64Array" +]; + +test(function() { + interfaces.concat(["ArrayBuffer", "DataView"]).forEach(function(i) { + test(function() { + // XXX The spec is wrong here. + assert_throws(new TypeError(), function() { + new window[i](); + }); + }, "Constructing interface " + i + " with no arguments should throw."); + }); + interfaces.forEach(function(i) { + args.forEach(function(arg, j) { + var input = arg[0], expected = arg[1]; + test(function() { + var ta = new window[i](input); + assert_equals(ta.length, expected); + }, "The argument " + format_value(input) + " (" + j + ") should be interpreted as " + + expected + " for interface " + i + "."); + }); + }); +}); +</script> diff --git a/dom/imptests/html/webgl/common.js b/dom/imptests/html/webgl/common.js new file mode 100644 index 000000000..416c21ce9 --- /dev/null +++ b/dom/imptests/html/webgl/common.js @@ -0,0 +1,13 @@ +function getGl() { + var c = document.createElement("canvas"); + var gl = c.getContext("experimental-webgl"); + assert_true(!!gl, "Should be able to get a context."); + return gl; +} + +function shouldGenerateGLError(cx, glError, fn) { + test(function() { + fn(); + assert_equals(cx.getError(), glError); + }, "Calling " + fn + " should generate a " + glError + " error."); +} diff --git a/dom/imptests/html/webgl/test_bufferSubData.html b/dom/imptests/html/webgl/test_bufferSubData.html new file mode 100644 index 000000000..a97df9062 --- /dev/null +++ b/dom/imptests/html/webgl/test_bufferSubData.html @@ -0,0 +1,26 @@ +<!doctype html> +<title>bufferSubData</title> +<link rel=author href=mailto:Ms2ger@gmail.com title=Ms2ger> +<link rel=help href=https://www.khronos.org/registry/webgl/specs/latest/#5.14.5> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=common.js></script> + +<div id=log></div> +<script> +test(function() { + var gl = getGl(); + assert_equals(gl.getError(), WebGLRenderingContext.NO_ERROR); + + var b = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, b); + + var a = new Float32Array(1); + gl.bufferData(gl.ARRAY_BUFFER, a, gl.STATIC_DRAW); + + var nan = 0 / 0; + gl.bufferSubData(gl.ARRAY_BUFFER, nan, a); + + assert_equals(gl.getError(), WebGLRenderingContext.NO_ERROR); +}); +</script> diff --git a/dom/imptests/html/webgl/test_compressedTexImage2D.html b/dom/imptests/html/webgl/test_compressedTexImage2D.html new file mode 100644 index 000000000..b0a031add --- /dev/null +++ b/dom/imptests/html/webgl/test_compressedTexImage2D.html @@ -0,0 +1,30 @@ +<!doctype html> +<title>compressedTexImage2D</title> +<link rel=author href=mailto:Ms2ger@gmail.com title=Ms2ger> +<link rel=help href=https://www.khronos.org/registry/webgl/specs/latest/#5.14.8> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=common.js></script> + +<div id=log></div> +<script> +test(function() { + var COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; + var gl = getGl(); + + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + shouldGenerateGLError(gl, gl.INVALID_ENUM, function() { + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0, new Uint8Array(8)); + }); + shouldGenerateGLError(gl, gl.INVALID_ENUM, function() { + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0, new Uint8Array(8), null); + }); + test(function() { + assert_throws(new TypeError(), function() { + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0); + }); + }, "Should throw a TypeError when passing too few arguments."); +}); +</script> diff --git a/dom/imptests/html/webgl/test_compressedTexSubImage2D.html b/dom/imptests/html/webgl/test_compressedTexSubImage2D.html new file mode 100644 index 000000000..539f9e17f --- /dev/null +++ b/dom/imptests/html/webgl/test_compressedTexSubImage2D.html @@ -0,0 +1,30 @@ +<!doctype html> +<title>compressedTexSubImage2D</title> +<link rel=author href=mailto:Ms2ger@gmail.com title=Ms2ger> +<link rel=help href=https://www.khronos.org/registry/webgl/specs/latest/#5.14.8> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=common.js></script> + +<div id=log></div> +<script> +test(function() { + var COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; + var gl = getGl(); + + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + shouldGenerateGLError(gl, gl.INVALID_ENUM, function() { + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 10, 10, COMPRESSED_RGB_S3TC_DXT1_EXT, new Uint8Array(8)); + }); + shouldGenerateGLError(gl, gl.INVALID_ENUM, function() { + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 10, 10, COMPRESSED_RGB_S3TC_DXT1_EXT, new Uint8Array(8), null); + }); + test(function() { + assert_throws(new TypeError(), function() { + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 10, 10, COMPRESSED_RGB_S3TC_DXT1_EXT); + }); + }, "Should throw a TypeError when passing too few arguments."); +}); +</script> diff --git a/dom/imptests/html/webgl/test_texImage2D.html b/dom/imptests/html/webgl/test_texImage2D.html new file mode 100644 index 000000000..2f769160d --- /dev/null +++ b/dom/imptests/html/webgl/test_texImage2D.html @@ -0,0 +1,20 @@ +<!doctype html> +<title>texImage2D</title> +<link rel=author href=mailto:Ms2ger@gmail.com title=Ms2ger> +<link rel=help href=https://www.khronos.org/registry/webgl/specs/latest/#5.14.8> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=common.js></script> + +<div id=log></div> +<script> +test(function() { + var gl = getGl(); + assert_throws(new TypeError(), function() { + gl.texImage2D(0, 0, 0, 0, 0, window); + }); + assert_throws(new TypeError(), function() { + gl.texImage2D(0, 0, 0, 0, 0, { get width() { throw 7 }, get height() { throw 7 }, data: new Uint8ClampedArray(10) }); + }); +}); +</script> diff --git a/dom/imptests/html/webgl/test_texSubImage2D.html b/dom/imptests/html/webgl/test_texSubImage2D.html new file mode 100644 index 000000000..294b30c76 --- /dev/null +++ b/dom/imptests/html/webgl/test_texSubImage2D.html @@ -0,0 +1,20 @@ +<!doctype html> +<title>texSubImage2D</title> +<link rel=author href=mailto:Ms2ger@gmail.com title=Ms2ger> +<link rel=help href=https://www.khronos.org/registry/webgl/specs/latest/#5.14.8> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=common.js></script> + +<div id=log></div> +<script> +test(function() { + var gl = getGl(); + assert_throws(new TypeError(), function() { + gl.texSubImage2D(0, 0, 0, 0, 0, 0, window); + }); + assert_throws(new TypeError(), function() { + gl.texSubImage2D(0, 0, 0, 0, 0, 0, { get width() { throw 7 }, get height() { throw 7 }, data: new Uint8ClampedArray(10) }); + }); +}); +</script> diff --git a/dom/imptests/html/webgl/test_uniformMatrixNfv.html b/dom/imptests/html/webgl/test_uniformMatrixNfv.html new file mode 100644 index 000000000..f75cbcb99 --- /dev/null +++ b/dom/imptests/html/webgl/test_uniformMatrixNfv.html @@ -0,0 +1,33 @@ +<!doctype html> +<title>uniformMatrix*fv</title> +<link rel=author href=mailto:Ms2ger@gmail.com title=Ms2ger> +<link rel=help href=https://www.khronos.org/registry/webgl/specs/latest/#WebGLRenderingContext> +<link rel=help href=http://dev.w3.org/2006/webapi/WebIDL/#es-boolean> +<link rel=help href=http://es5.github.com/#x9.2> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=common.js></script> + +<div id=log></div> +<script id="vshader" type="x-shader/x-vertex"> +uniform mat2 m2; +uniform mat3 m3; +uniform mat4 m4; +</script> +<script> +var floatArray = function(n) { + var a = []; + for (var i = 0; i < n; ++i) { + a.push(1); + } + return a; +}; +[2, 3, 4].forEach(function(n) { + test(function() { + var gl = getGl(); + var f = "uniformMatrix" + n + "fv"; + var loc = gl.getUniformLocation(gl.createProgram(), "m" + n); + gl[f](loc, { valueOf: function() { throw "Error"; } }, floatArray(n)); + }, "Should not throw for " + n); +}); +</script> diff --git a/dom/imptests/idlharness.js b/dom/imptests/idlharness.js new file mode 100644 index 000000000..8e41703e6 --- /dev/null +++ b/dom/imptests/idlharness.js @@ -0,0 +1,1706 @@ +/* +Distributed under both the W3C Test Suite License [1] and the W3C +3-clause BSD License [2]. To contribute to a W3C Test Suite, see the +policies and contribution forms [3]. + +[1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license +[2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license +[3] http://www.w3.org/2004/10/27-testcases +*/ + +/* For user documentation see docs/idlharness.md */ + +/** + * Notes for people who want to edit this file (not just use it as a library): + * + * Most of the interesting stuff happens in the derived classes of IdlObject, + * especially IdlInterface. The entry point for all IdlObjects is .test(), + * which is called by IdlArray.test(). An IdlObject is conceptually just + * "thing we want to run tests on", and an IdlArray is an array of IdlObjects + * with some additional data thrown in. + * + * The object model is based on what WebIDLParser.js produces, which is in turn + * based on its pegjs grammar. If you want to figure out what properties an + * object will have from WebIDLParser.js, the best way is to look at the + * grammar: + * + * https://github.com/darobin/webidl.js/blob/master/lib/grammar.peg + * + * So for instance: + * + * // interface definition + * interface + * = extAttrs:extendedAttributeList? S? "interface" S name:identifier w herit:ifInheritance? w "{" w mem:ifMember* w "}" w ";" w + * { return { type: "interface", name: name, inheritance: herit, members: mem, extAttrs: extAttrs }; } + * + * This means that an "interface" object will have a .type property equal to + * the string "interface", a .name property equal to the identifier that the + * parser found, an .inheritance property equal to either null or the result of + * the "ifInheritance" production found elsewhere in the grammar, and so on. + * After each grammatical production is a JavaScript function in curly braces + * that gets called with suitable arguments and returns some JavaScript value. + * + * (Note that the version of WebIDLParser.js we use might sometimes be + * out-of-date or forked.) + * + * The members and methods of the classes defined by this file are all at least + * briefly documented, hopefully. + */ +(function(){ +"use strict"; +/// 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 +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 = {}; + + /** + * A map from strings to arrays of strings. The keys are interface or + * exception names, and are expected to also exist as keys in this.members + * (otherwise they'll be ignored). This is populated by add_objects() -- + * see documentation at the start of the file. The actual tests will be + * run by calling this.members[name].test_object(obj) for each obj in + * this.objects[name]. obj is a string that will be eval'd to produce a + * JavaScript value, which is supposed to be an object implementing the + * given IdlObject (interface, exception, etc.). + */ + this.objects = {}; + + /** + * When adding multiple collections of IDLs one at a time, an earlier one + * might contain a partial interface or implements statement that depends + * on a later one. Save these up and handle them right before we run + * tests. + * + * .partials is simply an array of objects from WebIDLParser.js' + * "partialinterface" production. .implements maps strings to arrays of + * strings, such that + * + * A implements B; + * A implements C; + * D implements E; + * + * results in { A: ["B", "C"], D: ["E"] }. + */ + this.partials = []; + this["implements"] = {}; +}; + +//@} +IdlArray.prototype.add_idls = function(raw_idls) +//@{ +{ + /** Entry point. See documentation at beginning of file. */ + this.internal_add_idls(WebIDL2.parse(raw_idls)); +}; + +//@} +IdlArray.prototype.add_untested_idls = function(raw_idls) +//@{ +{ + /** Entry point. See documentation at beginning of file. */ + var parsed_idls = WebIDL2.parse(raw_idls); + for (var i = 0; i < parsed_idls.length; i++) + { + parsed_idls[i].untested = true; + if ("members" in parsed_idls[i]) + { + for (var j = 0; j < parsed_idls[i].members.length; j++) + { + parsed_idls[i].members[j].untested = true; + } + } + } + this.internal_add_idls(parsed_idls); +}; + +//@} +IdlArray.prototype.internal_add_idls = function(parsed_idls) +//@{ +{ + /** + * Internal helper called by add_idls() and add_untested_idls(). + * parsed_idls is an array of objects that come from WebIDLParser.js's + * "definitions" production. The add_untested_idls() entry point + * additionally sets an .untested property on each object (and its + * .members) so that they'll be skipped by test() -- they'll only be + * used for base interfaces of tested interfaces, return types, etc. + */ + parsed_idls.forEach(function(parsed_idl) + { + if (parsed_idl.type == "interface" && parsed_idl.partial) + { + this.partials.push(parsed_idl); + return; + } + + if (parsed_idl.type == "implements") + { + if (!(parsed_idl.target in this["implements"])) + { + this["implements"][parsed_idl.target] = []; + } + this["implements"][parsed_idl.target].push(parsed_idl["implements"]); + return; + } + + 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, /* 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; + + case "typedef": + this.members[parsed_idl.name] = new IdlTypedef(parsed_idl); + break; + + case "callback": + // TODO + console.log("callback not yet supported"); + break; + + case "enum": + this.members[parsed_idl.name] = new IdlEnum(parsed_idl); + break; + + case "callback interface": + 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)); +}; + +//@} +IdlArray.prototype.add_objects = function(dict) +//@{ +{ + /** Entry point. See documentation at beginning of file. */ + for (var k in dict) + { + if (k in this.objects) + { + this.objects[k] = this.objects[k].concat(dict[k]); + } + else + { + this.objects[k] = dict[k]; + } + } +}; + +//@} +IdlArray.prototype.prevent_multiple_testing = function(name) +//@{ +{ + /** Entry point. See documentation at beginning of file. */ + this.members[name].prevent_multiple_testing = true; +}; + +//@} +IdlArray.prototype.recursively_get_implements = function(interface_name) +//@{ +{ + /** + * Helper function for test(). Returns an array of things that implement + * interface_name, so if the IDL contains + * + * A implements B; + * B implements C; + * B implements D; + * + * then recursively_get_implements("A") should return ["B", "C", "D"]. + */ + var ret = this["implements"][interface_name]; + if (ret === undefined) + { + return []; + } + for (var i = 0; i < this["implements"][interface_name].length; i++) + { + ret = ret.concat(this.recursively_get_implements(ret[i])); + if (ret.indexOf(ret[i]) != ret.lastIndexOf(ret[i])) + { + throw "Circular implements statements involving " + ret[i]; + } + } + return ret; +}; + +//@} +IdlArray.prototype.test = function() +//@{ +{ + /** Entry point. See documentation at beginning of file. */ + + // First merge in all the partial interfaces and implements statements we + // encountered. + this.partials.forEach(function(parsed_idl) + { + if (!(parsed_idl.name in this.members) + || !(this.members[parsed_idl.name] instanceof IdlInterface)) + { + throw "Partial interface " + parsed_idl.name + " with no original interface"; + } + if (parsed_idl.extAttrs) + { + parsed_idl.extAttrs.forEach(function(extAttr) + { + this.members[parsed_idl.name].extAttrs.push(extAttr); + }.bind(this)); + } + parsed_idl.members.forEach(function(member) + { + this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member)); + }.bind(this)); + }.bind(this)); + this.partials = []; + + for (var lhs in this["implements"]) + { + this.recursively_get_implements(lhs).forEach(function(rhs) + { + var errStr = lhs + " implements " + rhs + ", but "; + if (!(lhs in this.members)) throw errStr + lhs + " is undefined."; + if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface."; + if (!(rhs in this.members)) throw errStr + rhs + " is undefined."; + if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface."; + this.members[rhs].members.forEach(function(member) + { + this.members[lhs].members.push(new IdlInterfaceMember(member)); + }.bind(this)); + }.bind(this)); + } + this["implements"] = {}; + + // Now run test() on every member, and test_object() for every object. + for (var name in this.members) + { + this.members[name].test(); + if (name in this.objects) + { + this.objects[name].forEach(function(str) + { + this.members[name].test_object(str); + }.bind(this)); + } + } +}; + +//@} +IdlArray.prototype.assert_type_is = function(value, type) +//@{ +{ + /** + * Helper function that tests that value is an instance of type according + * to the rules of WebIDL. value is any JavaScript value, and type is an + * object produced by WebIDLParser.js' "type" production. That production + * is fairly elaborate due to the complexity of WebIDL's types, so it's + * best to look at the grammar to figure out what properties it might have. + */ + if (type.idlType == "any") + { + // No assertions to make + return; + } + + if (type.nullable && value === null) + { + // This is fine + return; + } + + if (type.array) + { + // TODO: not supported yet + return; + } + + if (type.sequence) + { + assert_true(Array.isArray(value), "is not array"); + if (!value.length) + { + // Nothing we can do. + return; + } + this.assert_type_is(value[0], type.idlType.idlType); + return; + } + + type = type.idlType; + + switch(type) + { + case "void": + assert_equals(value, undefined); + return; + + case "boolean": + assert_equals(typeof value, "boolean"); + return; + + case "byte": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "not an integer"); + assert_true(-128 <= value && value <= 127, "byte " + value + " not in range [-128, 127]"); + return; + + case "octet": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "not an integer"); + assert_true(0 <= value && value <= 255, "octet " + value + " not in range [0, 255]"); + return; + + case "short": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "not an integer"); + assert_true(-32768 <= value && value <= 32767, "short " + value + " not in range [-32768, 32767]"); + return; + + case "unsigned short": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "not an integer"); + assert_true(0 <= value && value <= 65535, "unsigned short " + value + " not in range [0, 65535]"); + return; + + case "long": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "not an integer"); + assert_true(-2147483648 <= value && value <= 2147483647, "long " + value + " not in range [-2147483648, 2147483647]"); + return; + + case "unsigned long": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "not an integer"); + 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": + case "USVString": + // TODO: https://github.com/w3c/testharness.js/issues/92 + assert_equals(typeof value, "string"); + return; + + case "object": + assert_true(typeof value == "object" || typeof value == "function", "wrong type: not object or function"); + return; + } + + if (!(type in this.members)) + { + throw "Unrecognized type " + type; + } + + 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 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 self) + { + 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) + { + // TODO: Test when we actually have something to test this on + } + else if (this.members[type] instanceof IdlTypedef) + { + // TODO: Test when we actually have something to test this on + } + else + { + throw "Type " + type + " isn't an interface or dictionary"; + } +}; +//@} + +/// IdlObject /// +function IdlObject() {} +IdlObject.prototype.test = function() +//@{ +{ + /** + * By default, this does nothing, so no actual tests are run for IdlObjects + * that don't define any (e.g., IdlDictionary at the time of this writing). + */ +}; + +//@} +IdlObject.prototype.has_extended_attribute = function(name) +//@{ +{ + /** + * This is only meaningful for things that support extended attributes, + * such as interfaces, exceptions, and members. + */ + return this.extAttrs.some(function(o) + { + return o.name == name; + }); +}; + +//@} + +/// IdlDictionary /// +// Used for IdlArray.prototype.assert_type_is +function IdlDictionary(obj) +//@{ +{ + /** + * obj is an object produced by the WebIDLParser.js "dictionary" + * production. + */ + + /** Self-explanatory. */ + this.name = obj.name; + + /** An array of objects produced by the "dictionaryMember" production. */ + this.members = obj.members; + + /** + * The name (as a string) of the dictionary type we inherit from, or null + * if there is none. + */ + this.base = obj.inheritance; +} + +//@} +IdlDictionary.prototype = Object.create(IdlObject.prototype); + +/// 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; + + /** A back-reference to our IdlArray. */ + this.array = obj.array; + + /** + * An indicator of whether we should run tests on the (exception) interface + * object and (exception) interface prototype object. Tests on members are + * controlled by .untested on each member, not this. + */ + 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; +}; +//@} + +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; + } + + if (!this.untested) + { + // First test things to do with the exception/interface object and + // exception/interface prototype object. + this.test_self(); + } + // Then test things to do with its members (constants, fields, attributes, + // 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(); +}; +//@} + +IdlInterface.prototype.test_self = function() +//@{ +{ + test(function() + { + // This function tests WebIDL as of 2015-01-13. + // TODO: Consider [Exposed]. + + // "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(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(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 otherwise specified (see + // below). + + // "* Its [[Get]] internal property is set as described in ECMA-262 + // section 9.1.8." + // Not much to test for this. + + // "* Its [[Construct]] internal property is set as described in + // ECMA-262 section 19.2.2.3." + // Tested below if no constructor is defined. TODO: test constructors + // if defined. + + // "* Its @@hasInstance property is set as described in ECMA-262 + // section 19.2.3.8, unless otherwise specified." + // TODO + + // 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() { + self[this.name](); + }.bind(this), "interface object didn't throw TypeError when called as a function"); + assert_throws(new TypeError(), function() { + 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(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(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 = 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() + { + // This function tests WebIDL as of 2015-01-21. + // https://heycam.github.io/webidl/#interface-object + + 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; + } + + // "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(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 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). + 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 { + 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(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(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() + { + 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; + } + + 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(self[this.name].prototype, "constructor", + this.name + '.prototype does not have own property "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(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() + { + 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(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(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(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() + { + 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; + } + + assert_own_property(self[this.name], "prototype", + 'interface "' + this.name + '" does not have own property "prototype"'); + + 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(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() + { + 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(self[this.name], member.name, + "The interface object must have a property " + + format_value(member.name)); + } 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() { + self[this.name].prototype[member.name]; + }.bind(this), "getting property on prototype object must throw TypeError"); + } else { + assert_equals(self[this.name].prototype[member.name], undefined, + "getting property on prototype object must return undefined"); + } + 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() + { + 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; + } + + 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 memberHolderObject; + if (member["static"]) { + 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"); + memberHolderObject = self[this.name].prototype; + } + + this.do_member_operation_asserts(memberHolderObject, member); + }.bind(this), this.name + " interface: operation " + member.name + + "(" + member.arguments.map(function(m) { return m.idlType.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"); + + // 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() { + 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() { + memberHolderObject[member.name].apply({}, args); + }, "calling operation with this = {} didn't throw TypeError"); + } +} + +//@} +IdlInterface.prototype.test_member_stringifier = function(member) +//@{ +{ + test(function() + { + 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; + } + + 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 = 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_equals(desc.writable, !stringifierUnforgeable, + "property should be writable if and only if not unforgeable"); + assert_true(desc.enumerable, "property is not enumerable"); + 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() { + 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() { + 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() +//@{ +{ + for (var i = 0; i < this.members.length; i++) + { + var member = this.members[i]; + if (member.untested) { + continue; + } + + 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.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) { + 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; + } + } +}; + +//@} +IdlInterface.prototype.test_object = function(desc) +//@{ +{ + var obj, exception = null; + try + { + obj = eval(desc); + } + catch(e) + { + exception = e; + } + + // TODO: WebIDLParser doesn't currently support named legacycallers, so I'm + // not sure what those would look like in the AST + var expected_typeof = this.members.some(function(member) + { + return member.legacycaller + || ("idlType" in member && member.idlType.legacycaller) + || ("idlType" in member && typeof member.idlType == "object" + && "idlType" in member.idlType && member.idlType.idlType == "legacycaller"); + }) ? "function" : "object"; + + this.test_primary_interface_of(desc, obj, exception, expected_typeof); + var current_interface = this; + while (current_interface) + { + if (!(current_interface.name in this.array.members)) + { + throw "Interface " + current_interface.name + " not found (inherited by " + this.name + ")"; + } + if (current_interface.prevent_multiple_testing && current_interface.already_tested) + { + return; + } + current_interface.test_interface_of(desc, obj, exception, expected_typeof); + current_interface = this.array.members[current_interface.base]; + } +}; + +//@} +IdlInterface.prototype.test_primary_interface_of = function(desc, obj, exception, expected_typeof) +//@{ +{ + // 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 != 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(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), + 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() + { + assert_equals(exception, null, "Unexpected exception when evaluating object"); + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); + assert_class_string(obj, this.name, "class string of " + desc); + if (!this.has_stringifier()) + { + assert_equals(String(obj), "[object " + this.name + "]", "String(" + desc + ")"); + } + }.bind(this), "Stringification of " + desc); +}; + +//@} +IdlInterface.prototype.test_interface_of = function(desc, obj, exception, expected_typeof) +//@{ +{ + // 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.type == "attribute" && member.isUnforgeable) + { + test(function() + { + assert_equals(exception, null, "Unexpected exception when evaluating object"); + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); + 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"]) { + 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 + // anything. + var property, thrown = false; + try + { + property = obj[member.name]; + } + catch (e) + { + thrown = true; + } + if (!thrown) + { + this.array.assert_type_is(property, member.idlType); + } + } + if (member.type == "operation") + { + assert_equals(typeof obj[member.name], "function"); + } + } + }.bind(this), this.name + " interface: " + desc + ' must inherit property "' + member.name + '" with the proper type (' + i + ')'); + } + // TODO: This is wrong if there are multiple operations with the same + // identifier. + // 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"]) { + 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 < 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 + + "(" + member.arguments.map(function(m) { return m.idlType.idlType; }) + + ") on " + desc + " with too few arguments must throw TypeError"); + } + } +}; + +//@} +IdlInterface.prototype.has_stringifier = function() +//@{ +{ + if (this.members.some(function(member) { return member.stringifier; })) { + return true; + } + if (this.base && + this.array.members[this.base].has_stringifier()) { + return true; + } + return false; +}; + +//@} +IdlInterface.prototype.do_interface_attribute_asserts = function(obj, member) +//@{ +{ + // 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.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:" + 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(desc.get.length, 0, "getter length must be 0"); + + + // TODO: Test calling setter on the interface prototype (should throw + // TypeError in most cases). + 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"); + } +} +//@} + +/// IdlInterfaceMember /// +function IdlInterfaceMember(obj) +//@{ +{ + /** + * obj is an object produced by the WebIDLParser.js "ifMember" production. + * We just forward all properties to this object without modification, + * except for special extAttrs handling. + */ + 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) +//@{ +{ + /** + * type is an object produced by the WebIDLParser.js "type" production. We + * return a JavaScript value that matches the type, if we can figure out + * how. + */ + if (type.nullable) + { + return null; + } + switch (type.idlType) + { + case "any": + case "boolean": + return true; + + case "byte": case "octet": case "short": case "unsigned short": + case "long": case "unsigned long": case "long long": + case "unsigned long long": case "float": case "double": + case "unrestricted float": case "unrestricted double": + return 7; + + case "DOMString": + case "ByteString": + case "USVString": + return "foo"; + + case "object": + return {a: "b"}; + + case "Node": + return document.createTextNode("abc"); + } + return null; +} +//@} + +/// IdlEnum /// +// Used for IdlArray.prototype.assert_type_is +function IdlEnum(obj) +//@{ +{ + /** + * obj is an object produced by the WebIDLParser.js "dictionary" + * production. + */ + + /** Self-explanatory. */ + this.name = obj.name; + + /** An array of values produced by the "enum" production. */ + this.values = obj.values; + +} +//@} + +IdlEnum.prototype = Object.create(IdlObject.prototype); + +/// IdlTypedef /// +// Used for IdlArray.prototype.assert_type_is +function IdlTypedef(obj) +//@{ +{ + /** + * obj is an object produced by the WebIDLParser.js "typedef" + * production. + */ + + /** Self-explanatory. */ + this.name = obj.name; + + /** An array of values produced by the "typedef" production. */ + this.values = obj.values; + +} +//@} + +IdlTypedef.prototype = Object.create(IdlObject.prototype); + +}()); +// vim: set expandtab shiftwidth=4 tabstop=4 foldmarker=@{,@} foldmethod=marker: diff --git a/dom/imptests/importTestsuite.py b/dom/imptests/importTestsuite.py new file mode 100644 index 000000000..c66b28fb3 --- /dev/null +++ b/dom/imptests/importTestsuite.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. + +""" +Imports a test suite from a remote repository. Takes one argument, a file in +the format described in README. +Note: removes both source and destination directory before starting. Do not + use with outstanding changes in either directory. +""" + +from __future__ import print_function, unicode_literals + +import os +import shutil +import subprocess +import sys + +import parseManifest +import writeBuildFiles + +def readManifests(iden, dirs): + def parseManifestFile(iden, path): + pathstr = "hg-%s/%s/MANIFEST" % (iden, path) + subdirs, mochitests, reftests, _, supportfiles = parseManifest.parseManifestFile(pathstr) + return subdirs, mochitests, reftests, supportfiles + + data = [] + for path in dirs: + subdirs, mochitests, reftests, supportfiles = parseManifestFile(iden, path) + data.append({ + "path": path, + "mochitests": mochitests, + "reftests": reftests, + "supportfiles": supportfiles, + }) + data.extend(readManifests(iden, ["%s/%s" % (path, d) for d in subdirs])) + return data + + +def getData(confFile): + """This function parses a file of the form + (hg or git)|URL of remote repository|identifier for the local directory + First directory of tests + ... + Last directory of tests""" + vcs = "" + url = "" + iden = "" + directories = [] + try: + with open(confFile, 'r') as fp: + first = True + for line in fp: + if first: + vcs, url, iden = line.strip().split("|") + first = False + else: + directories.append(line.strip()) + finally: + return vcs, url, iden, directories + + +def makePathInternal(a, b): + if not b: + # Empty directory, i.e., the repository root. + return a + return "%s/%s" % (a, b) + + +def makeSourcePath(a, b): + """Make a path in the source (upstream) directory.""" + return makePathInternal("hg-%s" % a, b) + + +def makeDestPath(a, b): + """Make a path in the destination (mozilla-central) directory, shortening as + appropriate.""" + def shorten(path): + path = path.replace('dom-tree-accessors', 'dta') + path = path.replace('document.getElementsByName', 'doc.gEBN') + path = path.replace('requirements-for-implementations', 'implreq') + path = path.replace('other-elements-attributes-and-apis', 'oeaaa') + return path + + return shorten(makePathInternal(a, b)) + + +def extractReftestFiles(reftests): + """Returns the set of files referenced in the reftests argument""" + files = set() + for line in reftests: + files.update([line[1], line[2]]) + return files + + +def copy(dest, directories): + """Copy mochitests and support files from the external HG directory to their + place in mozilla-central. + """ + print("Copying tests...") + for d in directories: + sourcedir = makeSourcePath(dest, d["path"]) + destdir = makeDestPath(dest, d["path"]) + os.makedirs(destdir) + + reftestfiles = extractReftestFiles(d["reftests"]) + + for mochitest in d["mochitests"]: + shutil.copy("%s/%s" % (sourcedir, mochitest), "%s/test_%s" % (destdir, mochitest)) + for reftest in sorted(reftestfiles): + shutil.copy("%s/%s" % (sourcedir, reftest), "%s/%s" % (destdir, reftest)) + for support in d["supportfiles"]: + shutil.copy("%s/%s" % (sourcedir, support), "%s/%s" % (destdir, support)) + +def printBuildFiles(dest, directories): + """Create a mochitest.ini that all the contains tests we import. + """ + print("Creating manifest...") + all_mochitests = set() + all_support = set() + + for d in directories: + path = makeDestPath(dest, d["path"]) + + all_mochitests |= set('%s/test_%s' % (d['path'], mochitest) + for mochitest in d['mochitests']) + all_support |= set('%s/%s' % (d['path'], p) for p in d['supportfiles']) + + if d["reftests"]: + with open(path + "/reftest.list", "w") as fh: + result = writeBuildFiles.substReftestList("importTestsuite.py", + d["reftests"]) + fh.write(result) + + manifest_path = dest + '/mochitest.ini' + with open(manifest_path, 'w') as fh: + result = writeBuildFiles.substManifest('importTestsuite.py', + all_mochitests, all_support) + fh.write(result) + subprocess.check_call(["hg", "add", manifest_path]) + +def hgadd(dest, directories): + """Inform hg of the files in |directories|.""" + print("hg addremoving...") + for d in directories: + subprocess.check_call(["hg", "addremove", makeDestPath(dest, d)]) + +def removeAndCloneRepo(vcs, url, dest): + """Replaces the repo at dest by a fresh clone from url using vcs""" + assert vcs in ('hg', 'git') + + print("Removing %s..." % dest) + subprocess.check_call(["rm", "-rf", dest]) + + print("Cloning %s to %s with %s..." % (url, dest, vcs)) + subprocess.check_call([vcs, "clone", url, dest]) + +def importRepo(confFile): + try: + vcs, url, iden, directories = getData(confFile) + dest = iden + hgdest = "hg-%s" % iden + + print("Removing %s..." % dest) + subprocess.check_call(["rm", "-rf", dest]) + + removeAndCloneRepo(vcs, url, hgdest) + + data = readManifests(iden, directories) + print("Going to import %s..." % [d["path"] for d in data]) + + copy(dest, data) + printBuildFiles(dest, data) + hgadd(dest, directories) + print("Removing %s again..." % hgdest) + subprocess.check_call(["rm", "-rf", hgdest]) + except subprocess.CalledProcessError as e: + print(e.returncode) + finally: + print("Done") + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Need one argument.") + else: + importRepo(sys.argv[1]) + diff --git a/dom/imptests/moz.build b/dom/imptests/moz.build new file mode 100644 index 000000000..595e58ce7 --- /dev/null +++ b/dom/imptests/moz.build @@ -0,0 +1,23 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MOCHITEST_MANIFESTS += [ + 'html/mochitest.ini', + 'webapps/mochitest.ini', +] + +MOCHITEST_MANIFESTS += [ + 'failures/html/typedarrays/mochitest.ini', + 'failures/webapps/WebStorage/tests/submissions/Infraware/mochitest.ini', +] + +TEST_HARNESS_FILES.testing.mochitest.resources += [ + 'idlharness.js', + 'testharness.css', + 'testharness.js', + 'testharnessreport.js', + 'WebIDLParser.js', +] diff --git a/dom/imptests/parseFailures.py b/dom/imptests/parseFailures.py new file mode 100644 index 000000000..6824b836c --- /dev/null +++ b/dom/imptests/parseFailures.py @@ -0,0 +1,79 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from __future__ import print_function, unicode_literals + +import collections +import json +import os +import sys + +import writeBuildFiles + +def extractLines(fp): + lines = [] + watch = False + for line in fp: + line = line.decode('utf-8') + if line == '@@@ @@@ Failures\n': + watch = True + elif watch: + watch = False + idx = line.index('@@@') + lines.append((line[:idx], line[idx + 3:])) + return lines + +def ensuredir(path): + dir = path[:path.rfind('/')] + if not os.path.exists(dir): + os.makedirs(dir) + +def dumpFailures(lines): + files = [] + for url, objstr in lines: + if objstr == '{}\n': + continue + + # Avoid overly large diffs. + if 'editing/' in url: + sep = ':' + else: + sep = ': ' + + jsonpath = 'failures/' + url + '.json' + files.append(jsonpath) + ensuredir(jsonpath) + obj = json.loads(objstr, object_pairs_hook=collections.OrderedDict) + formattedobjstr = json.dumps(obj, indent=2, separators=(',', sep)) + '\n' + formattedobj = formattedobjstr.encode('utf-8') + fp = open(jsonpath, 'wb') + fp.write(formattedobj) + fp.close() + return files + +def writeFiles(files): + pathmap = {} + for path in files: + dirp, leaf = path.rsplit('/', 1) + pathmap.setdefault(dirp, []).append(leaf) + + for k, v in pathmap.items(): + with open(k + '/mochitest.ini', 'w') as fh: + result = writeBuildFiles.substManifest('parseFailures.py', v, []) + fh.write(result) + + +def main(logPath): + fp = open(logPath, 'rb') + lines = extractLines(fp) + fp.close() + + files = dumpFailures(lines) + writeFiles(files) + +if __name__ == '__main__': + if len(sys.argv) < 2: + print("Please pass the path to the logfile from which failures should be extracted.") + main(sys.argv[1]) + diff --git a/dom/imptests/parseManifest.py b/dom/imptests/parseManifest.py new file mode 100644 index 000000000..94412122d --- /dev/null +++ b/dom/imptests/parseManifest.py @@ -0,0 +1,69 @@ +# Copyright (C) 2011-2013 Ms2ger +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + + +def parseManifest(fd): + def parseReftestLine(chunks): + assert len(chunks) % 2 == 0 + reftests = [] + for i in range(2, len(chunks), 2): + if not chunks[i] in ["==", "!="]: + raise Exception("Misformatted reftest line " + line) + reftests.append([chunks[i], chunks[1], chunks[i + 1]]) + return reftests + + dirs = [] + autotests = [] + reftests = [] + othertests = [] + supportfiles = [] + for fullline in fd: + line = fullline.strip() + if not line: + continue + + chunks = line.split(" ") + + if chunks[0] == "MANIFEST": + raise Exception("MANIFEST listed on line " + line) + + if chunks[0] == "dir": + dirs.append(chunks[1]) + elif chunks[0] == "support" and chunks[1] == "dir": + dirs.append(chunks[1]) + elif chunks[0] == "ref": + if len(chunks) % 2: + raise Exception("Missing chunk in line " + line) + reftests.extend(parseReftestLine(chunks)) + elif chunks[0] == "support": + supportfiles.append(chunks[1]) + elif chunks[0] in ["manual", "parser", "http"]: + othertests.append(chunks[1]) + else: + # automated + autotests.append(chunks[0]) + return dirs, autotests, reftests, othertests, supportfiles + + +def parseManifestFile(path): + fp = open(path) + dirs, autotests, reftests, othertests, supportfiles = parseManifest(fp) + fp.close() + return dirs, autotests, reftests, othertests, supportfiles diff --git a/dom/imptests/testharness.css b/dom/imptests/testharness.css new file mode 100644 index 000000000..e2ed5a043 --- /dev/null +++ b/dom/imptests/testharness.css @@ -0,0 +1,102 @@ +html { + font-family:DejaVu Sans, Bitstream Vera Sans, Arial, Sans; +} + +#log .warning, +#log .warning a { + color: black; + background: yellow; +} + +#log .error, +#log .error a { + color: white; + background: red; +} + +section#summary { + margin-bottom:1em; +} + +table#results { + border-collapse:collapse; + table-layout:fixed; + width:100%; +} + +table#results th:first-child, +table#results td:first-child { + width:4em; +} + +table#results th:last-child, +table#results td:last-child { + width:50%; +} + +table#results.assertions th:last-child, +table#results.assertions td:last-child { + width:35%; +} + +table#results th { + padding:0; + padding-bottom:0.5em; + border-bottom:medium solid black; +} + +table#results td { + padding:1em; + padding-bottom:0.5em; + border-bottom:thin solid black; +} + +tr.pass > td:first-child { + color:green; +} + +tr.fail > td:first-child { + color:red; +} + +tr.timeout > td:first-child { + color:red; +} + +tr.notrun > td:first-child { + color:blue; +} + +.pass > td:first-child, .fail > td:first-child, .timeout > td:first-child, .notrun > td:first-child { + font-variant:small-caps; +} + +table#results span { + display:block; +} + +table#results span.expected { + font-family:DejaVu Sans Mono, Bitstream Vera Sans Mono, Monospace; + white-space:pre; +} + +table#results span.actual { + font-family:DejaVu Sans Mono, Bitstream Vera Sans Mono, Monospace; + white-space:pre; +} + +span.ok { + color:green; +} + +tr.error { + color:red; +} + +span.timeout { + color:red; +} + +span.ok, span.timeout, span.error { + font-variant:small-caps; +}
\ No newline at end of file diff --git a/dom/imptests/testharness.js b/dom/imptests/testharness.js new file mode 100644 index 000000000..f4c66aae6 --- /dev/null +++ b/dom/imptests/testharness.js @@ -0,0 +1,2657 @@ +/*global self*/ +/*jshint latedef: nofunc*/ +/* +Distributed under both the W3C Test Suite License [1] and the W3C +3-clause BSD License [2]. To contribute to a W3C Test Suite, see the +policies and contribution forms [3]. + +[1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license +[2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license +[3] http://www.w3.org/2004/10/27-testcases +*/ + +/* Documentation is in docs/api.md */ + +(function () +{ + 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, + 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: + * + * interface TestEnvironment { + * // Invoked after the global 'tests' object has been created and it's + * // safe to call add_*_callback() to register event handlers. + * void on_tests_ready(); + * + * // Invoked after setup() has been called to notify the test environment + * // of changes to the test harness properties. + * void on_new_harness_properties(object properties); + * + * // Should return a new unique default test name. + * DOMString next_default_test_name(); + * + * // Should return the test harness timeout duration in milliseconds. + * float test_timeout(); + * + * // Should return the global scope object. + * object global_scope(); + * }; + */ + + /* + * A test environment with a DOM. The global object is 'window'. By default + * test results are displayed in a table. Any parent windows receive + * callbacks or messages via postMessage() when test events occur. See + * 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, same_origin) { + if (same_origin) { + try { + 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, "*"); + } + }); + }; + + WindowTestEnvironment.prototype._forEach_windows = function(callback) { + // Iterate of the the windows [self ... top, opener]. The callback is passed + // two objects, the first one is the windows object itself, the second one + // is a boolean indicating whether or not its on the same origin as the + // current window. + var cache = this.window_cache; + if (!cache) { + cache = [[self, true]]; + var w = self; + var i = 0; + var so; + var origins = location.ancestorOrigins; + while (w != w.parent) { + w = w.parent; + // In WebKit, calls to parent windows' properties that aren't on the same + // origin cause an error message to be displayed in the error console but + // don't throw an exception. This is a deviation from the current HTML5 + // spec. See: https://bugs.webkit.org/show_bug.cgi?id=43504 + // The problem with WebKit's behavior is that it pollutes the error console + // with error messages that can't be caught. + // + // This issue can be mitigated by relying on the (for now) proprietary + // `location.ancestorOrigins` property which returns an ordered list of + // the origins of enclosing windows. See: + // http://trac.webkit.org/changeset/113945. + if (origins) { + so = (location.origin == origins[i]); + } else { + so = is_same_origin(w); + } + cache.push([w, so]); + i++; + } + w = window.opener; + if (w) { + // window.opener isn't included in the `location.ancestorOrigins` prop. + // We'll just have to deal with a simple check and an error msg on WebKit + // browsers in this case. + cache.push([w, is_same_origin(w)]); + } + this.window_cache = cache; + } + + forEach(cache, + function(a) { + callback.apply(null, a); + }); + }; + + 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); + }); + + add_test_state_callback(function(test) { + this_obj.output_handler.show_status(); + }); + + add_result_callback(function (test) { + this_obj.output_handler.show_status(); + }); + + add_completion_callback(function (tests, harness_status) { + this_obj.output_handler.show_results(tests, harness_status); + }); + 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"); + for (var i = 0; i < metas.length; i++) { + if (metas[i].name == "timeout") { + if (metas[i].content == "long") { + return settings.harness_timeout.long; + } + break; + } + } + return settings.harness_timeout.normal; + }; + + WindowTestEnvironment.prototype.global_scope = function() { + return window; + }; + + /* + * Base TestEnvironment implementation for a generic web worker. + * + * Workers accumulate test results. One or more clients can connect and + * retrieve results from a worker at any time. + * + * WorkerTestEnvironment supports communicating with a client via a + * MessagePort. The mechanism for determining the appropriate MessagePort + * for communicating with a client depends on the type of worker and is + * implemented by the various specializations of WorkerTestEnvironment + * below. + * + * A client document using testharness can use fetch_tests_from_worker() to + * retrieve results from a worker. See apisample16.html. + */ + function WorkerTestEnvironment() { + this.name_counter = 0; + this.all_loaded = true; + this.message_list = []; + this.message_ports = []; + } + + WorkerTestEnvironment.prototype._dispatch = function(message) { + this.message_list.push(message); + for (var i = 0; i < this.message_ports.length; ++i) + { + this.message_ports[i].postMessage(message); + } + }; + + // The only requirement is that port has a postMessage() method. It doesn't + // have to be an instance of a MessagePort, and often isn't. + WorkerTestEnvironment.prototype._add_message_port = function(port) { + this.message_ports.push(port); + for (var i = 0; i < this.message_list.length; ++i) + { + port.postMessage(this.message_list[i]); + } + }; + + WorkerTestEnvironment.prototype.next_default_test_name = function() { + var suffix = this.name_counter > 0 ? " " + this.name_counter : ""; + this.name_counter++; + return "Untitled" + suffix; + }; + + WorkerTestEnvironment.prototype.on_new_harness_properties = function() {}; + + WorkerTestEnvironment.prototype.on_tests_ready = function() { + var this_obj = this; + add_start_callback( + function(properties) { + this_obj._dispatch({ + type: "start", + properties: properties, + }); + }); + add_test_state_callback( + function(test) { + this_obj._dispatch({ + type: "test_state", + test: test.structured_clone() + }); + }); + add_result_callback( + function(test) { + this_obj._dispatch({ + type: "result", + test: test.structured_clone() + }); + }); + add_completion_callback( + function(tests, harness_status) { + this_obj._dispatch({ + type: "complete", + tests: map(tests, + function(test) { + return test.structured_clone(); + }), + status: harness_status.structured_clone() + }); + }); + }; + + WorkerTestEnvironment.prototype.add_on_loaded_callback = function() {}; + + WorkerTestEnvironment.prototype.test_timeout = function() { + // Tests running in a worker don't have a default timeout. I.e. all + // worker tests behave as if settings.explicit_timeout is true. + return null; + }; + + WorkerTestEnvironment.prototype.global_scope = function() { + return self; + }; + + /* + * Dedicated web workers. + * https://html.spec.whatwg.org/multipage/workers.html#dedicatedworkerglobalscope + * + * This class is used as the test_environment when testharness is running + * inside a dedicated worker. + */ + function DedicatedWorkerTestEnvironment() { + WorkerTestEnvironment.call(this); + // self is an instance of DedicatedWorkerGlobalScope which exposes + // a postMessage() method for communicating via the message channel + // established when the worker is created. + this._add_message_port(self); + } + DedicatedWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironment.prototype); + + DedicatedWorkerTestEnvironment.prototype.on_tests_ready = function() { + WorkerTestEnvironment.prototype.on_tests_ready.call(this); + // In the absence of an onload notification, we a require dedicated + // workers to explicitly signal when the tests are done. + tests.wait_for_finish = true; + }; + + /* + * Shared web workers. + * https://html.spec.whatwg.org/multipage/workers.html#sharedworkerglobalscope + * + * This class is used as the test_environment when testharness is running + * inside a shared web worker. + */ + function SharedWorkerTestEnvironment() { + WorkerTestEnvironment.call(this); + var this_obj = this; + // Shared workers receive message ports via the 'onconnect' event for + // each connection. + self.addEventListener("connect", + function(message_event) { + this_obj._add_message_port(message_event.source); + }); + } + SharedWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironment.prototype); + + SharedWorkerTestEnvironment.prototype.on_tests_ready = function() { + WorkerTestEnvironment.prototype.on_tests_ready.call(this); + // In the absence of an onload notification, we a require shared + // workers to explicitly signal when the tests are done. + tests.wait_for_finish = true; + }; + + /* + * Service workers. + * http://www.w3.org/TR/service-workers/ + * + * This class is used as the test_environment when testharness is running + * inside a service worker. + */ + 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") { + 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. + on_event(self, "install", + function(event) { + this_obj.all_loaded = true; + if (this_obj.on_loaded_callback) { + this_obj.on_loaded_callback(); + } + }); + } + ServiceWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironment.prototype); + + ServiceWorkerTestEnvironment.prototype.add_on_loaded_callback = function(callback) { + if (this.all_loaded) { + callback(); + } else { + this.on_loaded_callback = callback; + } + }; + + function create_test_environment() { + if ('document' in self) { + return new WindowTestEnvironment(); + } + if ('DedicatedWorkerGlobalScope' in self && + self instanceof DedicatedWorkerGlobalScope) { + return new DedicatedWorkerTestEnvironment(); + } + if ('SharedWorkerGlobalScope' in self && + self instanceof SharedWorkerGlobalScope) { + return new SharedWorkerTestEnvironment(); + } + if ('ServiceWorkerGlobalScope' in self && + self instanceof ServiceWorkerGlobalScope) { + return new ServiceWorkerTestEnvironment(); + } + throw new Error("Unsupported test environment"); + } + + var test_environment = create_test_environment(); + + function is_shared_worker(worker) { + return 'SharedWorker' in self && worker instanceof SharedWorker; + } + + function is_service_worker(worker) { + return 'ServiceWorker' in self && worker instanceof ServiceWorker; + } + + /* + * API functions + */ + + function test(func, name, properties) + { + var test_name = name ? name : test_environment.next_default_test_name(); + properties = properties ? properties : {}; + var test_obj = new Test(test_name, properties); + test_obj.step(func, test_obj, test_obj); + if (test_obj.phase === test_obj.phases.STARTED) { + test_obj.done(); + } + } + + function async_test(func, name, properties) + { + if (typeof func !== "function") { + properties = name; + name = func; + func = null; + } + var test_name = name ? name : test_environment.next_default_test_name(); + properties = properties ? properties : {}; + var test_obj = new Test(test_name, properties); + if (func) { + test_obj.step(func, test_obj, test_obj); + } + return test_obj; + } + + function promise_test(func, name, properties) { + var test = async_test(name, properties); + // If there is no promise tests queue make one. + test.step(function() { + if (!tests.promise_tests) { + tests.promise_tests = Promise.resolve(); + } + }); + tests.promise_tests = tests.promise_tests.then(function() { + return Promise.resolve(test.step(func, test, test)) + .then( + function() { + test.done(); + }) + .catch(test.step_func( + function(value) { + 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') { + eventTypes = [eventTypes]; + } + + var waitingFor = null; + + var eventHandler = test.step_func(function(evt) { + assert_true(!!waitingFor, + 'Not expecting event, but got ' + evt.type + ' event'); + assert_equals(evt.type, waitingFor.types[0], + 'Expected ' + waitingFor.types[0] + ' event, but got ' + + evt.type + ' event instead'); + if (waitingFor.types.length > 1) { + // Pop first event from array + waitingFor.types.shift(); + return; + } + // We need to null out waitingFor before calling the resolve function + // since the Promise's resolve handlers may call wait_for() which will + // need to set waitingFor. + var resolveFunc = waitingFor.resolve; + waitingFor = null; + resolveFunc(evt); + }); + + for (var i = 0; i < eventTypes.length; i++) { + watchedNode.addEventListener(eventTypes[i], eventHandler); + } + + /** + * Returns a Promise that will resolve after the specified event or + * series of events has occured. + */ + this.wait_for = function(types) { + if (waitingFor) { + return Promise.reject('Already waiting for an event or events'); + } + if (typeof types == 'string') { + types = [types]; + } + return new Promise(function(resolve, reject) { + waitingFor = { + types: types, + resolve: resolve, + reject: reject + }; + }); + }; + + function stop_watching() { + for (var i = 0; i < eventTypes.length; i++) { + watchedNode.removeEventListener(eventTypes[i], eventHandler); + } + }; + + test.add_cleanup(stop_watching); + + return this; + } + expose(EventWatcher, 'EventWatcher'); + + function setup(func_or_properties, maybe_properties) + { + var func = null; + var properties = {}; + if (arguments.length === 2) { + func = func_or_properties; + properties = maybe_properties; + } else if (func_or_properties instanceof Function) { + func = func_or_properties; + } else { + properties = func_or_properties; + } + tests.setup(func, properties); + test_environment.on_new_harness_properties(properties); + } + + function done() { + if (tests.tests.length === 0) { + tests.set_file_is_test(); + } + if (tests.file_is_test) { + tests.tests[0].done(); + } + tests.end_wait(); + } + + function generate_tests(func, args, properties) { + forEach(args, function(x, i) + { + var name = x[0]; + test(function() + { + func.apply(this, x.slice(1)); + }, + name, + Array.isArray(properties) ? properties[i] : properties); + }); + } + + function on_event(object, event, callback) + { + object.addEventListener(event, callback, false); + } + + function step_timeout(f, t) { + var outer_this = this; + var args = Array.prototype.slice.call(arguments, 2); + return setTimeout(function() { + f.apply(outer_this, args); + }, t * tests.timeout_multiplier); + } + + 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'); + expose(step_timeout, 'step_timeout'); + + /* + * Return a string truncated to the given length, with ... added at the end + * if it was longer. + */ + function truncate(s, len) + { + if (s.length > len) { + return s.substring(0, len - 3) + "..."; + } + return s; + } + + /* + * Return true if object is probably a Node object. + */ + function is_node(object) + { + // I use duck-typing instead of instanceof, because + // instanceof doesn't work if the node is from another window (like an + // iframe's contentWindow): + // http://www.w3.org/Bugs/Public/show_bug.cgi?id=12295 + if ("nodeType" in object && + "nodeName" in object && + "nodeValue" in object && + "childNodes" in object) { + try { + object.nodeType; + } catch (e) { + // The object is probably Node.prototype or another prototype + // object that inherits from it, and not a Node instance. + return false; + } + return true; + } + return false; + } + + /* + * Convert a value to a nice, human-readable string + */ + function format_value(val, seen) + { + if (!seen) { + seen = []; + } + if (typeof val === "object" && val !== null) { + if (seen.indexOf(val) >= 0) { + return "[...]"; + } + seen.push(val); + } + if (Array.isArray(val)) { + return "[" + val.map(function(x) {return format_value(x, seen);}).join(", ") + "]"; + } + + switch (typeof val) { + case "string": + val = val.replace("\\", "\\\\"); + for (var i = 0; i < 32; i++) { + var replace = "\\"; + switch (i) { + case 0: replace += "0"; break; + case 1: replace += "x01"; break; + case 2: replace += "x02"; break; + case 3: replace += "x03"; break; + case 4: replace += "x04"; break; + case 5: replace += "x05"; break; + case 6: replace += "x06"; break; + case 7: replace += "x07"; break; + case 8: replace += "b"; break; + case 9: replace += "t"; break; + case 10: replace += "n"; break; + case 11: replace += "v"; break; + case 12: replace += "f"; break; + case 13: replace += "r"; break; + case 14: replace += "x0e"; break; + case 15: replace += "x0f"; break; + case 16: replace += "x10"; break; + case 17: replace += "x11"; break; + case 18: replace += "x12"; break; + case 19: replace += "x13"; break; + case 20: replace += "x14"; break; + case 21: replace += "x15"; break; + case 22: replace += "x16"; break; + case 23: replace += "x17"; break; + case 24: replace += "x18"; break; + case 25: replace += "x19"; break; + case 26: replace += "x1a"; break; + case 27: replace += "x1b"; break; + case 28: replace += "x1c"; break; + case 29: replace += "x1d"; break; + case 30: replace += "x1e"; break; + case 31: replace += "x1f"; break; + } + val = val.replace(RegExp(String.fromCharCode(i), "g"), replace); + } + return '"' + val.replace(/"/g, '\\"') + '"'; + case "boolean": + case "undefined": + return String(val); + case "number": + // In JavaScript, -0 === 0 and String(-0) == "0", so we have to + // special-case. + if (val === -0 && 1/val === -Infinity) { + return "-0"; + } + return String(val); + case "object": + if (val === null) { + return "null"; + } + + // Special-case Node objects, since those come up a lot in my tests. I + // ignore namespaces. + if (is_node(val)) { + switch (val.nodeType) { + case Node.ELEMENT_NODE: + var ret = "<" + val.localName; + for (var i = 0; i < val.attributes.length; i++) { + ret += " " + val.attributes[i].name + '="' + val.attributes[i].value + '"'; + } + ret += ">" + val.innerHTML + "</" + val.localName + ">"; + return "Element node " + truncate(ret, 60); + case Node.TEXT_NODE: + return 'Text node "' + truncate(val.data, 60) + '"'; + case Node.PROCESSING_INSTRUCTION_NODE: + return "ProcessingInstruction node with target " + format_value(truncate(val.target, 60)) + " and data " + format_value(truncate(val.data, 60)); + case Node.COMMENT_NODE: + return "Comment node <!--" + truncate(val.data, 60) + "-->"; + case Node.DOCUMENT_NODE: + return "Document node with " + val.childNodes.length + (val.childNodes.length == 1 ? " child" : " children"); + case Node.DOCUMENT_TYPE_NODE: + return "DocumentType node"; + case Node.DOCUMENT_FRAGMENT_NODE: + return "DocumentFragment node with " + val.childNodes.length + (val.childNodes.length == 1 ? " child" : " children"); + default: + return "Node object of unknown type"; + } + } + + /* falls through */ + default: + return typeof val + ' "' + truncate(String(val), 60) + '"'; + } + } + expose(format_value, "format_value"); + + /* + * Assertions + */ + + function assert_true(actual, description) + { + assert(actual === true, "assert_true", description, + "expected true got ${actual}", {actual:actual}); + } + expose(assert_true, "assert_true"); + + function assert_false(actual, description) + { + assert(actual === false, "assert_false", description, + "expected false got ${actual}", {actual:actual}); + } + expose(assert_false, "assert_false"); + + function same_value(x, y) { + if (y !== y) { + //NaN case + return x !== x; + } + if (x === 0 && y === 0) { + //Distinguish +0 and -0 + return 1/x === 1/y; + } + return x === y; + } + + function assert_equals(actual, expected, description) + { + /* + * Test if two primitives are equal or two objects + * are the same object + */ + if (typeof actual != typeof expected) { + assert(false, "assert_equals", description, + "expected (" + typeof expected + ") ${expected} but got (" + typeof actual + ") ${actual}", + {expected:expected, actual:actual}); + return; + } + assert(same_value(actual, expected), "assert_equals", description, + "expected ${expected} but got ${actual}", + {expected:expected, actual:actual}); + } + expose(assert_equals, "assert_equals"); + + function assert_not_equals(actual, expected, description) + { + /* + * Test if two primitives are unequal or two objects + * are different objects + */ + assert(!same_value(actual, expected), "assert_not_equals", description, + "got disallowed value ${actual}", + {actual:actual}); + } + expose(assert_not_equals, "assert_not_equals"); + + function assert_in_array(actual, expected, description) + { + assert(expected.indexOf(actual) != -1, "assert_in_array", description, + "value ${actual} not in array ${expected}", + {actual:actual, expected:expected}); + } + expose(assert_in_array, "assert_in_array"); + + function assert_object_equals(actual, expected, description) + { + //This needs to be improved a great deal + function check_equal(actual, expected, stack) + { + stack.push(actual); + + var p; + for (p in actual) { + assert(expected.hasOwnProperty(p), "assert_object_equals", description, + "unexpected property ${p}", {p:p}); + + if (typeof actual[p] === "object" && actual[p] !== null) { + if (stack.indexOf(actual[p]) === -1) { + check_equal(actual[p], expected[p], stack); + } + } else { + assert(same_value(actual[p], expected[p]), "assert_object_equals", description, + "property ${p} expected ${expected} got ${actual}", + {p:p, expected:expected, actual:actual}); + } + } + for (p in expected) { + assert(actual.hasOwnProperty(p), + "assert_object_equals", description, + "expected property ${p} missing", {p:p}); + } + stack.pop(); + } + check_equal(actual, expected, []); + } + expose(assert_object_equals, "assert_object_equals"); + + function assert_array_equals(actual, expected, description) + { + 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}", + {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]}); + } + } + expose(assert_array_equals, "assert_array_equals"); + + function assert_approx_equals(actual, expected, epsilon, description) + { + /* + * Test if two primitive numbers are equal withing +/- epsilon + */ + assert(typeof actual === "number", + "assert_approx_equals", description, + "expected a number but got a ${type_actual}", + {type_actual:typeof actual}); + + assert(Math.abs(actual - expected) <= epsilon, + "assert_approx_equals", description, + "expected ${expected} +/- ${epsilon} but got ${actual}", + {expected:expected, actual:actual, epsilon:epsilon}); + } + expose(assert_approx_equals, "assert_approx_equals"); + + function assert_less_than(actual, expected, description) + { + /* + * Test if a primitive number is less than another + */ + assert(typeof actual === "number", + "assert_less_than", description, + "expected a number but got a ${type_actual}", + {type_actual:typeof actual}); + + assert(actual < expected, + "assert_less_than", description, + "expected a number less than ${expected} but got ${actual}", + {expected:expected, actual:actual}); + } + expose(assert_less_than, "assert_less_than"); + + function assert_greater_than(actual, expected, description) + { + /* + * Test if a primitive number is greater than another + */ + assert(typeof actual === "number", + "assert_greater_than", description, + "expected a number but got a ${type_actual}", + {type_actual:typeof actual}); + + assert(actual > expected, + "assert_greater_than", description, + "expected a number greater than ${expected} but got ${actual}", + {expected:expected, actual:actual}); + } + expose(assert_greater_than, "assert_greater_than"); + + function assert_between_exclusive(actual, lower, upper, description) + { + /* + * Test if a primitive number is between two others + */ + assert(typeof actual === "number", + "assert_between_exclusive", description, + "expected a number but got a ${type_actual}", + {type_actual:typeof actual}); + + assert(actual > lower && actual < upper, + "assert_between_exclusive", description, + "expected a number greater than ${lower} " + + "and less than ${upper} but got ${actual}", + {lower:lower, upper:upper, actual:actual}); + } + expose(assert_between_exclusive, "assert_between_exclusive"); + + function assert_less_than_equal(actual, expected, description) + { + /* + * 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_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) + { + /* + * Test if a primitive number is greater than or equal to another + */ + assert(typeof actual === "number", + "assert_greater_than_equal", description, + "expected a number but got a ${type_actual}", + {type_actual:typeof actual}); + + assert(actual >= expected, + "assert_greater_than_equal", description, + "expected a number greater than or equal to ${expected} but got ${actual}", + {expected:expected, actual:actual}); + } + expose(assert_greater_than_equal, "assert_greater_than_equal"); + + function assert_between_inclusive(actual, lower, upper, description) + { + /* + * Test if a primitive number is between to two others or equal to either of them + */ + assert(typeof actual === "number", + "assert_between_inclusive", description, + "expected a number but got a ${type_actual}", + {type_actual:typeof actual}); + + assert(actual >= lower && actual <= upper, + "assert_between_inclusive", description, + "expected a number greater than or equal to ${lower} " + + "and less than or equal to ${upper} but got ${actual}", + {lower:lower, upper:upper, actual:actual}); + } + expose(assert_between_inclusive, "assert_between_inclusive"); + + function assert_regexp_match(actual, expected, description) { + /* + * Test if a string (actual) matches a regexp (expected) + */ + assert(expected.test(actual), + "assert_regexp_match", description, + "expected ${expected} but got ${actual}", + {expected:expected, actual:actual}); + } + expose(assert_regexp_match, "assert_regexp_match"); + + function assert_class_string(object, class_string, description) { + assert_equals({}.toString.call(object), "[object " + class_string + "]", + description); + } + expose(assert_class_string, "assert_class_string"); + + + function _assert_own_property(name) { + return function(object, property_name, description) + { + assert(object.hasOwnProperty(property_name), + name, description, + "expected property ${p} missing", {p:property_name}); + }; + } + expose(_assert_own_property("assert_exists"), "assert_exists"); + expose(_assert_own_property("assert_own_property"), "assert_own_property"); + + function assert_not_exists(object, property_name, description) + { + assert(!object.hasOwnProperty(property_name), + "assert_not_exists", description, + "unexpected property ${p} found", {p:property_name}); + } + expose(assert_not_exists, "assert_not_exists"); + + function _assert_inherits(name) { + return function (object, property_name, description) + { + assert(typeof object === "object", + name, description, + "provided value is not an object"); + + assert("hasOwnProperty" in object, + name, description, + "provided value is an object but has no hasOwnProperty method"); + + assert(!object.hasOwnProperty(property_name), + name, description, + "property ${p} found on object expected in prototype chain", + {p:property_name}); + + assert(property_name in object, + name, description, + "property ${p} not found in prototype chain", + {p:property_name}); + }; + } + expose(_assert_inherits("assert_inherits"), "assert_inherits"); + expose(_assert_inherits("assert_idl_attribute"), "assert_idl_attribute"); + + function assert_readonly(object, property_name, description) + { + var initial_value = object[property_name]; + try { + //Note that this can have side effects in the case where + //the property has PutForwards + object[property_name] = initial_value + "a"; //XXX use some other value here? + assert(same_value(object[property_name], initial_value), + "assert_readonly", description, + "changing property ${p} succeeded", + {p:property_name}); + } finally { + object[property_name] = initial_value; + } + } + expose(assert_readonly, "assert_readonly"); + + function assert_throws(code, func, description) + { + try { + func.call(this); + assert(false, "assert_throws", description, + "${func} did not throw", {func:func}); + } catch (e) { + if (e instanceof AssertionError) { + throw e; + } + if (code === null) { + return; + } + if (typeof code === "object") { + assert(typeof e == "object" && "name" in e && e.name == code.name, + "assert_throws", description, + "${func} threw ${actual} (${actual_name}) expected ${expected} (${expected_name})", + {func:func, actual:e, actual_name:e.name, + expected:code, + expected_name:code.name}); + return; + } + + var code_name_map = { + INDEX_SIZE_ERR: 'IndexSizeError', + HIERARCHY_REQUEST_ERR: 'HierarchyRequestError', + WRONG_DOCUMENT_ERR: 'WrongDocumentError', + INVALID_CHARACTER_ERR: 'InvalidCharacterError', + NO_MODIFICATION_ALLOWED_ERR: 'NoModificationAllowedError', + NOT_FOUND_ERR: 'NotFoundError', + NOT_SUPPORTED_ERR: 'NotSupportedError', + INVALID_STATE_ERR: 'InvalidStateError', + SYNTAX_ERR: 'SyntaxError', + INVALID_MODIFICATION_ERR: 'InvalidModificationError', + NAMESPACE_ERR: 'NamespaceError', + INVALID_ACCESS_ERR: 'InvalidAccessError', + TYPE_MISMATCH_ERR: 'TypeMismatchError', + SECURITY_ERR: 'SecurityError', + NETWORK_ERR: 'NetworkError', + ABORT_ERR: 'AbortError', + URL_MISMATCH_ERR: 'URLMismatchError', + QUOTA_EXCEEDED_ERR: 'QuotaExceededError', + TIMEOUT_ERR: 'TimeoutError', + INVALID_NODE_TYPE_ERR: 'InvalidNodeTypeError', + DATA_CLONE_ERR: 'DataCloneError' + }; + + var name = code in code_name_map ? code_name_map[code] : code; + + var name_code_map = { + IndexSizeError: 1, + HierarchyRequestError: 3, + WrongDocumentError: 4, + InvalidCharacterError: 5, + NoModificationAllowedError: 7, + NotFoundError: 8, + NotSupportedError: 9, + InvalidStateError: 11, + SyntaxError: 12, + InvalidModificationError: 13, + NamespaceError: 14, + InvalidAccessError: 15, + TypeMismatchError: 17, + SecurityError: 18, + 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, + 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 || + (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? + + assert(typeof e == "object", + "assert_throws", description, + "${func} threw ${e} with type ${type}, not an object", + {func:func, e:e, type:typeof e}); + + for (var prop in required_props) { + assert(typeof e == "object" && prop in e && e[prop] == required_props[prop], + "assert_throws", description, + "${func} threw ${e} that is not a DOMException " + code + ": property ${prop} is equal to ${actual}, expected ${expected}", + {func:func, e:e, prop:prop, actual:e[prop], expected:required_props[prop]}); + } + } + } + expose(assert_throws, "assert_throws"); + + function assert_unreached(description) { + assert(false, "assert_unreached", description, + "Reached unreachable code"); + } + expose(assert_unreached, "assert_unreached"); + + function assert_any(assert_func, actual, expected_array) + { + var args = [].slice.call(arguments, 3); + var errors = []; + var passed = false; + forEach(expected_array, + function(expected) + { + try { + assert_func.apply(this, [actual, expected].concat(args)); + passed = true; + } catch (e) { + errors.push(e.message); + } + }); + if (!passed) { + throw new AssertionError(errors.join("\n\n")); + } + } + expose(assert_any, "assert_any"); + + function Test(name, properties) + { + if (tests.file_is_test && tests.tests.length) { + throw new Error("Tried to create a test with file_is_test"); + } + this.name = name; + + this.phase = this.phases.INITIAL; + + this.status = this.NOTRUN; + this.timeout_id = null; + this.index = null; + + this.properties = properties; + 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); + } + + Test.statuses = { + PASS:0, + FAIL:1, + TIMEOUT:2, + NOTRUN:3 + }; + + Test.prototype = merge({}, Test.statuses); + + Test.prototype.phases = { + INITIAL:0, + STARTED:1, + HAS_RESULT:2, + COMPLETE:3 + }; + + Test.prototype.structured_clone = function() + { + if (!this._structured_clone) { + var msg = this.message; + 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; + } + this.phase = this.phases.STARTED; + //If we don't get a result before the harness times out that will be a test timout + this.set_status(this.TIMEOUT, "Test timed out"); + + tests.started = true; + tests.notify_test_state(this); + + if (this.timeout_id === null) { + this.set_timeout(); + } + + this.steps.push(func); + + if (arguments.length === 1) { + this_obj = this; + } + + try { + return func.apply(this_obj, Array.prototype.slice.call(arguments, 2)); + } catch (e) { + if (this.phase >= this.phases.HAS_RESULT) { + return; + } + 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; + + if (arguments.length === 1) { + this_obj = test_this; + } + + return function() + { + return test_this.step.apply(test_this, [func, this_obj].concat( + Array.prototype.slice.call(arguments))); + }; + }; + + Test.prototype.step_func_done = function(func, this_obj) + { + var test_this = this; + + if (arguments.length === 1) { + this_obj = test_this; + } + + return function() + { + if (func) { + test_this.step.apply(test_this, [func, this_obj].concat( + Array.prototype.slice.call(arguments))); + } + test_this.done(); + }; + }; + + Test.prototype.unreached_func = function(description) + { + return this.step_func(function() { + assert_unreached(description); + }); + }; + + Test.prototype.step_timeout = function(f, timeout) { + var test_this = this; + var args = Array.prototype.slice.call(arguments, 2); + return setTimeout(this.step_func(function() { + return f.apply(test_this, args); + }, timeout * tests.timeout_multiplier)); + } + + Test.prototype.add_cleanup = function(callback) { + this.cleanup_callbacks.push(callback); + }; + + Test.prototype.force_timeout = function() { + this.set_status(this.TIMEOUT); + this.phase = this.phases.HAS_RESULT; + }; + + Test.prototype.set_timeout = function() + { + if (this.timeout_length !== null) { + var this_obj = this; + this.timeout_id = setTimeout(function() + { + this_obj.timeout(); + }, this.timeout_length); + } + }; + + 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(); + }; + + Test.prototype.done = function() + { + if (this.phase == this.phases.COMPLETE) { + return; + } + + if (this.phase <= this.phases.STARTED) { + this.set_status(this.PASS, null); + } + + this.phase = this.phases.COMPLETE; + + clearTimeout(this.timeout_id); + tests.result(this); + this.cleanup(); + }; + + Test.prototype.cleanup = function() { + forEach(this.cleanup_callbacks, + function(cleanup_callback) { + cleanup_callback(); + }); + }; + + /* + * A RemoteTest object mirrors a Test object on a remote worker. The + * associated RemoteWorker updates the RemoteTest object in response to + * received events. In turn, the RemoteTest object replicates these events + * on the local document. This allows listeners (test result reporting + * etc..) to transparently handle local and remote events. + */ + function RemoteTest(clone) { + var this_obj = this; + Object.keys(clone).forEach( + function(key) { + this_obj[key] = clone[key]; + }); + this.index = null; + 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) { + 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; + } + + /* + * A RemoteWorker listens for test events from a worker. These events are + * then used to construct and maintain RemoteTest objects that mirror the + * tests running on the remote worker. + */ + function RemoteWorker(worker) { + this.running = true; + 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)) { + 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 + // before all the messages are dispatched. + this.worker = worker; + + message_port.onmessage = + function(message) { + if (this_obj.running && (message.data.type in this_obj.message_handlers)) { + this_obj.message_handlers[message.data.type].call(this_obj, message.data); + } + }; + } + + 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, + stack: error.stack + } + }); + error.preventDefault(); + }; + + RemoteWorker.prototype.test_state = function(data) { + var remote_test = this.tests[data.test.index]; + if (!remote_test) { + remote_test = new RemoteTest(data.test); + this.tests[data.test.index] = remote_test; + } + remote_test.update_state_from(data.test); + tests.notify_test_state(remote_test); + }; + + RemoteWorker.prototype.test_done = function(data) { + var remote_test = this.tests[data.test.index]; + remote_test.update_state_from(data.test); + remote_test.done(); + 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(); + } + }; + + RemoteWorker.prototype.message_handlers = { + test_state: RemoteWorker.prototype.test_state, + result: RemoteWorker.prototype.test_done, + complete: RemoteWorker.prototype.worker_done + }; + + /* + * Harness + */ + + function TestsStatus() + { + this.status = null; + this.message = null; + this.stack = null; + } + + TestsStatus.statuses = { + OK:0, + ERROR:1, + TIMEOUT:2 + }; + + TestsStatus.prototype = merge({}, TestsStatus.statuses); + + 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, + stack:this.stack + }, TestsStatus.statuses); + } + return this._structured_clone; + }; + + function Tests() + { + this.tests = []; + this.num_pending = 0; + + this.phases = { + INITIAL:0, + SETUP:1, + HAVE_TESTS:2, + HAVE_RESULTS:3, + COMPLETE:4 + }; + this.phase = this.phases.INITIAL; + + this.properties = {}; + + this.wait_for_finish = false; + this.processing_callbacks = false; + + this.allow_uncaught_exception = false; + + this.file_is_test = false; + + this.timeout_multiplier = 1; + this.timeout_length = test_environment.test_timeout(); + this.timeout_id = null; + + this.start_callbacks = []; + this.test_state_callbacks = []; + this.test_done_callbacks = []; + this.all_done_callbacks = []; + + this.pending_workers = []; + + this.status = new TestsStatus(); + + var this_obj = this; + + test_environment.add_on_loaded_callback(function() { + if (this_obj.all_done()) { + this_obj.complete(); + } + }); + + this.set_timeout(); + } + + Tests.prototype.setup = function(func, properties) + { + if (this.phase >= this.phases.HAVE_RESULTS) { + return; + } + + if (this.phase < this.phases.SETUP) { + this.phase = this.phases.SETUP; + } + + this.properties = properties; + + for (var p in properties) { + if (properties.hasOwnProperty(p)) { + var value = properties[p]; + if (p == "allow_uncaught_exception") { + this.allow_uncaught_exception = value; + } else if (p == "explicit_done" && value) { + this.wait_for_finish = true; + } else if (p == "explicit_timeout" && value) { + this.timeout_length = null; + if (this.timeout_id) + { + clearTimeout(this.timeout_id); + } + } else if (p == "timeout_multiplier") { + this.timeout_multiplier = value; + } + } + } + + 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"); + } + this.wait_for_finish = true; + this.file_is_test = true; + // Create the test, which will add it to the list of tests + async_test(); + }; + + Tests.prototype.set_timeout = function() { + var this_obj = this; + clearTimeout(this.timeout_id); + if (this.timeout_length !== null) { + this.timeout_id = setTimeout(function() { + this_obj.timeout(); + }, this.timeout_length); + } + }; + + Tests.prototype.timeout = function() { + if (this.status.status === null) { + this.status.status = this.status.TIMEOUT; + } + this.complete(); + }; + + Tests.prototype.end_wait = function() + { + this.wait_for_finish = false; + if (this.all_done()) { + this.complete(); + } + }; + + Tests.prototype.push = function(test) + { + if (this.phase < this.phases.HAVE_TESTS) { + this.start(); + } + this.num_pending++; + test.index = this.tests.push(test); + this.notify_test_state(test); + }; + + Tests.prototype.notify_test_state = function(test) { + var this_obj = this; + forEach(this.test_state_callbacks, + function(callback) { + callback(test, this_obj); + }); + }; + + Tests.prototype.all_done = function() { + return (this.tests.length > 0 && test_environment.all_loaded && + this.num_pending === 0 && !this.wait_for_finish && + !this.processing_callbacks && + !this.pending_workers.some(function(w) { return w.running; })); + }; + + Tests.prototype.start = function() { + this.phase = this.phases.HAVE_TESTS; + this.notify_start(); + }; + + Tests.prototype.notify_start = function() { + var this_obj = this; + forEach (this.start_callbacks, + function(callback) + { + callback(this_obj.properties); + }); + }; + + Tests.prototype.result = function(test) + { + if (this.phase > this.phases.HAVE_RESULTS) { + return; + } + this.phase = this.phases.HAVE_RESULTS; + this.num_pending--; + this.notify_result(test); + }; + + Tests.prototype.notify_result = function(test) { + var this_obj = this; + this.processing_callbacks = true; + forEach(this.test_done_callbacks, + function(callback) + { + callback(test, this_obj); + }); + this.processing_callbacks = false; + if (this_obj.all_done()) { + this_obj.complete(); + } + }; + + Tests.prototype.complete = function() { + if (this.phase === this.phases.COMPLETE) { + return; + } + this.phase = this.phases.COMPLETE; + var this_obj = this; + this.tests.forEach( + function(x) + { + if (x.phase < x.phases.COMPLETE) { + this_obj.notify_result(x); + x.cleanup(); + x.phase = x.phases.COMPLETE; + } + } + ); + this.notify_complete(); + }; + + Tests.prototype.notify_complete = function() { + var this_obj = this; + if (this.status.status === null) { + this.status.status = this.status.OK; + } + + forEach (this.all_done_callbacks, + function(callback) + { + callback(this_obj.tests, this_obj.status); + }); + }; + + Tests.prototype.fetch_tests_from_worker = function(worker) { + if (this.phase >= this.phases.COMPLETE) { + return; + } + + this.pending_workers.push(new RemoteWorker(worker)); + }; + + function fetch_tests_from_worker(port) { + tests.fetch_tests_from_worker(port); + } + expose(fetch_tests_from_worker, 'fetch_tests_from_worker'); + + function timeout() { + if (tests.timeout_length === null) { + tests.timeout(); + } + } + expose(timeout, 'timeout'); + + 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) { + tests.test_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; + this.phase = this.INITIAL; + } + + Output.prototype.INITIAL = 0; + Output.prototype.STARTED = 1; + Output.prototype.HAVE_RESULTS = 2; + Output.prototype.COMPLETE = 3; + + Output.prototype.setup = function(properties) { + if (this.phase > this.INITIAL) { + return; + } + + //If output is disabled in testharnessreport.js the test shouldn't be + //able to override that + this.enabled = this.enabled && (properties.hasOwnProperty("output") ? + properties.output : settings.output); + }; + + Output.prototype.init = function(properties) { + if (this.phase >= this.STARTED) { + return; + } + if (properties.output_document) { + this.output_document = properties.output_document; + } else { + this.output_document = document; + } + this.phase = this.STARTED; + }; + + Output.prototype.resolve_log = function() { + var output_document; + if (typeof this.output_document === "function") { + output_document = this.output_document.apply(undefined); + } else { + output_document = this.output_document; + } + if (!output_document) { + return; + } + var node = output_document.getElementById("log"); + if (!node) { + if (!document.body || document.readyState == "loading") { + return; + } + node = output_document.createElement("div"); + node.id = "log"; + output_document.body.appendChild(node); + } + this.output_document = output_document; + this.output_node = node; + }; + + Output.prototype.show_status = function() { + if (this.phase < this.STARTED) { + this.init(); + } + if (!this.enabled) { + return; + } + if (this.phase < this.HAVE_RESULTS) { + this.resolve_log(); + this.phase = this.HAVE_RESULTS; + } + var done_count = tests.tests.length - tests.num_pending; + if (this.output_node) { + if (done_count < 100 || + (done_count < 1000 && done_count % 100 === 0) || + done_count % 1000 === 0) { + this.output_node.textContent = "Running, " + + done_count + " complete, " + + tests.num_pending + " remain"; + } + } + }; + + Output.prototype.show_results = function (tests, harness_status) { + if (this.phase >= this.COMPLETE) { + return; + } + if (!this.enabled) { + return; + } + if (!this.output_node) { + this.resolve_log(); + } + this.phase = this.COMPLETE; + + var log = this.output_node; + if (!log) { + return; + } + var output_document = this.output_document; + + while (log.lastChild) { + log.removeChild(log.lastChild); + } + + var harness_url = get_harness_url(); + if (harness_url !== null) { + var stylesheet = output_document.createElementNS(xhtml_ns, "link"); + stylesheet.setAttribute("rel", "stylesheet"); + stylesheet.setAttribute("href", harness_url + "testharness.css"); + var heads = output_document.getElementsByTagName("head"); + if (heads.length) { + heads[0].appendChild(stylesheet); + } + } + + var status_text_harness = {}; + status_text_harness[harness_status.OK] = "OK"; + status_text_harness[harness_status.ERROR] = "Error"; + status_text_harness[harness_status.TIMEOUT] = "Timeout"; + + var status_text = {}; + status_text[Test.prototype.PASS] = "Pass"; + status_text[Test.prototype.FAIL] = "Fail"; + status_text[Test.prototype.TIMEOUT] = "Timeout"; + status_text[Test.prototype.NOTRUN] = "Not Run"; + + var status_number = {}; + forEach(tests, + function(test) { + var status = status_text[test.status]; + if (status_number.hasOwnProperty(status)) { + status_number[status] += 1; + } else { + status_number[status] = 1; + } + }); + + function status_class(status) + { + return status.replace(/\s/g, '').toLowerCase(); + } + + var summary_template = ["section", {"id":"summary"}, + ["h2", {}, "Summary"], + function() + { + + var status = status_text_harness[harness_status.status]; + var rv = [["section", {}, + ["p", {}, + "Harness status: ", + ["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)) { + if (status_number.hasOwnProperty(status_text[i])) { + var status = status_text[i]; + rv[0].push(["div", {"class":status_class(status)}, + ["label", {}, + ["input", {type:"checkbox", checked:"checked"}], + status_number[status] + " " + status]]); + } + i++; + } + return rv; + }, + ]; + + log.appendChild(render(summary_template, {num_tests:tests.length}, output_document)); + + forEach(output_document.querySelectorAll("section#summary label"), + function(element) + { + on_event(element, "click", + function(e) + { + if (output_document.getElementById("results") === null) { + e.preventDefault(); + return; + } + var result_class = element.parentNode.getAttribute("class"); + var style_element = output_document.querySelector("style#hide-" + result_class); + var input_element = element.querySelector("input"); + if (!style_element && !input_element.checked) { + style_element = output_document.createElementNS(xhtml_ns, "style"); + style_element.id = "hide-" + result_class; + style_element.textContent = "table#results > tbody > tr."+result_class+"{display:none}"; + output_document.body.appendChild(style_element); + } else if (style_element && input_element.checked) { + style_element.parentNode.removeChild(style_element); + } + }); + }); + + // This use of innerHTML plus manual escaping is not recommended in + // general, but is necessary here for performance. Using textContent + // on each individual <td> adds tens of seconds of execution time for + // large test suites (tens of thousands of tests). + function escape_html(s) + { + return s.replace(/\&/g, "&") + .replace(/</g, "<") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + + function has_assertions() + { + for (var i = 0; i < tests.length; i++) { + if (tests[i].properties.hasOwnProperty("assert")) { + return true; + } + } + return false; + } + + function get_assertion(test) + { + if (test.properties.hasOwnProperty("assert")) { + if (Array.isArray(test.properties.assert)) { + return test.properties.assert.join(' '); + } + return test.properties.assert; + } + return ''; + } + + log.appendChild(document.createElementNS(xhtml_ns, "section")); + var assertions = has_assertions(); + var html = "<h2>Details</h2><table id='results' " + (assertions ? "class='assertions'" : "" ) + ">" + + "<thead><tr><th>Result</th><th>Test Name</th>" + + (assertions ? "<th>Assertion</th>" : "") + + "<th>Message</th></tr></thead>" + + "<tbody>"; + for (var i = 0; i < tests.length; i++) { + html += '<tr class="' + + 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."; + log.appendChild(document.createElementNS(xhtml_ns, "pre")) + .textContent = html; + } + }; + + /* + * Template code + * + * A template is just a javascript structure. An element is represented as: + * + * [tag_name, {attr_name:attr_value}, child1, child2] + * + * the children can either be strings (which act like text nodes), other templates or + * functions (see below) + * + * A text node is represented as + * + * ["{text}", value] + * + * String values have a simple substitution syntax; ${foo} represents a variable foo. + * + * It is possible to embed logic in templates by using a function in a place where a + * node would usually go. The function must either return part of a template or null. + * + * In cases where a set of nodes are required as output rather than a single node + * with children it is possible to just use a list + * [node1, node2, node3] + * + * Usage: + * + * render(template, substitutions) - take a template and an object mapping + * variable names to parameters and return either a DOM node or a list of DOM nodes + * + * substitute(template, substitutions) - take a template and variable mapping object, + * make the variable substitutions and return the substituted template + * + */ + + function is_single_node(template) + { + return typeof template[0] === "string"; + } + + function substitute(template, substitutions) + { + if (typeof template === "function") { + var replacement = template(substitutions); + if (!replacement) { + return null; + } + + return substitute(replacement, substitutions); + } + + if (is_single_node(template)) { + return substitute_single(template, substitutions); + } + + return filter(map(template, function(x) { + return substitute(x, substitutions); + }), function(x) {return x !== null;}); + } + + function substitute_single(template, substitutions) + { + var substitution_re = /\$\{([^ }]*)\}/g; + + function do_substitution(input) { + var components = input.split(substitution_re); + var rv = []; + for (var i = 0; i < components.length; i += 2) { + rv.push(components[i]); + if (components[i + 1]) { + rv.push(String(substitutions[components[i + 1]])); + } + } + return rv; + } + + function substitute_attrs(attrs, rv) + { + rv[1] = {}; + for (var name in template[1]) { + if (attrs.hasOwnProperty(name)) { + var new_name = do_substitution(name).join(""); + var new_value = do_substitution(attrs[name]).join(""); + rv[1][new_name] = new_value; + } + } + } + + function substitute_children(children, rv) + { + for (var i = 0; i < children.length; i++) { + if (children[i] instanceof Object) { + var replacement = substitute(children[i], substitutions); + if (replacement !== null) { + if (is_single_node(replacement)) { + rv.push(replacement); + } else { + extend(rv, replacement); + } + } + } else { + extend(rv, do_substitution(String(children[i]))); + } + } + return rv; + } + + var rv = []; + rv.push(do_substitution(String(template[0])).join("")); + + if (template[0] === "{text}") { + substitute_children(template.slice(1), rv); + } else { + substitute_attrs(template[1], rv); + substitute_children(template.slice(2), rv); + } + + return rv; + } + + function make_dom_single(template, doc) + { + var output_document = doc || document; + var element; + if (template[0] === "{text}") { + element = output_document.createTextNode(""); + for (var i = 1; i < template.length; i++) { + element.data += template[i]; + } + } else { + element = output_document.createElementNS(xhtml_ns, template[0]); + for (var name in template[1]) { + if (template[1].hasOwnProperty(name)) { + element.setAttribute(name, template[1][name]); + } + } + for (var i = 2; i < template.length; i++) { + if (template[i] instanceof Object) { + var sub_element = make_dom(template[i]); + element.appendChild(sub_element); + } else { + var text_node = output_document.createTextNode(template[i]); + element.appendChild(text_node); + } + } + } + + return element; + } + + function make_dom(template, substitutions, output_document) + { + if (is_single_node(template)) { + return make_dom_single(template, output_document); + } + + return map(template, function(x) { + return make_dom_single(x, output_document); + }); + } + + function render(template, substitutions, output_document) + { + return make_dom(substitute(template, substitutions), output_document); + } + + /* + * Utility funcions + */ + function assert(expected_true, function_name, description, error, substitutions) + { + if (tests.tests.length === 0) { + tests.set_file_is_test(); + } + if (expected_true !== true) { + var msg = make_message(function_name, description, + error, substitutions); + throw new AssertionError(msg); + } + } + + function AssertionError(message) + { + this.message = message; + this.stack = this.get_stack(); + } + + AssertionError.prototype = Object.create(Error.prototype); + + AssertionError.prototype.get_stack = function() { + var stack = new Error().stack; + // IE11 does not initialize 'Error.stack' until the object is thrown. + if (!stack) { + try { + throw new Error(); + } catch (e) { + stack = e.stack; + } + } + + var lines = stack.split("\n"); + + // Create a pattern to match stack frames originating within testharness.js. These include the + // script URL, followed by the line/col (e.g., '/resources/testharness.js:120:21'). + var re = new RegExp((get_script_url() || "\\btestharness.js") + ":\\d+:\\d+"); + + // Some browsers include a preamble that specifies the type of the error object. Skip this by + // advancing until we find the first stack frame originating from testharness.js. + var i = 0; + while (!re.test(lines[i]) && i < lines.length) { + i++; + } + + // Then skip the top frames originating from testharness.js to begin the stack at the test code. + while (re.test(lines[i]) && i < lines.length) { + i++; + } + + // Paranoid check that we didn't skip all frames. If so, return the original stack unmodified. + if (i >= lines.length) { + return stack; + } + + 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]); + } + } + var node_form = substitute(["{text}", "${function_name}: ${description}" + error], + merge({function_name:function_name, + description:(description?description + " ":"")}, + substitutions)); + return node_form.slice(1).join(""); + } + + function filter(array, callable, thisObj) { + var rv = []; + for (var i = 0; i < array.length; i++) { + if (array.hasOwnProperty(i)) { + var pass = callable.call(thisObj, array[i], i, array); + if (pass) { + rv.push(array[i]); + } + } + } + return rv; + } + + function map(array, callable, thisObj) + { + var rv = []; + rv.length = array.length; + for (var i = 0; i < array.length; i++) { + if (array.hasOwnProperty(i)) { + rv[i] = callable.call(thisObj, array[i], i, array); + } + } + return rv; + } + + function extend(array, items) + { + Array.prototype.push.apply(array, items); + } + + function forEach(array, callback, thisObj) + { + for (var i = 0; i < array.length; i++) { + if (array.hasOwnProperty(i)) { + callback.call(thisObj, array[i], i, array); + } + } + } + + function merge(a,b) + { + var rv = {}; + var p; + for (p in a) { + rv[p] = a[p]; + } + for (p in b) { + rv[p] = b[p]; + } + return rv; + } + + function expose(object, name) + { + var components = name.split("."); + var target = test_environment.global_scope(); + for (var i = 0; i < components.length - 1; i++) { + if (!(components[i] in target)) { + target[components[i]] = {}; + } + target = target[components[i]]; + } + target[components[components.length - 1]] = object; + } + + function is_same_origin(w) { + try { + 'random_prop' in w; + return true; + } catch (e) { + return false; + } + } + + /** Returns the 'src' URL of the first <script> tag in the page to include the file 'testharness.js'. */ + function get_script_url() + { + if (!('document' in self)) { + return undefined; + } + + var scripts = document.getElementsByTagName("script"); + for (var i = 0; i < scripts.length; i++) { + var src; + if (scripts[i].src) { + src = scripts[i].src; + } else if (scripts[i].href) { + //SVG case + src = scripts[i].href.baseVal; + } + + var matches = src && src.match(/^(.*\/|)testharness\.js$/); + if (matches) { + return src; + } + } + return undefined; + } + + /** Returns the URL path at which the files for testharness.js are assumed to reside (e.g., '/resources/'). + The path is derived from inspecting the 'src' of the <script> tag that included 'testharness.js'. */ + function get_harness_url() + { + var script_url = get_script_url(); + + // Exclude the 'testharness.js' file from the returned path, but '+ 1' to include the trailing slash. + return script_url ? script_url.slice(0, script_url.lastIndexOf('/') + 1) : undefined; + } + + function supports_post_message(w) + { + var supports; + var type; + // Given IE implements postMessage across nested iframes but not across + // windows or tabs, you can't infer cross-origin communication from the presence + // of postMessage on the current window object only. + // + // Touching the postMessage prop on a window can throw if the window is + // not from the same origin AND post message is not supported in that + // browser. So just doing an existence test here won't do, you also need + // to wrap it in a try..cacth block. + try { + type = typeof w.postMessage; + if (type === "function") { + supports = true; + } + + // IE8 supports postMessage, but implements it as a host object which + // returns "object" as its `typeof`. + else if (type === "object") { + supports = true; + } + + // This is the case where postMessage isn't supported AND accessing a + // window property across origins does NOT throw (e.g. old Safari browser). + else { + supports = false; + } + } catch (e) { + // This is the case where postMessage isn't supported AND accessing a + // window property across origins throws (e.g. old Firefox browser). + supports = false; + } + return supports; + } + + /** + * Setup globals + */ + + 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; + } + 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: diff --git a/dom/imptests/testharnessreport.js b/dom/imptests/testharnessreport.js new file mode 100644 index 000000000..6bc7ec3ab --- /dev/null +++ b/dom/imptests/testharnessreport.js @@ -0,0 +1,312 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var W3CTest = { + /** + * Dictionary mapping a test URL to either the string "all", which means that + * all tests in this file are expected to fail, or a dictionary mapping test + * names to either the boolean |true|, or the string "debug". The former + * means that this test is expected to fail in all builds, and the latter + * that it is only expected to fail in debug builds. + */ + "expectedFailures": {}, + + /** + * If set to true, we will dump the test failures to the console. + */ + "dumpFailures": false, + + /** + * If dumpFailures is true, this holds a structure like necessary for + * expectedFailures, for ease of updating the expectations. + */ + "failures": {}, + + /** + * List of test results, needed by TestRunner to update the UI. + */ + "tests": [], + + /** + * Number of unlogged passes, to stop buildbot from truncating the log. + * We will print a message every MAX_COLLAPSED_MESSAGES passes. + */ + "collapsedMessages": 0, + "MAX_COLLAPSED_MESSAGES": 100, + + /** + * Reference to the TestRunner object in the parent frame. + */ + "runner": parent === this ? null : parent.TestRunner || parent.wrappedJSObject.TestRunner, + + /** + * Prefixes for the error logging. Indexed first by int(todo) and second by + * int(result). Also contains the test's status, and expected status. + */ + "prefixes": [ + [ + {status: 'FAIL', expected: 'PASS', message: "TEST-UNEXPECTED-FAIL"}, + {status: 'PASS', expected: 'PASS', message: "TEST-PASS"} + ], + [ + {status: 'FAIL', expected: 'FAIL', message: "TEST-KNOWN-FAIL"}, + {status: 'PASS', expected: 'FAIL', message: "TEST-UNEXPECTED-PASS"} + ] + ], + + /** + * Prefix of the path to parent of the the failures directory. + */ + "pathprefix": "/tests/dom/imptests/", + + /** + * Returns the URL of the current test, relative to the root W3C tests + * directory. Used as a key into the expectedFailures dictionary. + */ + "getPath": function() { + var url = this.getURL(); + if (!url.startsWith(this.pathprefix)) { + return ""; + } + return url.substring(this.pathprefix.length); + }, + + /** + * Returns the root-relative URL of the current test. + */ + "getURL": function() { + return this.runner ? this.runner.currentTestURL : location.pathname; + }, + + /** + * Report the results in the tests array. + */ + "reportResults": function() { + var element = function element(aLocalName) { + var xhtmlNS = "http://www.w3.org/1999/xhtml"; + return document.createElementNS(xhtmlNS, aLocalName); + }; + + var stylesheet = element("link"); + stylesheet.setAttribute("rel", "stylesheet"); + stylesheet.setAttribute("href", "/resources/testharness.css"); + var heads = document.getElementsByTagName("head"); + if (heads.length) { + heads[0].appendChild(stylesheet); + } + + var log = document.getElementById("log"); + if (!log) { + return; + } + var section = log.appendChild(element("section")); + section.id = "summary"; + section.appendChild(element("h2")).textContent = "Details"; + + var table = section.appendChild(element("table")); + table.id = "results"; + + var tr = table.appendChild(element("thead")).appendChild(element("tr")); + for (var header of ["Result", "Test Name", "Message"]) { + tr.appendChild(element("th")).textContent = header; + } + var statuses = [ + ["Unexpected Fail", "Pass"], + ["Known Fail", "Unexpected Pass"] + ]; + var tbody = table.appendChild(element("tbody")); + for (var test of this.tests) { + tr = tbody.appendChild(element("tr")); + tr.className = (test.result === !test.todo ? "pass" : "fail"); + tr.appendChild(element("td")).textContent = + statuses[+test.todo][+test.result]; + tr.appendChild(element("td")).textContent = test.name; + tr.appendChild(element("td")).textContent = test.message; + } + }, + + /** + * Returns a printable message based on aTest's 'name' and 'message' + * properties. + */ + "formatTestMessage": function(aTest) { + return aTest.name + (aTest.message ? ": " + aTest.message : ""); + }, + + /** + * Lets the test runner know about a test result. + */ + "_log": function(test) { + var url = this.getURL(); + var message = this.formatTestMessage(test); + var result = this.prefixes[+test.todo][+test.result]; + + if (this.runner) { + this.runner.structuredLogger.testStatus(url, + test.name, + result.status, + result.expected, + message); + } else { + var msg = result.message + " | "; + if (url) { + msg += url; + } + msg += " | " + this.formatTestMessage(test); + dump(msg + "\n"); + } + }, + + /** + * Logs a message about collapsed messages (if any), and resets the counter. + */ + "_logCollapsedMessages": function() { + if (this.collapsedMessages) { + this._log({ + "name": document.title, + "result": true, + "todo": false, + "message": "Elided " + this.collapsedMessages + " passes or known failures." + }); + } + this.collapsedMessages = 0; + }, + + /** + * Maybe logs a result, eliding up to MAX_COLLAPSED_MESSAGES consecutive + * passes. + */ + "_maybeLog": function(test) { + var success = (test.result === !test.todo); + if (success && ++this.collapsedMessages < this.MAX_COLLAPSED_MESSAGES) { + return; + } + this._logCollapsedMessages(); + this._log(test); + }, + + /** + * Reports a test result. The argument is an object with the following + * properties: + * + * o message (string): message to be reported + * o result (boolean): whether this test failed + * o todo (boolean): whether this test is expected to fail + */ + "report": function(test) { + this.tests.push(test); + this._maybeLog(test); + }, + + /** + * Returns true if this test is expected to fail, and false otherwise. + */ + "_todo": function(test) { + if (this.expectedFailures === "all") { + return true; + } + var value = this.expectedFailures[test.name]; + return value === true || (value === "debug" && !!SpecialPowers.isDebugBuild); + }, + + /** + * Callback function for testharness.js. Called when one test in a file + * finishes. + */ + "result": function(test) { + var url = this.getPath(); + this.report({ + "name": test.name, + "message": test.message || "", + "result": test.status === test.PASS, + "todo": this._todo(test) + }); + if (this.dumpFailures && test.status !== test.PASS) { + this.failures[test.name] = true; + } + }, + + /** + * Callback function for testharness.js. Called when the entire test file + * finishes. + */ + "finish": function(tests, status) { + var url = this.getPath(); + this.report({ + "name": "Finished test", + "message": "Status: " + status.status, + "result": status.status === status.OK, + "todo": + url in this.expectedFailures && + this.expectedFailures[url] === "error" + }); + + this._logCollapsedMessages(); + + if (this.dumpFailures) { + dump("@@@ @@@ Failures\n"); + dump(url + "@@@" + JSON.stringify(this.failures) + "\n"); + } + if (this.runner) { + this.runner.testFinished(this.tests.map(function(aTest) { + return { + "message": this.formatTestMessage(aTest), + "result": aTest.result, + "todo": aTest.todo + }; + }, this)); + } else { + this.reportResults(); + } + }, + + /** + * Log an unexpected failure. Intended to be used from harness code, not + * from tests. + */ + "logFailure": function(aTestName, aMessage) { + this.report({ + "name": aTestName, + "message": aMessage, + "result": false, + "todo": false + }); + }, + + /** + * Timeout the current test. Intended to be used from harness code, not + * from tests. + */ + "timeout": function() { + this.logFailure("Timeout", "Test runner timed us out."); + timeout(); + } +}; +(function() { + try { + var path = W3CTest.getPath(); + if (path) { + // Get expected fails. If there aren't any, there will be a 404, which is + // fine. Anything else is unexpected. + var url = W3CTest.pathprefix + "failures/" + path + ".json"; + var request = new XMLHttpRequest(); + request.open("GET", url, false); + request.send(); + if (request.status === 200) { + W3CTest.expectedFailures = JSON.parse(request.responseText); + } else if (request.status !== 404) { + W3CTest.logFailure("Fetching failures file", "Request status was " + request.status); + } + } + + add_result_callback(W3CTest.result.bind(W3CTest)); + add_completion_callback(W3CTest.finish.bind(W3CTest)); + setup({ + "output": W3CTest.runner && !W3CTest.runner.getParameterInfo().closeWhenDone, + "explicit_timeout": true + }); + } catch (e) { + W3CTest.logFailure("Harness setup", "Unexpected exception: " + e); + } +})(); diff --git a/dom/imptests/updateTestharness.py b/dom/imptests/updateTestharness.py new file mode 100644 index 000000000..42deadcd4 --- /dev/null +++ b/dom/imptests/updateTestharness.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. + +from __future__ import unicode_literals + +import subprocess + +repo = "https://github.com/w3c/testharness.js" +dest = "resources-upstream" +files = [{"f":"testharness.js"}, + {"f":"testharness.css"}, + {"f":"idlharness.js"}, + {"d":"webidl2/lib/webidl2.js", "f":"WebIDLParser.js"}] + +subprocess.check_call(["git", "clone", repo, dest]) +subprocess.check_call(["git", "submodule", "init"], cwd=dest) +subprocess.check_call(["git", "submodule", "update"], cwd=dest) +for f in files: + path = f["d"] if "d" in f else f["f"] + subprocess.check_call(["cp", "%s/%s" % (dest, path), f["f"]]) + subprocess.check_call(["hg", "add", f["f"]]) +subprocess.check_call(["rm", "-rf", dest]) + diff --git a/dom/imptests/webapps.txt b/dom/imptests/webapps.txt new file mode 100644 index 000000000..25172d699 --- /dev/null +++ b/dom/imptests/webapps.txt @@ -0,0 +1,3 @@ +hg|https://dvcs.w3.org/hg/webapps|webapps +WebStorage/tests/submissions +XMLHttpRequest/tests/submissions/Ms2ger diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/local_change_item_iframe.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/local_change_item_iframe.html new file mode 100644 index 000000000..19505615d --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/local_change_item_iframe.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<html> + <body> + <script> + if (('localStorage' in window) && window.localStorage !== null){ + try { + localStorage.setItem("name", "user1"); + localStorage.setItem("name", "user2"); + } catch (e) { + parent.fail("setItem method is failed."); + } + localStorage.clear(); + } else { + parent.fail("localStorage is not supported."); + } + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/local_security_iframe.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/local_security_iframe.html new file mode 100644 index 000000000..bc2fad3ab --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/local_security_iframe.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML>
+<html>
+ <body>
+ <script>
+ if (('localStorage' in window) && window.localStorage != null){
+ try {
+ localStorage.setItem("Security", "false");
+ parent.localStorage.clear();
+ } catch (e) {
+ if(e.code == e['SECURITY_ERR'])
+ localStorage.setItem("Security", "true");
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/local_set_item_clear_iframe.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/local_set_item_clear_iframe.html new file mode 100644 index 000000000..74a6285a2 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/local_set_item_clear_iframe.html @@ -0,0 +1,17 @@ +<!DOCTYPE HTML> +<html> + <body> + <script> + if (('localStorage' in window) && window.localStorage !== null){ + try { + localStorage.setItem("name", "user1"); + } catch (e) { + parent.fail("setItem method is failed."); + } + localStorage.clear(); + } else { + parent.fail("localStorage is not supported."); + } + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/local_set_item_iframe.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/local_set_item_iframe.html new file mode 100644 index 000000000..bacb3b2bb --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/local_set_item_iframe.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML> +<html> + <body> + <script> + if (('localStorage' in window) && window.localStorage !== null){ + try { + localStorage.setItem("name", "user1"); + } catch (e) { + parent.fail("setItem method is failed."); + } + } else { + parent.fail("localStorage is not supported."); + } + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/session_change_item_iframe.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/session_change_item_iframe.html new file mode 100644 index 000000000..6c86f58ed --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/session_change_item_iframe.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<html> + <body> + <script> + if (('sessionStorage' in window) && window.sessionStorage !== null){ + try { + sessionStorage.setItem("name", "user1"); + sessionStorage.setItem("name", "user2"); + } catch (e) { + parent.fail("setItem method is failed."); + } + sessionStorage.clear(); + } else { + parent.fail("sessionStorage is not supported."); + } + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/session_set_item_clear_iframe.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/session_set_item_clear_iframe.html new file mode 100644 index 000000000..a305f21a9 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/session_set_item_clear_iframe.html @@ -0,0 +1,17 @@ +<!DOCTYPE HTML> +<html> + <body> + <script> + if (('sessionStorage' in window) && window.sessionStorage !== null){ + try { + sessionStorage.setItem('name', 'user1'); + } catch (e) { + parent.fail('setItem method is failed.'); + } + sessionStorage.clear(); + } else { + parent.fail('sessionStorage is not supported.'); + } + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/session_set_item_iframe.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/session_set_item_iframe.html new file mode 100644 index 000000000..28d7059c3 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/session_set_item_iframe.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML> +<html> + <body> + <script> + if (('sessionStorage' in window) && window.sessionStorage !== null){ + try { + sessionStorage.setItem('name', 'user1'); + } catch (e) { + parent.fail('setItem method is failed.'); + } + } else { + parent.fail('sessionStorage is not supported.'); + } + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_constructor.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_constructor.html new file mode 100644 index 000000000..cc543c7fc --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_constructor.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>event_session_Constructor</h1> + <div id="log"></div> + <script> + test(function() { + var t = async_test("storageeventinit test"); + function onStorageEvent(event) { + t.step(function() { + assert_equals(event.type, 'storage'); + assert_equals(event.key, null); + assert_equals(event.oldValue, null); + assert_equals(event.newValue, null); + assert_equals(event.url, ''); + assert_equals(event.storageArea, null); + }); + t.done(); + } + + window.addEventListener('storage', onStorageEvent, false); + var event = new StorageEvent('storage'); + window.dispatchEvent(event); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_key.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_key.html new file mode 100644 index 000000000..534f92f3b --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_key.html @@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script> + function fail(msg) { + t.step(function() { + assert_notreached(msg); + }); + t.done(); + } + </script> + </head> + <body> + <h1>event_local_key</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + var t = async_test("key property test of local event"); + var expected = ['name', null] + function onStorageEvent(event) { + t.step(function() { + assert_equals(event.key, expected.shift()); + }); + if (!expected.length) { + t.done(); + } + } + + window.addEventListener('storage', onStorageEvent, false); + + var el = document.createElement("iframe"); + el.setAttribute('id', 'ifrm'); + el.setAttribute('src', 'iframe/local_set_item_clear_iframe.html'); + document.body.appendChild(el); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_newvalue.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_newvalue.html new file mode 100644 index 000000000..91a8c504c --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_newvalue.html @@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script> + function fail(msg) { + t.step(function() { + assert_notreached(msg); + }); + t.done(); + } + </script> + </head> + <body> + <h1>event_local_newValue</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + var t = async_test("newValue property test of local event"); + var expected = ['user1', 'user2', null] + function onStorageEvent(event) { + t.step(function() { + assert_equals(event.newValue, expected.shift()); + }); + if (!expected.length) { + t.done(); + } + } + + window.addEventListener('storage', onStorageEvent, false); + + var el = document.createElement("iframe"); + el.setAttribute('id', 'ifrm'); + el.setAttribute('src', 'iframe/local_change_item_iframe.html'); + document.body.appendChild(el); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_oldvalue.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_oldvalue.html new file mode 100644 index 000000000..faf434001 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_oldvalue.html @@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script> + function fail(msg) { + t.step(function() { + assert_notreached(msg); + }); + t.done(); + } + </script> + </head> + <body> + <h1>event_local_oldValue</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + var t = async_test("oldValue property test of local event"); + var expected = [null, 'user1', null] + function onStorageEvent(event) { + t.step(function() { + assert_equals(event.oldValue, expected.shift()); + }); + if (!expected.length) { + t.done(); + } + } + + window.addEventListener('storage', onStorageEvent, false); + + var el = document.createElement("iframe"); + el.setAttribute('id', 'ifrm'); + el.setAttribute('src', 'iframe/local_change_item_iframe.html'); + document.body.appendChild(el); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_storagearea.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_storagearea.html new file mode 100644 index 000000000..abe0113a8 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_storagearea.html @@ -0,0 +1,43 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script> + function fail(msg) { + t.step(function() { + assert_notreached(msg); + }); + t.done(); + } + </script> + </head> + <body> + <h1>event_local_storageArea</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + var t = async_test("storageArea property test of local event"); + function onStorageEvent(event) { + t.step(function() { + assert_equals(event.storageArea.length, 1); + var key = event.storageArea.key(0); + var value = event.storageArea.getItem(key); + assert_equals(key, "name"); + assert_equals(value, "user1"); + }); + t.done(); + } + + window.addEventListener('storage', onStorageEvent, false); + + var el = document.createElement("iframe"); + el.setAttribute('id', 'ifrm'); + el.setAttribute('src', 'iframe/local_set_item_iframe.html'); + document.body.appendChild(el); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_storageeventinit.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_storageeventinit.html new file mode 100644 index 000000000..36e5a19e8 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_storageeventinit.html @@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>event_local_StorageEventInit</h1> + <div id="log"></div> + <script> + test(function() { + var t = async_test("storageeventinit test"); + function onStorageEvent(event) { + t.step(function() { + assert_equals(event.key, 'key'); + assert_equals(event.oldValue, 'oldValue'); + assert_equals(event.newValue, 'newValue'); + assert_equals(event.url, window.location.href); + assert_equals(event.storageArea, window.localStorage); + }); + t.done(); + } + + window.addEventListener('storage', onStorageEvent, false); + + var event = new StorageEvent('storage', { + key: 'key', + oldValue: 'oldValue', + newValue: 'newValue', + url: window.location.href, + storageArea: window.localStorage + }); + + window.dispatchEvent(event); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_url.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_url.html new file mode 100644 index 000000000..8a4e6fb53 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_url.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script> + function fail(msg) { + t.step(function() { + assert_notreached(msg); + }); + t.done(); + } + </script> + </head> + <body> + <h1>event_local_url</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + var t = async_test("url property test of local event"); + function onStorageEvent(event) { + t.step(function() { + var url = window.location.href; + + var pos = url.lastIndexOf("/"); + if (pos != -1) { + url = url.substr(0, pos + 1); + url = url + "iframe/local_set_item_iframe.html"; + } + + assert_equals(event.url, url); + }); + t.done(); + } + + window.addEventListener('storage', onStorageEvent, false); + + var el = document.createElement("iframe"); + el.setAttribute('id', 'ifrm'); + el.setAttribute('src', 'iframe/local_set_item_iframe.html'); + document.body.appendChild(el); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_key.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_key.html new file mode 100644 index 000000000..f976a6732 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_key.html @@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script> + function fail(msg) { + t.step(function() { + assert_notreached(msg); + }); + t.done(); + } + </script> + </head> + <body> + <h1>event_session_key</h1> + <div id="log"></div> + <script> + test(function() { + sessionStorage.clear(); + var t = async_test("key property test of session event"); + var expected = ['name', null] + function onStorageEvent(event) { + t.step(function() { + assert_equals(event.key, expected.shift()); + }); + if (!expected.length) { + t.done(); + } + } + + window.addEventListener('storage', onStorageEvent, false); + + var el = document.createElement("iframe"); + el.setAttribute('id', 'ifrm'); + el.setAttribute('src', 'iframe/session_set_item_clear_iframe.html'); + document.body.appendChild(el); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_newvalue.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_newvalue.html new file mode 100644 index 000000000..b2358d323 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_newvalue.html @@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script> + function fail(msg) { + t.step(function() { + assert_notreached(msg); + }); + t.done(); + } + </script> + </head> + <body> + <h1>event_session_newValue</h1> + <div id="log"></div> + <script> + test(function() { + sessionStorage.clear(); + var t = async_test("newvalue property test of session event"); + var expected = ['user1', 'user2', null] + function onStorageEvent(event) { + t.step(function() { + assert_equals(event.newValue, expected.shift()); + }); + if (!expected.length) { + t.done(); + } + } + + window.addEventListener('storage', onStorageEvent, false); + + var el = document.createElement("iframe"); + el.setAttribute('id', 'ifrm'); + el.setAttribute('src', 'iframe/session_change_item_iframe.html'); + document.body.appendChild(el); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_oldvalue.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_oldvalue.html new file mode 100644 index 000000000..b81c30921 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_oldvalue.html @@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script> + function fail(msg) { + t.step(function() { + assert_notreached(msg); + }); + t.done(); + } + </script> + </head> + <body> + <h1>event_session_oldValue</h1> + <div id="log"></div> + <script> + test(function() { + sessionStorage.clear(); + var t = async_test("oldvalue property test of session event"); + var expected = [null, 'user1', null] + function onStorageEvent(event) { + t.step(function() { + assert_equals(event.oldValue, expected.shift()); + }); + if (!expected.length) { + t.done(); + } + } + + window.addEventListener('storage', onStorageEvent, false); + + var el = document.createElement("iframe"); + el.setAttribute('id', 'ifrm'); + el.setAttribute('src', 'iframe/session_change_item_iframe.html'); + document.body.appendChild(el); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_storagearea.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_storagearea.html new file mode 100644 index 000000000..f478e6d7f --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_storagearea.html @@ -0,0 +1,43 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script> + function fail(msg) { + t.step(function() { + assert_notreached(msg); + }); + t.done(); + } + </script> + </head> + <body> + <h1>event_session_storageArea</h1> + <div id="log"></div> + <script> + test(function() { + var t = async_test("storageArea property test of session event"); + function onStorageEvent(event) { + t.step(function() { + assert_equals(event.storageArea.length, 1); + var key = event.storageArea.key(0); + var value = event.storageArea.getItem(key); + assert_equals(key, "name"); + assert_equals(value, "user1"); + }); + t.done(); + } + + window.addEventListener('storage', onStorageEvent, false); + + var el = document.createElement("iframe"); + el.setAttribute('id', 'ifrm'); + el.setAttribute('src', 'iframe/session_set_item_iframe.html'); + document.body.appendChild(el); + }); + </script> + + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_storageeventinit.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_storageeventinit.html new file mode 100644 index 000000000..a2d2c1a73 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_storageeventinit.html @@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>event_session_StorageEventInit</h1> + <div id="log"></div> + <script> + test(function() { + var t = async_test("storageeventinit test"); + function onStorageEvent(event) { + t.step(function() { + assert_equals(event.key, 'key'); + assert_equals(event.oldValue, 'oldValue'); + assert_equals(event.newValue, 'newValue'); + assert_equals(event.url, window.location.href); + assert_equals(event.storageArea, window.sessionStorage); + }); + t.done(); + } + + window.addEventListener('storage', onStorageEvent, false); + + var event = new StorageEvent('storage', { + key: 'key', + oldValue: 'oldValue', + newValue: 'newValue', + url: window.location.href, + storageArea: window.sessionStorage + }); + + window.dispatchEvent(event); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_url.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_url.html new file mode 100644 index 000000000..c4842bedc --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_url.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script> + function fail(msg) { + t.step(function() { + assert_notreached(msg); + }); + t.done(); + } + </script> + </head> + <body> + <h1>event_session_url</h1> + <div id="log"></div> + <script> + test(function() { + sessionStorage.clear(); + var t = async_test("url property test of session event"); + function onStorageEvent(event) { + t.step(function() { + var url = window.location.href; + + var pos = url.lastIndexOf("/"); + if (pos != -1) { + url = url.substr(0, pos + 1); + url = url + "iframe/session_set_item_iframe.html"; + } + + assert_equals(event.url, url); + }); + t.done(); + } + + window.addEventListener('storage', onStorageEvent, false); + + var el = document.createElement("iframe"); + el.setAttribute('id', 'ifrm'); + el.setAttribute('src', 'iframe/session_set_item_iframe.html'); + document.body.appendChild(el); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_clear.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_clear.html new file mode 100644 index 000000000..39fd6d63a --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_clear.html @@ -0,0 +1,25 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_local_clear</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + localStorage.setItem("name", "user1"); + + assert_not_equals(localStorage.getItem("name"), null); + assert_equals(localStorage.length, 1); + + localStorage.clear(); + assert_equals(localStorage.getItem("name"), null, "localStorage.getItem('name')") + assert_equals(localStorage.length, 0, "localStorage.length") + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_getitem.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_getitem.html new file mode 100644 index 000000000..a6872ac94 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_getitem.html @@ -0,0 +1,23 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_local_getItem</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + localStorage.setItem("name", "user1"); + localStorage.setItem("age", "20"); + test(function() { + assert_equals(localStorage.getItem("name"), "user1", "localStorage.getItem('name')") + assert_equals(localStorage.getItem("unknown"), null, "localStorage.getItem('unknown')") + }, "getItem method test") + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_key.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_key.html new file mode 100644 index 000000000..7ec0acba4 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_key.html @@ -0,0 +1,40 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_local_key</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + + localStorage.setItem("name", "user1"); + localStorage.setItem("age", "20"); + localStorage.setItem("a", "1"); + localStorage.setItem("b", "2"); + + var keys = ["name", "age", "a", "b"]; + function doTest(index) { + test(function() { + var key = localStorage.key(index); + assert_not_equals(key, null); + assert_true(keys.indexOf(key) >= 0, + "Unexpected key " + key + " found."); + }, "key(" + index + ") should return the right thing."); + } + for (var i = 0; i < keys.length; ++i) { + doTest(i); + doTest(i + 0x100000000); + } + test(function() { + assert_equals(localStorage.key(-1), null, "localStorage.key(-1)"); + assert_equals(localStorage.key(4), null, "localStorage.key(4)"); + }, "key() should return null for out-of-range arguments."); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_length.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_length.html new file mode 100644 index 000000000..cf58cc7f4 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_length.html @@ -0,0 +1,23 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_local_length</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + assert_equals(localStorage.length, 0, "localStorage.length") + + localStorage.setItem("name", "user1"); + localStorage.setItem("age", "20"); + + assert_equals(localStorage.length, 2, "localStorage.length") + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_removeitem.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_removeitem.html new file mode 100644 index 000000000..67d336a00 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_removeitem.html @@ -0,0 +1,23 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_local_removeItem</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + localStorage.setItem("name", "user1"); + assert_equals(localStorage.getItem("name"), "user1"); + + localStorage.removeItem("name"); + localStorage.removeItem("unknown"); + assert_equals(localStorage.getItem("name"), null, "localStorage.getItem('name')") + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_security.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_security.html new file mode 100644 index 000000000..4833d8a17 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_security.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Web Storage</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <h1>storage_local_security</h1>
+ <iframe id="frame" src="iframe/local_security_iframe.html" style="width: 0px; height: 0px;"></iframe>
+ <div id="log"></div>
+ <script>
+ var t1 = async_test('storage local security test');
+
+ iframeWindow = document.getElementById('frame').contentWindow;
+
+ setTimeout(function(){
+ try {
+ var errFlag =iframeWindow.localStorage.getItem("Security");
+ t1.step(function() {
+ assert_equals(errFlag, "true", 'SECURITY_ERR error is not raised.')
+ });
+ } catch (e) {
+ t1.step(function() {
+ assert_unreached('Error is raised.');
+ });
+ }
+ t1.done();
+ }, 500);
+ </script>
+ </body>
+</html>
diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_setitem.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_setitem.html new file mode 100644 index 000000000..1225d8a7b --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_setitem.html @@ -0,0 +1,19 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_local_setItem</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + localStorage.setItem("name", "user1"); + assert_equals(localStorage.length, 1, "localStorage.setItem") + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_clear.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_clear.html new file mode 100644 index 000000000..2d63a259a --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_clear.html @@ -0,0 +1,25 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_session_clear</h1> + <div id="log"></div> + <script> + test(function() { + sessionStorage.clear(); + sessionStorage.setItem("name", "user1"); + + assert_not_equals(sessionStorage.getItem("name"), null); + assert_equals(sessionStorage.length, 1); + + sessionStorage.clear(); + assert_equals(sessionStorage.getItem("name"), null, "sessionStorage.getItem('name')") + assert_equals(sessionStorage.length, 0, "sessionStorage.length") + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_getitem.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_getitem.html new file mode 100644 index 000000000..8f0b04452 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_getitem.html @@ -0,0 +1,23 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_session_getItem</h1> + <div id="log"></div> + <script> + test(function() { + sessionStorage.clear(); + sessionStorage.setItem("name", "user1"); + sessionStorage.setItem("age", "20"); + test(function() { + assert_equals(sessionStorage.getItem("name"), "user1", "sessionStorage.getItem('name')") + assert_equals(sessionStorage.getItem("unknown"), null, "sessionStorage.getItem('unknown')") + }, "getItem method test") + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_key.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_key.html new file mode 100644 index 000000000..31911f99c --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_key.html @@ -0,0 +1,40 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_session_key</h1> + <div id="log"></div> + <script> + test(function() { + sessionStorage.clear(); + + sessionStorage.setItem("name", "user1"); + sessionStorage.setItem("age", "20"); + sessionStorage.setItem("a", "1"); + sessionStorage.setItem("b", "2"); + + var keys = ["name", "age", "a", "b"]; + function doTest(index) { + test(function() { + var key = sessionStorage.key(index); + assert_not_equals(key, null); + assert_true(keys.indexOf(key) >= 0, + "Unexpected key " + key + " found."); + }, "key(" + index + ") should return the right thing."); + } + for (var i = 0; i < keys.length; ++i) { + doTest(i); + doTest(i + 0x100000000); + } + test(function() { + assert_equals(sessionStorage.key(-1), null, "sessionStorage.key(-1)"); + assert_equals(sessionStorage.key(4), null, "sessionStorage.key(4)"); + }, "key() should return null for out-of-range arguments."); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_length.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_length.html new file mode 100644 index 000000000..daf06ccf6 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_length.html @@ -0,0 +1,23 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_session_length</h1> + <div id="log"></div> + <script> + test(function() { + sessionStorage.clear(); + assert_equals(sessionStorage.length, 0, "sessionStorage.length") + + sessionStorage.setItem("name", "user1"); + sessionStorage.setItem("age", "20"); + + assert_equals(sessionStorage.length, 2, "sessionStorage.length") + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_removeitem.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_removeitem.html new file mode 100644 index 000000000..7601f1e6e --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_removeitem.html @@ -0,0 +1,23 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_session_removeItem</h1> + <div id="log"></div> + <script> + test(function() { + sessionStorage.clear(); + sessionStorage.setItem("name", "user1"); + assert_equals(sessionStorage.getItem("name"), "user1"); + + sessionStorage.removeItem("name"); + sessionStorage.removeItem("unknown"); + assert_equals(sessionStorage.getItem("name"), null, "sessionStorage.getItem('name')") + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_setitem.html b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_setitem.html new file mode 100644 index 000000000..130d61e52 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_setitem.html @@ -0,0 +1,19 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_session_setItem</h1> + <div id="log"></div> + <script> + test(function() { + sessionStorage.clear(); + sessionStorage.setItem("name", "user1"); + assert_equals(sessionStorage.length, 1, "localStorage.setItem") + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/storage_builtins.js b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/storage_builtins.js new file mode 100644 index 000000000..9c2f1a5ff --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/storage_builtins.js @@ -0,0 +1,15 @@ +function test_storage_builtins(aStorage) { + test(function() { + aStorage.clear(); + assert_equals(aStorage.length, 0, "aStorage.length"); + + var builtins = ["key", "getItem", "setItem", "removeItem", "clear"]; + var origBuiltins = builtins.map(function(b) { return Storage.prototype[b]; }); + assert_array_equals(builtins.map(function(b) { return aStorage[b]; }), origBuiltins, "a"); + builtins.forEach(function(b) { aStorage[b] = b; }); + assert_array_equals(builtins.map(function(b) { return aStorage[b]; }), origBuiltins, "b"); + assert_array_equals(builtins.map(function(b) { return aStorage.getItem(b); }), builtins, "c"); + + assert_equals(aStorage.length, builtins.length, "aStorage.length"); + }); +} diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_event_constructor_js.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_event_constructor_js.html new file mode 100644 index 000000000..3bfca4b13 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_event_constructor_js.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>event_Constructor</h1> + <div id="log"></div> + <script> + test(function() { + var t = async_test("StorageEvent constructor and nulls"); + function onStorageEvent(event) { + t.step(function() { + assert_equals(event.type, 'storage', 'type'); + assert_equals(event.key, null, 'key'); + assert_equals(event.oldValue, null, 'oldValue'); + assert_equals(event.newValue, null, 'newValue'); + assert_equals(event.url, 'null', 'url'); + assert_equals(event.storageArea, null, 'storageArea'); + }); + t.done(); + } + + window.addEventListener('storage', onStorageEvent, false); + var event = new StorageEvent('storage', { + key: null, + oldValue: null, + newValue: null, + url: null + }); + window.dispatchEvent(event); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_missing_arguments.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_missing_arguments.html new file mode 100644 index 000000000..984c60a12 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_missing_arguments.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>missing_arguments</h1> + <div id="log"></div> + <script> + var tests = [ + function() { localStorage.key(); }, + function() { localStorage.getItem(); }, + function() { localStorage.setItem(); }, + function() { localStorage.setItem("a"); }, + function() { localStorage.removeItem(); }, + function() { sessionStorage.key(); }, + function() { sessionStorage.getItem(); }, + function() { sessionStorage.setItem(); }, + function() { sessionStorage.setItem("a"); }, + function() { sessionStorage.removeItem(); }, + function() { new StorageEvent(); } + ]; + tests.forEach(function(fun) { + test(function() { + assert_throws(new TypeError(), fun); + }, "Should throw TypeError for " + format_value(fun) + "."); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_builtins.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_builtins.html new file mode 100644 index 000000000..204c9bd77 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_builtins.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="storage_builtins.js"></script> + </head> + <body> + <h1>storage_local_builtins</h1> + <div id="log"></div> + <script> + test_storage_builtins(localStorage); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_clear_js.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_clear_js.html new file mode 100644 index 000000000..84b4bd087 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_clear_js.html @@ -0,0 +1,25 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_local_clear</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + localStorage.setItem("name", "user1"); + + assert_not_equals(localStorage.name, undefined); + assert_equals(localStorage.length, 1); + + localStorage.clear(); + assert_equals(localStorage.name, undefined, "localStorage.name") + assert_equals(localStorage.length, 0, "localStorage.length") + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_getitem_js.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_getitem_js.html new file mode 100644 index 000000000..ecced7ff2 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_getitem_js.html @@ -0,0 +1,38 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_local_getItem</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + localStorage.setItem("undefined", "foo"); + localStorage.setItem("null", "bar"); + localStorage.setItem("", "baz"); + test(function() { + assert_equals(localStorage.length, 3); + }, "All 3 items should be added."); + test(function() { + assert_equals(localStorage["unknown"], undefined, "localStorage['unknown']") + assert_equals(localStorage["undefined"], "foo", "localStorage['undefined']") + assert_equals(localStorage["null"], "bar", "localStorage['null']") + assert_equals(localStorage[undefined], "foo", "localStorage[undefined]") + assert_equals(localStorage[null], "bar", "localStorage[null]") + assert_equals(localStorage[""], "baz", "localStorage['']") + }, "array access should be correct"); + test(function() { + assert_equals(localStorage.getItem("undefined"), "foo", "localStorage.getItem('undefined')") + assert_equals(localStorage.getItem("null"), "bar", "localStorage.getItem('null')") + assert_equals(localStorage.getItem(undefined), "foo", "localStorage.getItem(undefined)") + assert_equals(localStorage.getItem(null), "bar", "localStorage.getItem(null)") + assert_equals(localStorage.getItem(""), "baz", "localStorage.getItem('')") + }, "getItem should be correct") + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_in_js.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_in_js.html new file mode 100644 index 000000000..b3f35cd9d --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_in_js.html @@ -0,0 +1,29 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_local_in</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + assert_false("name" in localStorage); + localStorage["name"] = "user1"; + assert_true("name" in localStorage); + }); + test(function() { + localStorage.clear(); + assert_false("name" in localStorage); + localStorage.setItem("name", "user1"); + assert_true("name" in localStorage); + assert_equals(localStorage.name, "user1"); + localStorage.removeItem("name"); + assert_false("name" in localStorage); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_index_js.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_index_js.html new file mode 100644 index 000000000..71dabace1 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_index_js.html @@ -0,0 +1,36 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_local_index</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + localStorage["name"] = "user1"; + localStorage["age"] = "42"; + test(function() { + assert_equals(localStorage[-1], undefined); + assert_equals(localStorage[0], undefined); + assert_equals(localStorage[1], undefined); + assert_equals(localStorage[2], undefined); + }) + test(function() { + assert_equals(localStorage["-1"], undefined); + assert_equals(localStorage["0"], undefined); + assert_equals(localStorage["1"], undefined); + assert_equals(localStorage["2"], undefined); + }) + localStorage.setItem(1, "number"); + test(function() { + assert_equals(localStorage[1], "number"); + assert_equals(localStorage["1"], "number"); + }) + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_length_js.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_length_js.html new file mode 100644 index 000000000..445db5189 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_length_js.html @@ -0,0 +1,23 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_local_length</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + assert_equals(localStorage.length, 0, "localStorage.length") + + localStorage["name"] = "user1"; + localStorage["age"] = "20"; + + assert_equals(localStorage.length, 2, "localStorage.length") + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_removeitem_js.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_removeitem_js.html new file mode 100644 index 000000000..a8f10e80a --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_removeitem_js.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_local_removeItem</h1> + <div id="log"></div> + <script> + test(function() { + localStorage.clear(); + localStorage.setItem("name", "user1"); + assert_equals(localStorage.getItem("name"), "user1"); + + test(function() { + delete localStorage["name"]; + delete localStorage["unknown"]; + assert_equals(localStorage.getItem("name"), null, "localStorage.getItem('name')") + }); + test(function() { + localStorage.setItem("null", "test"); + assert_true("null" in localStorage); + localStorage.removeItem(null, "test"); + assert_false("null" in localStorage); + }); + test(function() { + localStorage.setItem("undefined", "test"); + assert_true("undefined" in localStorage); + localStorage.removeItem(undefined, "test"); + assert_false("undefined" in localStorage); + }); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_setitem_js.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_setitem_js.html new file mode 100644 index 000000000..9549fa52f --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_setitem_js.html @@ -0,0 +1,119 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_local_setItem</h1> + <div id="log"></div> + <script> + var test_error = { name: "test" }; + test(function() { + localStorage.clear(); + test(function() { + assert_false("name" in localStorage); + assert_false("age" in localStorage); + }); + test(function() { + localStorage["name"] = "user1"; + assert_true("name" in localStorage); + assert_equals(localStorage.length, 1, "localStorage.length") + assert_equals(localStorage.getItem("name"), "user1"); + assert_equals(localStorage["name"], "user1"); + }); + test(function() { + localStorage["name"] = "user2"; + assert_true("name" in localStorage); + assert_equals(localStorage.length, 1, "localStorage.length") + assert_equals(localStorage.getItem("name"), "user2"); + assert_equals(localStorage["name"], "user2"); + }); + test(function() { + localStorage.setItem("name", "user3"); + assert_true("name" in localStorage); + assert_equals(localStorage.length, 1, "localStorage.length") + assert_equals(localStorage.getItem("name"), "user3"); + assert_equals(localStorage["name"], "user3"); + }); + test(function() { + localStorage.setItem("age", null); + assert_true("age" in localStorage); + assert_equals(localStorage.length, 2, "localStorage.length") + assert_equals(localStorage.getItem("age"), "null"); + assert_equals(localStorage["age"], "null"); + }); + test(function() { + localStorage["age"] = null; + assert_true("age" in localStorage); + assert_equals(localStorage.length, 2, "localStorage.length") + assert_equals(localStorage.getItem("age"), "null"); + assert_equals(localStorage["age"], "null"); + }); + test(function() { + localStorage.setItem("age", undefined); + assert_true("age" in localStorage); + assert_equals(localStorage.length, 2, "localStorage.length") + assert_equals(localStorage.getItem("age"), "undefined"); + assert_equals(localStorage["age"], "undefined"); + }); + test(function() { + localStorage["age"] = undefined; + assert_true("age" in localStorage); + assert_equals(localStorage.length, 2, "localStorage.length") + assert_equals(localStorage.getItem("age"), "undefined"); + assert_equals(localStorage["age"], "undefined"); + }); + test(function() { + assert_throws(test_error, function() { + localStorage.setItem("age", + { toString: function() { throw test_error; } }); + }); + assert_true("age" in localStorage); + assert_equals(localStorage.length, 2, "localStorage.length") + assert_equals(localStorage.getItem("age"), "undefined"); + assert_equals(localStorage["age"], "undefined"); + }); + test(function() { + assert_throws(test_error, function() { + localStorage["age"] = + { toString: function() { throw test_error; } }; + }); + assert_true("age" in localStorage); + assert_equals(localStorage.length, 2, "localStorage.length") + assert_equals(localStorage.getItem("age"), "undefined"); + assert_equals(localStorage["age"], "undefined"); + }); + test(function() { + localStorage.setItem(undefined, "test"); + assert_true("undefined" in localStorage); + assert_equals(localStorage.length, 3, "localStorage.length") + assert_equals(localStorage.getItem("undefined"), "test"); + assert_equals(localStorage["undefined"], "test"); + }); + test(function() { + localStorage[undefined] = "test2"; + assert_true("undefined" in localStorage); + assert_equals(localStorage.length, 3, "localStorage.length") + assert_equals(localStorage.getItem("undefined"), "test2"); + assert_equals(localStorage["undefined"], "test2"); + }); + test(function() { + localStorage.setItem(null, "test"); + assert_true("null" in localStorage); + assert_equals(localStorage.length, 4, "localStorage.length") + assert_equals(localStorage.getItem("null"), "test"); + assert_equals(localStorage["null"], "test"); + }); + test(function() { + localStorage[null] = "test2"; + assert_true("null" in localStorage); + assert_equals(localStorage.length, 4, "localStorage.length") + assert_equals(localStorage.getItem("null"), "test2"); + assert_equals(localStorage["null"], "test2"); + }); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_builtins.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_builtins.html new file mode 100644 index 000000000..ad9ae642d --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_builtins.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="storage_builtins.js"></script> + </head> + <body> + <h1>storage_session_builtins</h1> + <div id="log"></div> + <script> + test_storage_builtins(sessionStorage); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_clear_js.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_clear_js.html new file mode 100644 index 000000000..a6805f7a8 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_clear_js.html @@ -0,0 +1,25 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_session_clear</h1> + <div id="log"></div> + <script> + test(function() { + sessionStorage.clear(); + sessionStorage.setItem("name", "user1"); + + assert_not_equals(sessionStorage.name, undefined); + assert_equals(sessionStorage.length, 1); + + sessionStorage.clear(); + assert_equals(sessionStorage.name, undefined, "sessionStorage.name") + assert_equals(sessionStorage.length, 0, "sessionStorage.length") + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_getitem_js.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_getitem_js.html new file mode 100644 index 000000000..bc37c47e9 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_getitem_js.html @@ -0,0 +1,38 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_session_getItem</h1> + <div id="log"></div> + <script> + test(function() { + sessionStorage.clear(); + sessionStorage.setItem("undefined", "foo"); + sessionStorage.setItem("null", "bar"); + sessionStorage.setItem("", "baz"); + test(function() { + assert_equals(sessionStorage.length, 3); + }, "All 3 items should be added."); + test(function() { + assert_equals(sessionStorage["unknown"], undefined, "sessionStorage['unknown']") + assert_equals(sessionStorage["undefined"], "foo", "sessionStorage['undefined']") + assert_equals(sessionStorage["null"], "bar", "sessionStorage['null']") + assert_equals(sessionStorage[undefined], "foo", "sessionStorage[undefined]") + assert_equals(sessionStorage[null], "bar", "sessionStorage[null]") + assert_equals(sessionStorage[""], "baz", "sessionStorage['']") + }, "array access should be correct"); + test(function() { + assert_equals(sessionStorage.getItem("undefined"), "foo", "sessionStorage.getItem('undefined')") + assert_equals(sessionStorage.getItem("null"), "bar", "sessionStorage.getItem('null')") + assert_equals(sessionStorage.getItem(undefined), "foo", "sessionStorage.getItem(undefined)") + assert_equals(sessionStorage.getItem(null), "bar", "sessionStorage.getItem(null)") + assert_equals(sessionStorage.getItem(""), "baz", "sessionStorage.getItem('')") + }, "getItem should be correct") + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_in_js.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_in_js.html new file mode 100644 index 000000000..a285357d2 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_in_js.html @@ -0,0 +1,29 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_session_in</h1> + <div id="log"></div> + <script> + test(function() { + sessionStorage.clear(); + assert_false("name" in sessionStorage); + sessionStorage["name"] = "user1"; + assert_true("name" in sessionStorage); + }); + test(function() { + sessionStorage.clear(); + assert_false("name" in sessionStorage); + sessionStorage.setItem("name", "user1"); + assert_true("name" in sessionStorage); + assert_equals(sessionStorage.name, "user1"); + sessionStorage.removeItem("name"); + assert_false("name" in sessionStorage); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_index_js.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_index_js.html new file mode 100644 index 000000000..1aec8715a --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_index_js.html @@ -0,0 +1,36 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_session_index</h1> + <div id="log"></div> + <script> + test(function() { + sessionStorage.clear(); + sessionStorage["name"] = "user1"; + sessionStorage["age"] = "42"; + test(function() { + assert_equals(sessionStorage[-1], undefined); + assert_equals(sessionStorage[0], undefined); + assert_equals(sessionStorage[1], undefined); + assert_equals(sessionStorage[2], undefined); + }) + test(function() { + assert_equals(sessionStorage["-1"], undefined); + assert_equals(sessionStorage["0"], undefined); + assert_equals(sessionStorage["1"], undefined); + assert_equals(sessionStorage["2"], undefined); + }) + sessionStorage.setItem(1, "number"); + test(function() { + assert_equals(sessionStorage[1], "number"); + assert_equals(sessionStorage["1"], "number"); + }) + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_length_js.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_length_js.html new file mode 100644 index 000000000..50e05a26d --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_length_js.html @@ -0,0 +1,23 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_session_length</h1> + <div id="log"></div> + <script> + test(function() { + sessionStorage.clear(); + assert_equals(sessionStorage.length, 0, "sessionStorage.length") + + sessionStorage["name"] = "user1"; + sessionStorage["age"] = "20"; + + assert_equals(sessionStorage.length, 2, "sessionStorage.length") + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_removeitem_js.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_removeitem_js.html new file mode 100644 index 000000000..5012cffb7 --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_removeitem_js.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_session_removeItem</h1> + <div id="log"></div> + <script> + test(function() { + sessionStorage.clear(); + sessionStorage.setItem("name", "user1"); + assert_equals(sessionStorage.getItem("name"), "user1"); + + test(function() { + delete sessionStorage["name"]; + delete sessionStorage["unknown"]; + assert_equals(sessionStorage.getItem("name"), null, "sessionStorage.getItem('name')") + }); + test(function() { + sessionStorage.setItem("null", "test"); + assert_true("null" in sessionStorage); + sessionStorage.removeItem(null, "test"); + assert_false("null" in sessionStorage); + }); + test(function() { + sessionStorage.setItem("undefined", "test"); + assert_true("undefined" in sessionStorage); + sessionStorage.removeItem(undefined, "test"); + assert_false("undefined" in sessionStorage); + }); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_setitem_js.html b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_setitem_js.html new file mode 100644 index 000000000..8f2339c5f --- /dev/null +++ b/dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_setitem_js.html @@ -0,0 +1,119 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Web Storage</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <h1>storage_session_setItem</h1> + <div id="log"></div> + <script> + var test_error = { name: "test" }; + test(function() { + sessionStorage.clear(); + test(function() { + assert_false("name" in sessionStorage); + assert_false("age" in sessionStorage); + }); + test(function() { + sessionStorage["name"] = "user1"; + assert_true("name" in sessionStorage); + assert_equals(sessionStorage.length, 1, "sessionStorage.length") + assert_equals(sessionStorage.getItem("name"), "user1"); + assert_equals(sessionStorage["name"], "user1"); + }); + test(function() { + sessionStorage["name"] = "user2"; + assert_true("name" in sessionStorage); + assert_equals(sessionStorage.length, 1, "sessionStorage.length") + assert_equals(sessionStorage.getItem("name"), "user2"); + assert_equals(sessionStorage["name"], "user2"); + }); + test(function() { + sessionStorage.setItem("name", "user3"); + assert_true("name" in sessionStorage); + assert_equals(sessionStorage.length, 1, "sessionStorage.length") + assert_equals(sessionStorage.getItem("name"), "user3"); + assert_equals(sessionStorage["name"], "user3"); + }); + test(function() { + sessionStorage.setItem("age", null); + assert_true("age" in sessionStorage); + assert_equals(sessionStorage.length, 2, "sessionStorage.length") + assert_equals(sessionStorage.getItem("age"), "null"); + assert_equals(sessionStorage["age"], "null"); + }); + test(function() { + sessionStorage["age"] = null; + assert_true("age" in sessionStorage); + assert_equals(sessionStorage.length, 2, "sessionStorage.length") + assert_equals(sessionStorage.getItem("age"), "null"); + assert_equals(sessionStorage["age"], "null"); + }); + test(function() { + sessionStorage.setItem("age", undefined); + assert_true("age" in sessionStorage); + assert_equals(sessionStorage.length, 2, "sessionStorage.length") + assert_equals(sessionStorage.getItem("age"), "undefined"); + assert_equals(sessionStorage["age"], "undefined"); + }); + test(function() { + sessionStorage["age"] = undefined; + assert_true("age" in sessionStorage); + assert_equals(sessionStorage.length, 2, "sessionStorage.length") + assert_equals(sessionStorage.getItem("age"), "undefined"); + assert_equals(sessionStorage["age"], "undefined"); + }); + test(function() { + assert_throws(test_error, function() { + sessionStorage.setItem("age", + { toString: function() { throw test_error; } }); + }); + assert_true("age" in sessionStorage); + assert_equals(sessionStorage.length, 2, "sessionStorage.length") + assert_equals(sessionStorage.getItem("age"), "undefined"); + assert_equals(sessionStorage["age"], "undefined"); + }); + test(function() { + assert_throws(test_error, function() { + sessionStorage["age"] = + { toString: function() { throw test_error; } }; + }); + assert_true("age" in sessionStorage); + assert_equals(sessionStorage.length, 2, "sessionStorage.length") + assert_equals(sessionStorage.getItem("age"), "undefined"); + assert_equals(sessionStorage["age"], "undefined"); + }); + test(function() { + sessionStorage.setItem(undefined, "test"); + assert_true("undefined" in sessionStorage); + assert_equals(sessionStorage.length, 3, "sessionStorage.length") + assert_equals(sessionStorage.getItem("undefined"), "test"); + assert_equals(sessionStorage["undefined"], "test"); + }); + test(function() { + sessionStorage[undefined] = "test2"; + assert_true("undefined" in sessionStorage); + assert_equals(sessionStorage.length, 3, "sessionStorage.length") + assert_equals(sessionStorage.getItem("undefined"), "test2"); + assert_equals(sessionStorage["undefined"], "test2"); + }); + test(function() { + sessionStorage.setItem(null, "test"); + assert_true("null" in sessionStorage); + assert_equals(sessionStorage.length, 4, "sessionStorage.length") + assert_equals(sessionStorage.getItem("null"), "test"); + assert_equals(sessionStorage["null"], "test"); + }); + test(function() { + sessionStorage[null] = "test2"; + assert_true("null" in sessionStorage); + assert_equals(sessionStorage.length, 4, "sessionStorage.length") + assert_equals(sessionStorage.getItem("null"), "test2"); + assert_equals(sessionStorage["null"], "test2"); + }); + }); + </script> + </body> +</html> diff --git a/dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_FormData-append.html b/dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_FormData-append.html new file mode 100644 index 000000000..b05c5169b --- /dev/null +++ b/dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_FormData-append.html @@ -0,0 +1,14 @@ +<!doctype html> +<meta charset=utf-8> +<title>FormData.append</title> +<link rel=help href=http://xhr.spec.whatwg.org/#dom-formdata-append> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<div id=log></div> +<script> +test(function() { + var fd = new FormData(); + fd.append("name", new String("value")); + // TODO: test that it actually worked. +}, "Passing a String object to FormData.append should work."); +</script> diff --git a/dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_interfaces.html b/dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_interfaces.html new file mode 100644 index 000000000..165d013d2 --- /dev/null +++ b/dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_interfaces.html @@ -0,0 +1,102 @@ +<!doctype html> +<meta charset=utf-8> +<title>XMLHttpRequest IDL tests</title> +<div id=log></div> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=/resources/WebIDLParser.js></script> +<script src=/resources/idlharness.js></script> +<script type=text/plain class=untested> +interface EventTarget { + void addEventListener(DOMString type, EventListener? callback, optional boolean capture /* = false */); + void removeEventListener(DOMString type, EventListener? callback, optional boolean capture /* = false */); + boolean dispatchEvent(Event event); +}; +</script> +<script type=text/plain> +[NoInterfaceObject] +interface XMLHttpRequestEventTarget : EventTarget { + // event handlers + [TreatNonCallableAsNull] attribute Function? onloadstart; + [TreatNonCallableAsNull] attribute Function? onprogress; + [TreatNonCallableAsNull] attribute Function? onabort; + [TreatNonCallableAsNull] attribute Function? onerror; + [TreatNonCallableAsNull] attribute Function? onload; + [TreatNonCallableAsNull] attribute Function? ontimeout; + [TreatNonCallableAsNull] attribute Function? onloadend; +}; + +interface XMLHttpRequestUpload : XMLHttpRequestEventTarget { + +}; + +/* +enum XMLHttpRequestResponseType { + "", + "arraybuffer", + "blob", + "document", + "json", + "text" +}; +*/ + +[Constructor] +interface XMLHttpRequest : XMLHttpRequestEventTarget { + // event handler + [TreatNonCallableAsNull] attribute Function? onreadystatechange; + + // states + const unsigned short UNSENT = 0; + const unsigned short OPENED = 1; + const unsigned short HEADERS_RECEIVED = 2; + const unsigned short LOADING = 3; + const unsigned short DONE = 4; + readonly attribute unsigned short readyState; + + // request + void open(DOMString method, DOMString url, optional boolean async/* = true*/, optional DOMString? user, optional DOMString? password); + void setRequestHeader(DOMString header, DOMString value); + attribute unsigned long timeout; + attribute boolean withCredentials; + readonly attribute XMLHttpRequestUpload upload; + void send(optional (ArrayBufferView or Blob or Document or DOMString or FormData)? data/* = null*/); + void abort(); + + // response + readonly attribute unsigned short status; + readonly attribute DOMString statusText; + DOMString? getResponseHeader(DOMString header); + DOMString getAllResponseHeaders(); + void overrideMimeType(DOMString mime); +/* attribute XMLHttpRequestResponseType responseType; */ + readonly attribute any response; + readonly attribute DOMString responseText; + readonly attribute Document? responseXML; +}; + +[Constructor, + Constructor(HTMLFormElement form)] +interface FormData { + void append(DOMString name, Blob value, optional DOMString filename); + void append(DOMString name, DOMString value); +}; +</script> +<script> +"use strict"; +var form = document.createElement("form"); +var idlArray = new IdlArray(); +[].forEach.call(document.querySelectorAll("script[type=text\\/plain]"), function(node) { + if (node.className == "untested") { + idlArray.add_untested_idls(node.textContent); + } else { + idlArray.add_idls(node.textContent); + } +}); +idlArray.add_objects({ + XMLHttpRequest: ['new XMLHttpRequest()'], + XMLHttpRequestUpload: ['(new XMLHttpRequest()).upload'], + FormData: ['new FormData()', 'new FormData(form)'] +}); +idlArray.test(); +</script> diff --git a/dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_setrequestheader-invalid-arguments.htm b/dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_setrequestheader-invalid-arguments.htm new file mode 100644 index 000000000..a077ad95f --- /dev/null +++ b/dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_setrequestheader-invalid-arguments.htm @@ -0,0 +1,42 @@ +<!doctype html> +<html> + <head> + <title>XMLHttpRequest: setRequestHeader() with invalid arguments</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <div id="log"></div> +<!-- + CHAR = <any US-ASCII character (octets 0 - 127)> + CTL = <any US-ASCII control character + (octets 0 - 31) and DEL (127)> + SP = <US-ASCII SP, space (32)> + HT = <US-ASCII HT, horizontal-tab (9)> + token = 1*<any CHAR except CTLs or separators> + separators = "(" | ")" | "<" | ">" | "@" + | "," | ";" | ":" | "\" | <"> + | "/" | "[" | "]" | "?" | "=" + | "{" | "}" | SP | HT + field-name = token +--> + <script> + var invalid_headers = ["(", ")", "<", ">", "@", ",", ";", ":", "\\", + "\"", "/", "[", "]", "?", "=", "{", "}", " ", + "\u0009", "\u007f"] + for (var i = 0; i < 32; ++i) { + invalid_headers.push(String.fromCharCode(i)) + } + for (var i = 0; i < invalid_headers.length; ++i) { + test(function() { + assert_throws("SYNTAX_ERR", function() { + var client = new XMLHttpRequest() + client.open("GET", "../resources/delay.php?ms=0") + client.setRequestHeader(invalid_headers[i], "test") + }, "setRequestHeader should throw with header " + + format_value(invalid_headers[i]) +".") + }) + } + </script> + </body> +</html> diff --git a/dom/imptests/webapps/mochitest.ini b/dom/imptests/webapps/mochitest.ini new file mode 100644 index 000000000..0eea5c631 --- /dev/null +++ b/dom/imptests/webapps/mochitest.ini @@ -0,0 +1,60 @@ +# THIS FILE IS AUTOGENERATED BY importTestsuite.py - DO NOT EDIT +[DEFAULT] +support-files = + WebStorage/tests/submissions/Infraware/iframe/local_change_item_iframe.html + WebStorage/tests/submissions/Infraware/iframe/local_security_iframe.html + WebStorage/tests/submissions/Infraware/iframe/local_set_item_clear_iframe.html + WebStorage/tests/submissions/Infraware/iframe/local_set_item_iframe.html + WebStorage/tests/submissions/Infraware/iframe/session_change_item_iframe.html + WebStorage/tests/submissions/Infraware/iframe/session_set_item_clear_iframe.html + WebStorage/tests/submissions/Infraware/iframe/session_set_item_iframe.html + WebStorage/tests/submissions/Ms2ger/storage_builtins.js + +[WebStorage/tests/submissions/Infraware/test_event_constructor.html] +[WebStorage/tests/submissions/Infraware/test_event_local_key.html] +[WebStorage/tests/submissions/Infraware/test_event_local_newvalue.html] +[WebStorage/tests/submissions/Infraware/test_event_local_oldvalue.html] +[WebStorage/tests/submissions/Infraware/test_event_local_storagearea.html] +[WebStorage/tests/submissions/Infraware/test_event_local_storageeventinit.html] +[WebStorage/tests/submissions/Infraware/test_event_local_url.html] +[WebStorage/tests/submissions/Infraware/test_event_session_key.html] +[WebStorage/tests/submissions/Infraware/test_event_session_newvalue.html] +[WebStorage/tests/submissions/Infraware/test_event_session_oldvalue.html] +[WebStorage/tests/submissions/Infraware/test_event_session_storagearea.html] +[WebStorage/tests/submissions/Infraware/test_event_session_storageeventinit.html] +[WebStorage/tests/submissions/Infraware/test_event_session_url.html] +[WebStorage/tests/submissions/Infraware/test_storage_local_clear.html] +[WebStorage/tests/submissions/Infraware/test_storage_local_getitem.html] +[WebStorage/tests/submissions/Infraware/test_storage_local_key.html] +skip-if = toolkit == 'android' #bug 775227 +[WebStorage/tests/submissions/Infraware/test_storage_local_length.html] +[WebStorage/tests/submissions/Infraware/test_storage_local_removeitem.html] +[WebStorage/tests/submissions/Infraware/test_storage_local_security.html] +[WebStorage/tests/submissions/Infraware/test_storage_local_setitem.html] +[WebStorage/tests/submissions/Infraware/test_storage_session_clear.html] +[WebStorage/tests/submissions/Infraware/test_storage_session_getitem.html] +[WebStorage/tests/submissions/Infraware/test_storage_session_key.html] +[WebStorage/tests/submissions/Infraware/test_storage_session_length.html] +[WebStorage/tests/submissions/Infraware/test_storage_session_removeitem.html] +[WebStorage/tests/submissions/Infraware/test_storage_session_setitem.html] +[WebStorage/tests/submissions/Ms2ger/test_event_constructor_js.html] +[WebStorage/tests/submissions/Ms2ger/test_missing_arguments.html] +[WebStorage/tests/submissions/Ms2ger/test_storage_local_builtins.html] +[WebStorage/tests/submissions/Ms2ger/test_storage_local_clear_js.html] +[WebStorage/tests/submissions/Ms2ger/test_storage_local_getitem_js.html] +[WebStorage/tests/submissions/Ms2ger/test_storage_local_in_js.html] +[WebStorage/tests/submissions/Ms2ger/test_storage_local_index_js.html] +[WebStorage/tests/submissions/Ms2ger/test_storage_local_length_js.html] +[WebStorage/tests/submissions/Ms2ger/test_storage_local_removeitem_js.html] +[WebStorage/tests/submissions/Ms2ger/test_storage_local_setitem_js.html] +[WebStorage/tests/submissions/Ms2ger/test_storage_session_builtins.html] +[WebStorage/tests/submissions/Ms2ger/test_storage_session_clear_js.html] +[WebStorage/tests/submissions/Ms2ger/test_storage_session_getitem_js.html] +[WebStorage/tests/submissions/Ms2ger/test_storage_session_in_js.html] +[WebStorage/tests/submissions/Ms2ger/test_storage_session_index_js.html] +[WebStorage/tests/submissions/Ms2ger/test_storage_session_length_js.html] +[WebStorage/tests/submissions/Ms2ger/test_storage_session_removeitem_js.html] +[WebStorage/tests/submissions/Ms2ger/test_storage_session_setitem_js.html] +[XMLHttpRequest/tests/submissions/Ms2ger/test_FormData-append.html] +[XMLHttpRequest/tests/submissions/Ms2ger/test_interfaces.html] +[XMLHttpRequest/tests/submissions/Ms2ger/test_setrequestheader-invalid-arguments.htm] diff --git a/dom/imptests/writeBuildFiles.py b/dom/imptests/writeBuildFiles.py new file mode 100644 index 000000000..5a12add83 --- /dev/null +++ b/dom/imptests/writeBuildFiles.py @@ -0,0 +1,43 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. + +from __future__ import unicode_literals + +import string + +manifest_template = """# THIS FILE IS AUTOGENERATED BY ${caller} - DO NOT EDIT +[DEFAULT] +support-files = +${supportfiles} + +${tests} +""" + +reftest_template = """# THIS FILE IS AUTOGENERATED BY ${caller} - DO NOT EDIT + +${reftests} +""" + + + +def substManifest(caller, test_files, support_files): + test_files = [f.lstrip('/') for f in test_files] + support_files = [f.lstrip('/') for f in support_files] + + return string.Template(manifest_template).substitute({ + 'caller': caller, + 'supportfiles': '\n'.join(' %s' % f for f in sorted(support_files)), + 'tests': '\n'.join('[%s]' % f for f in sorted(test_files)) + }) + + +def substReftestList(caller, tests): + def reftests(tests): + return "\n".join(" ".join(line) for line in tests) + + return string.Template(reftest_template).substitute({ + "caller": caller, + "reftests": reftests(tests), + }) + |