summaryrefslogtreecommitdiff
path: root/dom/imptests
diff options
context:
space:
mode:
Diffstat (limited to 'dom/imptests')
-rw-r--r--dom/imptests/README106
-rw-r--r--dom/imptests/WebIDLParser.js924
-rw-r--r--dom/imptests/editing/mochitest.ini26
-rw-r--r--dom/imptests/failures/html/dom/nodes/mochitest.ini13
-rw-r--r--dom/imptests/failures/html/dom/ranges/mochitest.ini7
-rw-r--r--dom/imptests/failures/html/dom/ranges/test_Range-insertNode.html.json6
-rw-r--r--dom/imptests/failures/html/dom/ranges/test_Range-surroundContents.html.json44
-rw-r--r--dom/imptests/failures/html/dom/test_historical.html.json10
-rw-r--r--dom/imptests/failures/html/html/browsers/the-window-object/test_window-named-properties.html.json2
-rw-r--r--dom/imptests/failures/html/html/browsers/the-window-object/test_window-properties.html.json31
-rw-r--r--dom/imptests/failures/html/html/dom/documents/dta/mochitest.ini11
-rw-r--r--dom/imptests/failures/html/html/dom/documents/dta/test_nameditem-06.html.json3
-rw-r--r--dom/imptests/failures/html/typedarrays/mochitest.ini6
-rw-r--r--dom/imptests/failures/html/typedarrays/test_constructors.html.json147
-rw-r--r--dom/imptests/failures/webapps/WebStorage/tests/submissions/Infraware/mochitest.ini6
-rw-r--r--dom/imptests/failures/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_security.html.json3
-rw-r--r--dom/imptests/html.txt3
-rw-r--r--dom/imptests/html/dom/common.js1087
-rw-r--r--dom/imptests/html/dom/test_interface-objects.html45
-rw-r--r--dom/imptests/html/dom/test_interfaces.html455
-rw-r--r--dom/imptests/html/html/browsers/the-window-object/test_window-named-properties.html69
-rw-r--r--dom/imptests/html/html/dom/documents/dta/test_nameditem-06.html104
-rw-r--r--dom/imptests/html/html/dom/elements/global-attributes/reftest-stylo.list59
-rw-r--r--dom/imptests/html/html/dom/elements/global-attributes/reftest.list56
-rw-r--r--dom/imptests/html/js/builtins/test_WeakMap.prototype-properties.html103
-rw-r--r--dom/imptests/html/mochitest.ini24
-rw-r--r--dom/imptests/html/typedarrays/test_constructors.html48
-rw-r--r--dom/imptests/html/webgl/common.js13
-rw-r--r--dom/imptests/html/webgl/test_bufferSubData.html26
-rw-r--r--dom/imptests/html/webgl/test_compressedTexImage2D.html30
-rw-r--r--dom/imptests/html/webgl/test_compressedTexSubImage2D.html30
-rw-r--r--dom/imptests/html/webgl/test_texImage2D.html20
-rw-r--r--dom/imptests/html/webgl/test_texSubImage2D.html20
-rw-r--r--dom/imptests/html/webgl/test_uniformMatrixNfv.html33
-rw-r--r--dom/imptests/idlharness.js1706
-rw-r--r--dom/imptests/importTestsuite.py189
-rw-r--r--dom/imptests/moz.build23
-rw-r--r--dom/imptests/parseFailures.py79
-rw-r--r--dom/imptests/parseManifest.py69
-rw-r--r--dom/imptests/testharness.css102
-rw-r--r--dom/imptests/testharness.js2657
-rw-r--r--dom/imptests/testharnessreport.js312
-rw-r--r--dom/imptests/updateTestharness.py25
-rw-r--r--dom/imptests/webapps.txt3
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/local_change_item_iframe.html18
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/local_security_iframe.html16
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/local_set_item_clear_iframe.html17
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/local_set_item_iframe.html16
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/session_change_item_iframe.html18
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/session_set_item_clear_iframe.html17
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/iframe/session_set_item_iframe.html16
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_constructor.html32
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_key.html42
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_newvalue.html42
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_oldvalue.html42
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_storagearea.html43
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_storageeventinit.html39
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_local_url.html47
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_key.html42
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_newvalue.html42
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_oldvalue.html42
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_storagearea.html43
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_storageeventinit.html39
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_event_session_url.html47
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_clear.html25
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_getitem.html23
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_key.html40
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_length.html23
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_removeitem.html23
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_security.html32
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_setitem.html19
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_clear.html25
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_getitem.html23
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_key.html40
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_length.html23
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_removeitem.html23
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_session_setitem.html19
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/storage_builtins.js15
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_event_constructor_js.html37
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_missing_arguments.html32
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_builtins.html16
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_clear_js.html25
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_getitem_js.html38
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_in_js.html29
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_index_js.html36
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_length_js.html23
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_removeitem_js.html37
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_local_setitem_js.html119
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_builtins.html16
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_clear_js.html25
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_getitem_js.html38
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_in_js.html29
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_index_js.html36
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_length_js.html23
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_removeitem_js.html37
-rw-r--r--dom/imptests/webapps/WebStorage/tests/submissions/Ms2ger/test_storage_session_setitem_js.html119
-rw-r--r--dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_FormData-append.html14
-rw-r--r--dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_interfaces.html102
-rw-r--r--dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_setrequestheader-invalid-arguments.htm42
-rw-r--r--dom/imptests/webapps/mochitest.ini60
-rw-r--r--dom/imptests/writeBuildFiles.py43
101 files changed, 10724 insertions, 0 deletions
diff --git a/dom/imptests/README b/dom/imptests/README
new file mode 100644
index 0000000000..13906cef97
--- /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 0000000000..103a7f48bd
--- /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 0000000000..b5c82f5d49
--- /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 0000000000..4fb17f193b
--- /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 0000000000..da0d1a7711
--- /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 0000000000..a5ac1621d2
--- /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 0000000000..720ee5edb6
--- /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 0000000000..42e78b23d5
--- /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 0000000000..2c63c08510
--- /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 0000000000..b34f6573fa
--- /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 0000000000..6777d58162
--- /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 0000000000..504194194b
--- /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 0000000000..90d5fe8020
--- /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 0000000000..046fde96de
--- /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 0000000000..f17aa2160f
--- /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 0000000000..ea1a73ebfc
--- /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 0000000000..2d06371d86
--- /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 0000000000..dc7528a9a9
--- /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 0000000000..0e2bf1acdd
--- /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 0000000000..9299ec8b7b
--- /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 0000000000..b01e24da98
--- /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 0000000000..3fd3d474a4
--- /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 0000000000..4e76b85322
--- /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 0000000000..a58929e921
--- /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 0000000000..0bdb6bb86d
--- /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 0000000000..87b958f0e4
--- /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 0000000000..60e0dc8fe8
--- /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 0000000000..416c21ce93
--- /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 0000000000..a97df9062d
--- /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 0000000000..b0a031add0
--- /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 0000000000..539f9e17f8
--- /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 0000000000..2f769160df
--- /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 0000000000..294b30c760
--- /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 0000000000..f75cbcb997
--- /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 0000000000..8e41703e65
--- /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 0000000000..c66b28fb32
--- /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 0000000000..595e58ce78
--- /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 0000000000..6824b836c6
--- /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 0000000000..94412122d6
--- /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 0000000000..e2ed5a043f
--- /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 0000000000..f4c66aae62
--- /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, "&amp;")
+ .replace(/</g, "&lt;")
+ .replace(/"/g, "&quot;")
+ .replace(/'/g, "&#39;");
+ }
+
+ 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 0000000000..6bc7ec3ab6
--- /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 0000000000..42deadcd4b
--- /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 0000000000..25172d6997
--- /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 0000000000..19505615db
--- /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 0000000000..bc2fad3abf
--- /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 0000000000..74a6285a2a
--- /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 0000000000..bacb3b2bb5
--- /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 0000000000..6c86f58edd
--- /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 0000000000..a305f21a9c
--- /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 0000000000..28d7059c39
--- /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 0000000000..cc543c7fc9
--- /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 0000000000..534f92f3b0
--- /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 0000000000..91a8c504cb
--- /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 0000000000..faf434001f
--- /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 0000000000..abe0113a82
--- /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 0000000000..36e5a19e82
--- /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 0000000000..8a4e6fb535
--- /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 0000000000..f976a67325
--- /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 0000000000..b2358d323a
--- /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 0000000000..b81c30921a
--- /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 0000000000..f478e6d7f8
--- /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 0000000000..a2d2c1a736
--- /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 0000000000..c4842bedc8
--- /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 0000000000..39fd6d63a3
--- /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 0000000000..a6872ac946
--- /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 0000000000..7ec0acba47
--- /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 0000000000..cf58cc7f4a
--- /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 0000000000..67d336a006
--- /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 0000000000..4833d8a175
--- /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 0000000000..1225d8a7b7
--- /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 0000000000..2d63a259a0
--- /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 0000000000..8f0b044529
--- /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 0000000000..31911f99cb
--- /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 0000000000..daf06ccf60
--- /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 0000000000..7601f1e6ed
--- /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 0000000000..130d61e524
--- /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 0000000000..9c2f1a5ff3
--- /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 0000000000..3bfca4b138
--- /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 0000000000..984c60a122
--- /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 0000000000..204c9bd77f
--- /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 0000000000..84b4bd0871
--- /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 0000000000..ecced7ff21
--- /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 0000000000..b3f35cd9d9
--- /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 0000000000..71dabace16
--- /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 0000000000..445db51892
--- /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 0000000000..a8f10e80a4
--- /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 0000000000..9549fa52f4
--- /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 0000000000..ad9ae642d8
--- /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 0000000000..a6805f7a84
--- /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 0000000000..bc37c47e9e
--- /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 0000000000..a285357d29
--- /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 0000000000..1aec8715ac
--- /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 0000000000..50e05a26de
--- /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 0000000000..5012cffb79
--- /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 0000000000..8f2339c5f4
--- /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 0000000000..b05c5169b5
--- /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 0000000000..165d013d29
--- /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 0000000000..a077ad95f2
--- /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 0000000000..0eea5c631b
--- /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 0000000000..5a12add838
--- /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),
+ })
+