mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Bug 1104433 part 2 - Update dom/imptests/testharness.js and friends; rs=Ms2ger
This commit is contained in:
parent
9a78398b6b
commit
42cc797d1e
@ -31,7 +31,7 @@
|
|||||||
return tokens;
|
return tokens;
|
||||||
};
|
};
|
||||||
|
|
||||||
var parse = function (tokens) {
|
var parse = function (tokens, opt) {
|
||||||
var line = 1;
|
var line = 1;
|
||||||
tokens = tokens.slice();
|
tokens = tokens.slice();
|
||||||
|
|
||||||
@ -82,14 +82,41 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var all_ws = function () {
|
var all_ws = function (store, pea) { // pea == post extended attribute, tpea = same for types
|
||||||
var t = { type: "whitespace", value: "" };
|
var t = { type: "whitespace", value: "" };
|
||||||
while (true) {
|
while (true) {
|
||||||
var w = ws();
|
var w = ws();
|
||||||
if (!w) break;
|
if (!w) break;
|
||||||
t.value += w.value;
|
t.value += w.value;
|
||||||
}
|
}
|
||||||
if (t.value.length > 0) return t;
|
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 integer_type = function () {
|
||||||
@ -151,8 +178,15 @@
|
|||||||
else if (consume(OTHER, "[")) {
|
else if (consume(OTHER, "[")) {
|
||||||
all_ws();
|
all_ws();
|
||||||
consume(OTHER, "]") || error("Unterminated array type");
|
consume(OTHER, "]") || error("Unterminated array type");
|
||||||
if (!obj.array) obj.array = 1;
|
if (!obj.array) {
|
||||||
else obj.array++;
|
obj.array = 1;
|
||||||
|
obj.nullableArray = [obj.nullable];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
obj.array++;
|
||||||
|
obj.nullableArray.push(obj.nullable);
|
||||||
|
}
|
||||||
|
obj.nullable = false;
|
||||||
}
|
}
|
||||||
else return;
|
else return;
|
||||||
}
|
}
|
||||||
@ -160,40 +194,46 @@
|
|||||||
|
|
||||||
var single_type = function () {
|
var single_type = function () {
|
||||||
var prim = primitive_type()
|
var prim = primitive_type()
|
||||||
, ret = { sequence: false, nullable: false, array: false, union: false }
|
, ret = { sequence: false, generic: null, nullable: false, array: false, union: false }
|
||||||
|
, name
|
||||||
|
, value
|
||||||
;
|
;
|
||||||
if (prim) {
|
if (prim) {
|
||||||
ret.idlType = prim;
|
ret.idlType = prim;
|
||||||
}
|
}
|
||||||
else if (consume(ID, "sequence")) {
|
else if (name = consume(ID)) {
|
||||||
|
value = name.value;
|
||||||
all_ws();
|
all_ws();
|
||||||
if (!consume(OTHER, "<")) {
|
// Generic types
|
||||||
ret.idlType = "sequence";
|
if (consume(OTHER, "<")) {
|
||||||
}
|
// backwards compat
|
||||||
else {
|
if (value === "sequence") {
|
||||||
ret.sequence = true;
|
ret.sequence = true;
|
||||||
ret.idlType = type() || error("Error parsing sequence type");
|
}
|
||||||
|
ret.generic = value;
|
||||||
|
ret.idlType = type() || error("Error parsing generic type " + value);
|
||||||
all_ws();
|
all_ws();
|
||||||
if (!consume(OTHER, ">")) error("Unterminated sequence");
|
if (!consume(OTHER, ">")) error("Unterminated generic type " + value);
|
||||||
all_ws();
|
all_ws();
|
||||||
if (consume(OTHER, "?")) ret.nullable = true;
|
if (consume(OTHER, "?")) ret.nullable = true;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
ret.idlType = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var name = consume(ID);
|
return;
|
||||||
if (!name) return;
|
|
||||||
ret.idlType = name.value;
|
|
||||||
}
|
}
|
||||||
type_suffix(ret);
|
type_suffix(ret);
|
||||||
if (ret.nullable && ret.idlType === "any") error("Type any cannot be made nullable");
|
if (ret.nullable && !ret.array && ret.idlType === "any") error("Type any cannot be made nullable");
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
var union_type = function () {
|
var union_type = function () {
|
||||||
all_ws();
|
all_ws();
|
||||||
if (!consume(OTHER, "(")) return;
|
if (!consume(OTHER, "(")) return;
|
||||||
var ret = { sequence: false, nullable: false, array: false, union: true, idlType: [] };
|
var ret = { sequence: false, generic: null, nullable: false, array: false, union: true, idlType: [] };
|
||||||
var fst = type() || error("Union type with no content");
|
var fst = type() || error("Union type with no content");
|
||||||
ret.idlType.push(fst);
|
ret.idlType.push(fst);
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -211,16 +251,21 @@
|
|||||||
return single_type() || union_type();
|
return single_type() || union_type();
|
||||||
};
|
};
|
||||||
|
|
||||||
var argument = function () {
|
var argument = function (store) {
|
||||||
var ret = { optional: false, variadic: false };
|
var ret = { optional: false, variadic: false };
|
||||||
ret.extAttrs = extended_attrs();
|
ret.extAttrs = extended_attrs(store);
|
||||||
all_ws();
|
all_ws(store, "pea");
|
||||||
if (consume(ID, "optional")) {
|
var opt_token = consume(ID, "optional");
|
||||||
|
if (opt_token) {
|
||||||
ret.optional = true;
|
ret.optional = true;
|
||||||
all_ws();
|
all_ws();
|
||||||
}
|
}
|
||||||
ret.idlType = type();
|
ret.idlType = type();
|
||||||
if (!ret.idlType) return;
|
if (!ret.idlType) {
|
||||||
|
if (opt_token) tokens.unshift(opt_token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var type_token = last_token;
|
||||||
if (!ret.optional) {
|
if (!ret.optional) {
|
||||||
all_ws();
|
all_ws();
|
||||||
if (tokens.length >= 3 &&
|
if (tokens.length >= 3 &&
|
||||||
@ -235,7 +280,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
all_ws();
|
all_ws();
|
||||||
var name = consume(ID) || error("No name in argument");
|
var name = consume(ID);
|
||||||
|
if (!name) {
|
||||||
|
if (opt_token) tokens.unshift(opt_token);
|
||||||
|
tokens.unshift(type_token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
ret.name = name.value;
|
ret.name = name.value;
|
||||||
if (ret.optional) {
|
if (ret.optional) {
|
||||||
all_ws();
|
all_ws();
|
||||||
@ -244,20 +294,33 @@
|
|||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
var argument_list = function () {
|
var argument_list = function (store) {
|
||||||
var arg = argument(), ret = [];
|
var ret = []
|
||||||
if (!arg) return ret;
|
, arg = argument(store ? ret : null)
|
||||||
|
;
|
||||||
|
if (!arg) return;
|
||||||
ret.push(arg);
|
ret.push(arg);
|
||||||
while (true) {
|
while (true) {
|
||||||
all_ws();
|
all_ws(store ? ret : null);
|
||||||
if (!consume(OTHER, ",")) return ret;
|
if (!consume(OTHER, ",")) return ret;
|
||||||
all_ws();
|
var nxt = argument(store ? ret : null) || error("Trailing comma in arguments list");
|
||||||
var nxt = argument() || error("Trailing comma in arguments list");
|
|
||||||
ret.push(nxt);
|
ret.push(nxt);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var simple_extended_attr = function () {
|
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();
|
all_ws();
|
||||||
var name = consume(ID);
|
var name = consume(ID);
|
||||||
if (!name) return;
|
if (!name) return;
|
||||||
@ -274,24 +337,35 @@
|
|||||||
}
|
}
|
||||||
all_ws();
|
all_ws();
|
||||||
if (consume(OTHER, "(")) {
|
if (consume(OTHER, "(")) {
|
||||||
ret["arguments"] = argument_list();
|
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();
|
all_ws();
|
||||||
consume(OTHER, ")") || error("Unclosed argument in extended attribute");
|
consume(OTHER, ")") || error("Unexpected token in extended attribute argument list or type pair");
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Note: we parse something simpler than the official syntax. It's all that ever
|
// Note: we parse something simpler than the official syntax. It's all that ever
|
||||||
// seems to be used
|
// seems to be used
|
||||||
var extended_attrs = function () {
|
var extended_attrs = function (store) {
|
||||||
var eas = [];
|
var eas = [];
|
||||||
all_ws();
|
all_ws(store);
|
||||||
if (!consume(OTHER, "[")) return eas;
|
if (!consume(OTHER, "[")) return eas;
|
||||||
eas[0] = simple_extended_attr() || error("Extended attribute with not content");
|
eas[0] = simple_extended_attr(store) || error("Extended attribute with not content");
|
||||||
all_ws();
|
all_ws();
|
||||||
while (consume(OTHER, ",")) {
|
while (consume(OTHER, ",")) {
|
||||||
all_ws();
|
eas.push(simple_extended_attr(store) || error("Trailing comma in extended attribute"));
|
||||||
eas.push(simple_extended_attr() || error("Trailing comma in extended attribute"));
|
|
||||||
all_ws();
|
all_ws();
|
||||||
}
|
}
|
||||||
consume(OTHER, "]") || error("No end of extended attribute");
|
consume(OTHER, "]") || error("No end of extended attribute");
|
||||||
@ -314,8 +388,8 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var const_ = function () {
|
var const_ = function (store) {
|
||||||
all_ws();
|
all_ws(store, "pea");
|
||||||
if (!consume(ID, "const")) return;
|
if (!consume(ID, "const")) return;
|
||||||
var ret = { type: "const", nullable: false };
|
var ret = { type: "const", nullable: false };
|
||||||
all_ws();
|
all_ws();
|
||||||
@ -352,14 +426,14 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var operation_rest = function (ret) {
|
var operation_rest = function (ret, store) {
|
||||||
all_ws();
|
all_ws();
|
||||||
if (!ret) ret = {};
|
if (!ret) ret = {};
|
||||||
var name = consume(ID);
|
var name = consume(ID);
|
||||||
ret.name = name ? name.value : null;
|
ret.name = name ? name.value : null;
|
||||||
all_ws();
|
all_ws();
|
||||||
consume(OTHER, "(") || error("Invalid operation");
|
consume(OTHER, "(") || error("Invalid operation");
|
||||||
ret["arguments"] = argument_list();
|
ret["arguments"] = argument_list(store) || [];
|
||||||
all_ws();
|
all_ws();
|
||||||
consume(OTHER, ")") || error("Unterminated operation");
|
consume(OTHER, ")") || error("Unterminated operation");
|
||||||
all_ws();
|
all_ws();
|
||||||
@ -367,8 +441,8 @@
|
|||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
var callback = function () {
|
var callback = function (store) {
|
||||||
all_ws();
|
all_ws(store, "pea");
|
||||||
var ret;
|
var ret;
|
||||||
if (!consume(ID, "callback")) return;
|
if (!consume(ID, "callback")) return;
|
||||||
all_ws();
|
all_ws();
|
||||||
@ -387,7 +461,7 @@
|
|||||||
ret.idlType = return_type();
|
ret.idlType = return_type();
|
||||||
all_ws();
|
all_ws();
|
||||||
consume(OTHER, "(") || error("No arguments in callback");
|
consume(OTHER, "(") || error("No arguments in callback");
|
||||||
ret["arguments"] = argument_list();
|
ret["arguments"] = argument_list(store) || [];
|
||||||
all_ws();
|
all_ws();
|
||||||
consume(OTHER, ")") || error("Unterminated callback");
|
consume(OTHER, ")") || error("Unterminated callback");
|
||||||
all_ws();
|
all_ws();
|
||||||
@ -395,8 +469,8 @@
|
|||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
var attribute = function () {
|
var attribute = function (store) {
|
||||||
all_ws();
|
all_ws(store, "pea");
|
||||||
var grabbed = []
|
var grabbed = []
|
||||||
, ret = {
|
, ret = {
|
||||||
type: "attribute"
|
type: "attribute"
|
||||||
@ -454,8 +528,8 @@
|
|||||||
return typ;
|
return typ;
|
||||||
};
|
};
|
||||||
|
|
||||||
var operation = function () {
|
var operation = function (store) {
|
||||||
all_ws();
|
all_ws(store, "pea");
|
||||||
var ret = {
|
var ret = {
|
||||||
type: "operation"
|
type: "operation"
|
||||||
, getter: false
|
, getter: false
|
||||||
@ -478,13 +552,13 @@
|
|||||||
if (ret.getter || ret.setter || ret.creator || ret.deleter || ret.legacycaller) {
|
if (ret.getter || ret.setter || ret.creator || ret.deleter || ret.legacycaller) {
|
||||||
all_ws();
|
all_ws();
|
||||||
ret.idlType = return_type();
|
ret.idlType = return_type();
|
||||||
operation_rest(ret);
|
operation_rest(ret, store);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (consume(ID, "static")) {
|
if (consume(ID, "static")) {
|
||||||
ret["static"] = true;
|
ret["static"] = true;
|
||||||
ret.idlType = return_type();
|
ret.idlType = return_type();
|
||||||
operation_rest(ret);
|
operation_rest(ret, store);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
else if (consume(ID, "stringifier")) {
|
else if (consume(ID, "stringifier")) {
|
||||||
@ -492,7 +566,7 @@
|
|||||||
all_ws();
|
all_ws();
|
||||||
if (consume(OTHER, ";")) return ret;
|
if (consume(OTHER, ";")) return ret;
|
||||||
ret.idlType = return_type();
|
ret.idlType = return_type();
|
||||||
operation_rest(ret);
|
operation_rest(ret, store);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
ret.idlType = return_type();
|
ret.idlType = return_type();
|
||||||
@ -513,7 +587,7 @@
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
operation_rest(ret);
|
operation_rest(ret, store);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -530,8 +604,8 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var serialiser = function () {
|
var serialiser = function (store) {
|
||||||
all_ws();
|
all_ws(store, "pea");
|
||||||
if (!consume(ID, "serializer")) return;
|
if (!consume(ID, "serializer")) return;
|
||||||
var ret = { type: "serializer" };
|
var ret = { type: "serializer" };
|
||||||
all_ws();
|
all_ws();
|
||||||
@ -589,77 +663,84 @@
|
|||||||
else {
|
else {
|
||||||
ret.idlType = return_type();
|
ret.idlType = return_type();
|
||||||
all_ws();
|
all_ws();
|
||||||
ret.operation = operation_rest();
|
ret.operation = operation_rest(null, store);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
var interface_ = function (isPartial) {
|
var interface_ = function (isPartial, store) {
|
||||||
all_ws();
|
all_ws(isPartial ? null : store, "pea");
|
||||||
if (!consume(ID, "interface")) return;
|
if (!consume(ID, "interface")) return;
|
||||||
all_ws();
|
all_ws();
|
||||||
var name = consume(ID) || error("No name for interface");
|
var name = consume(ID) || error("No name for interface");
|
||||||
var ret = {
|
var mems = []
|
||||||
|
, ret = {
|
||||||
type: "interface"
|
type: "interface"
|
||||||
, name: name.value
|
, name: name.value
|
||||||
, partial: false
|
, partial: false
|
||||||
, members: []
|
, members: mems
|
||||||
};
|
};
|
||||||
if (!isPartial) ret.inheritance = inheritance() || null;
|
if (!isPartial) ret.inheritance = inheritance() || null;
|
||||||
all_ws();
|
all_ws();
|
||||||
consume(OTHER, "{") || error("Bodyless interface");
|
consume(OTHER, "{") || error("Bodyless interface");
|
||||||
while (true) {
|
while (true) {
|
||||||
all_ws();
|
all_ws(store ? mems : null);
|
||||||
if (consume(OTHER, "}")) {
|
if (consume(OTHER, "}")) {
|
||||||
all_ws();
|
all_ws();
|
||||||
consume(OTHER, ";") || error("Missing semicolon after interface");
|
consume(OTHER, ";") || error("Missing semicolon after interface");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
var ea = extended_attrs();
|
var ea = extended_attrs(store ? mems : null);
|
||||||
all_ws();
|
all_ws();
|
||||||
var cnt = const_();
|
var cnt = const_(store ? mems : null);
|
||||||
if (cnt) {
|
if (cnt) {
|
||||||
cnt.extAttrs = ea;
|
cnt.extAttrs = ea;
|
||||||
ret.members.push(cnt);
|
ret.members.push(cnt);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var mem = serialiser() || attribute() || operation() || error("Unknown member");
|
var mem = serialiser(store ? mems : null) ||
|
||||||
|
attribute(store ? mems : null) ||
|
||||||
|
operation(store ? mems : null) ||
|
||||||
|
error("Unknown member");
|
||||||
mem.extAttrs = ea;
|
mem.extAttrs = ea;
|
||||||
ret.members.push(mem);
|
ret.members.push(mem);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var partial = function () {
|
var partial = function (store) {
|
||||||
all_ws();
|
all_ws(store, "pea");
|
||||||
if (!consume(ID, "partial")) return;
|
if (!consume(ID, "partial")) return;
|
||||||
var thing = dictionary(true) || interface_(true) || error("Partial doesn't apply to anything");
|
var thing = dictionary(true, store) ||
|
||||||
|
interface_(true, store) ||
|
||||||
|
error("Partial doesn't apply to anything");
|
||||||
thing.partial = true;
|
thing.partial = true;
|
||||||
return thing;
|
return thing;
|
||||||
};
|
};
|
||||||
|
|
||||||
var dictionary = function (isPartial) {
|
var dictionary = function (isPartial, store) {
|
||||||
all_ws();
|
all_ws(isPartial ? null : store, "pea");
|
||||||
if (!consume(ID, "dictionary")) return;
|
if (!consume(ID, "dictionary")) return;
|
||||||
all_ws();
|
all_ws();
|
||||||
var name = consume(ID) || error("No name for dictionary");
|
var name = consume(ID) || error("No name for dictionary");
|
||||||
var ret = {
|
var mems = []
|
||||||
|
, ret = {
|
||||||
type: "dictionary"
|
type: "dictionary"
|
||||||
, name: name.value
|
, name: name.value
|
||||||
, partial: false
|
, partial: false
|
||||||
, members: []
|
, members: mems
|
||||||
};
|
};
|
||||||
if (!isPartial) ret.inheritance = inheritance() || null;
|
if (!isPartial) ret.inheritance = inheritance() || null;
|
||||||
all_ws();
|
all_ws();
|
||||||
consume(OTHER, "{") || error("Bodyless dictionary");
|
consume(OTHER, "{") || error("Bodyless dictionary");
|
||||||
while (true) {
|
while (true) {
|
||||||
all_ws();
|
all_ws(store ? mems : null);
|
||||||
if (consume(OTHER, "}")) {
|
if (consume(OTHER, "}")) {
|
||||||
all_ws();
|
all_ws();
|
||||||
consume(OTHER, ";") || error("Missing semicolon after dictionary");
|
consume(OTHER, ";") || error("Missing semicolon after dictionary");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
var ea = extended_attrs();
|
var ea = extended_attrs(store ? mems : null);
|
||||||
all_ws();
|
all_ws(store ? mems : null, "pea");
|
||||||
var typ = type() || error("No type for dictionary member");
|
var typ = type() || error("No type for dictionary member");
|
||||||
all_ws();
|
all_ws();
|
||||||
var name = consume(ID) || error("No name for dictionary member");
|
var name = consume(ID) || error("No name for dictionary member");
|
||||||
@ -675,28 +756,29 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var exception = function () {
|
var exception = function (store) {
|
||||||
all_ws();
|
all_ws(store, "pea");
|
||||||
if (!consume(ID, "exception")) return;
|
if (!consume(ID, "exception")) return;
|
||||||
all_ws();
|
all_ws();
|
||||||
var name = consume(ID) || error("No name for exception");
|
var name = consume(ID) || error("No name for exception");
|
||||||
var ret = {
|
var mems = []
|
||||||
|
, ret = {
|
||||||
type: "exception"
|
type: "exception"
|
||||||
, name: name.value
|
, name: name.value
|
||||||
, members: []
|
, members: mems
|
||||||
};
|
};
|
||||||
ret.inheritance = inheritance() || null;
|
ret.inheritance = inheritance() || null;
|
||||||
all_ws();
|
all_ws();
|
||||||
consume(OTHER, "{") || error("Bodyless exception");
|
consume(OTHER, "{") || error("Bodyless exception");
|
||||||
while (true) {
|
while (true) {
|
||||||
all_ws();
|
all_ws(store ? mems : null);
|
||||||
if (consume(OTHER, "}")) {
|
if (consume(OTHER, "}")) {
|
||||||
all_ws();
|
all_ws();
|
||||||
consume(OTHER, ";") || error("Missing semicolon after exception");
|
consume(OTHER, ";") || error("Missing semicolon after exception");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
var ea = extended_attrs();
|
var ea = extended_attrs(store ? mems : null);
|
||||||
all_ws();
|
all_ws(store ? mems : null, "pea");
|
||||||
var cnt = const_();
|
var cnt = const_();
|
||||||
if (cnt) {
|
if (cnt) {
|
||||||
cnt.extAttrs = ea;
|
cnt.extAttrs = ea;
|
||||||
@ -718,21 +800,22 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var enum_ = function () {
|
var enum_ = function (store) {
|
||||||
all_ws();
|
all_ws(store, "pea");
|
||||||
if (!consume(ID, "enum")) return;
|
if (!consume(ID, "enum")) return;
|
||||||
all_ws();
|
all_ws();
|
||||||
var name = consume(ID) || error("No name for enum");
|
var name = consume(ID) || error("No name for enum");
|
||||||
var ret = {
|
var vals = []
|
||||||
|
, ret = {
|
||||||
type: "enum"
|
type: "enum"
|
||||||
, name: name.value
|
, name: name.value
|
||||||
, values: []
|
, values: vals
|
||||||
};
|
};
|
||||||
all_ws();
|
all_ws();
|
||||||
consume(OTHER, "{") || error("No curly for enum");
|
consume(OTHER, "{") || error("No curly for enum");
|
||||||
var saw_comma = false;
|
var saw_comma = false;
|
||||||
while (true) {
|
while (true) {
|
||||||
all_ws();
|
all_ws(store ? vals : null);
|
||||||
if (consume(OTHER, "}")) {
|
if (consume(OTHER, "}")) {
|
||||||
all_ws();
|
all_ws();
|
||||||
if (saw_comma) error("Trailing comma in enum");
|
if (saw_comma) error("Trailing comma in enum");
|
||||||
@ -741,9 +824,10 @@
|
|||||||
}
|
}
|
||||||
var val = consume(STR) || error("Unexpected value in enum");
|
var val = consume(STR) || error("Unexpected value in enum");
|
||||||
ret.values.push(val.value.replace(/"/g, ""));
|
ret.values.push(val.value.replace(/"/g, ""));
|
||||||
all_ws();
|
all_ws(store ? vals : null);
|
||||||
if (consume(OTHER, ",")) {
|
if (consume(OTHER, ",")) {
|
||||||
all_ws();
|
if (store) vals.push({ type: "," });
|
||||||
|
all_ws(store ? vals : null);
|
||||||
saw_comma = true;
|
saw_comma = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -752,15 +836,15 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var typedef = function () {
|
var typedef = function (store) {
|
||||||
all_ws();
|
all_ws(store, "pea");
|
||||||
if (!consume(ID, "typedef")) return;
|
if (!consume(ID, "typedef")) return;
|
||||||
var ret = {
|
var ret = {
|
||||||
type: "typedef"
|
type: "typedef"
|
||||||
};
|
};
|
||||||
all_ws();
|
all_ws();
|
||||||
ret.typeExtAttrs = extended_attrs();
|
ret.typeExtAttrs = extended_attrs();
|
||||||
all_ws();
|
all_ws(store, "tpea");
|
||||||
ret.idlType = type() || error("No type in typedef");
|
ret.idlType = type() || error("No type in typedef");
|
||||||
all_ws();
|
all_ws();
|
||||||
var name = consume(ID) || error("No name in typedef");
|
var name = consume(ID) || error("No name in typedef");
|
||||||
@ -770,8 +854,8 @@
|
|||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
var implements_ = function () {
|
var implements_ = function (store) {
|
||||||
all_ws();
|
all_ws(store, "pea");
|
||||||
var target = consume(ID);
|
var target = consume(ID);
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
var w = all_ws();
|
var w = all_ws();
|
||||||
@ -794,24 +878,24 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var definition = function () {
|
var definition = function (store) {
|
||||||
return callback() ||
|
return callback(store) ||
|
||||||
interface_() ||
|
interface_(false, store) ||
|
||||||
partial() ||
|
partial(store) ||
|
||||||
dictionary() ||
|
dictionary(false, store) ||
|
||||||
exception() ||
|
exception(store) ||
|
||||||
enum_() ||
|
enum_(store) ||
|
||||||
typedef() ||
|
typedef(store) ||
|
||||||
implements_()
|
implements_(store)
|
||||||
;
|
;
|
||||||
};
|
};
|
||||||
|
|
||||||
var definitions = function () {
|
var definitions = function (store) {
|
||||||
if (!tokens.length) return [];
|
if (!tokens.length) return [];
|
||||||
var defs = [];
|
var defs = [];
|
||||||
while (true) {
|
while (true) {
|
||||||
var ea = extended_attrs()
|
var ea = extended_attrs(store ? defs : null)
|
||||||
, def = definition();
|
, def = definition(store ? defs : null);
|
||||||
if (!def) {
|
if (!def) {
|
||||||
if (ea.length) error("Stray extended attributes");
|
if (ea.length) error("Stray extended attributes");
|
||||||
break;
|
break;
|
||||||
@ -821,21 +905,20 @@
|
|||||||
}
|
}
|
||||||
return defs;
|
return defs;
|
||||||
};
|
};
|
||||||
var res = definitions();
|
var res = definitions(opt.ws);
|
||||||
if (tokens.length) error("Unrecognised tokens");
|
if (tokens.length) error("Unrecognised tokens");
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
var obj = {
|
var inNode = typeof module !== "undefined" && module.exports
|
||||||
parse: function (str) {
|
, obj = {
|
||||||
var tokens = tokenise(str);
|
parse: function (str, opt) {
|
||||||
return parse(tokens);
|
if (!opt) opt = {};
|
||||||
}
|
var tokens = tokenise(str);
|
||||||
|
return parse(tokens, opt);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if (typeof module !== "undefined" && module.exports) {
|
|
||||||
module.exports = obj;
|
if (inNode) module.exports = obj;
|
||||||
}
|
else window.WebIDL2 = obj;
|
||||||
else {
|
|
||||||
window.WebIDL2 = obj;
|
|
||||||
}
|
|
||||||
}());
|
}());
|
||||||
|
@ -8,131 +8,7 @@ policies and contribution forms [3].
|
|||||||
[3] http://www.w3.org/2004/10/27-testcases
|
[3] http://www.w3.org/2004/10/27-testcases
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/* For user documentation see docs/idlharness.md */
|
||||||
* This file automatically generates browser tests for WebIDL interfaces, using
|
|
||||||
* the testharness.js framework. To use, first include the following:
|
|
||||||
*
|
|
||||||
* <script src=/resources/testharness.js></script>
|
|
||||||
* <script src=/resources/testharnessreport.js></script>
|
|
||||||
* <script src=/resources/WebIDLParser.js></script>
|
|
||||||
* <script src=/resources/idlharness.js></script>
|
|
||||||
*
|
|
||||||
* Then you'll need some type of IDLs. Here's some script that can be run on a
|
|
||||||
* spec written in HTML, which will grab all the elements with class="idl",
|
|
||||||
* concatenate them, and replace the body so you can copy-paste:
|
|
||||||
*
|
|
||||||
var s = "";
|
|
||||||
[].forEach.call(document.getElementsByClassName("idl"), function(idl) {
|
|
||||||
//https://www.w3.org/Bugs/Public/show_bug.cgi?id=14914
|
|
||||||
if (!idl.classList.contains("extract"))
|
|
||||||
{
|
|
||||||
s += idl.textContent + "\n\n";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
document.body.innerHTML = '<pre></pre>';
|
|
||||||
document.body.firstChild.textContent = s;
|
|
||||||
*
|
|
||||||
* (TODO: write this in Python or something so that it can be done from the
|
|
||||||
* command line instead.)
|
|
||||||
*
|
|
||||||
* Once you have that, put it in your script somehow. The easiest way is to
|
|
||||||
* embed it literally in an HTML file with <script type=text/plain> or similar,
|
|
||||||
* so that you don't have to do any escaping. Another possibility is to put it
|
|
||||||
* in a separate .idl file that's fetched via XHR or similar. Sample usage:
|
|
||||||
*
|
|
||||||
* var idl_array = new IdlArray();
|
|
||||||
* idl_array.add_untested_idls("interface Node { readonly attribute DOMString nodeName; };");
|
|
||||||
* idl_array.add_idls("interface Document : Node { readonly attribute DOMString URL; };");
|
|
||||||
* idl_array.add_objects({Document: ["document"]});
|
|
||||||
* idl_array.test();
|
|
||||||
*
|
|
||||||
* This tests that window.Document exists and meets all the requirements of
|
|
||||||
* WebIDL. It also tests that window.document (the result of evaluating the
|
|
||||||
* string "document") has URL and nodeName properties that behave as they
|
|
||||||
* should, and otherwise meets WebIDL's requirements for an object whose
|
|
||||||
* primary interface is Document. It does not test that window.Node exists,
|
|
||||||
* which is what you want if the Node interface is already tested in some other
|
|
||||||
* specification's suite and your specification only extends or refers to it.
|
|
||||||
* Of course, each IDL string can define many different things, and calls to
|
|
||||||
* add_objects() can register many different objects for different interfaces:
|
|
||||||
* this is a very simple example.
|
|
||||||
*
|
|
||||||
* TODO: Write assert_writable, assert_enumerable, assert_configurable and
|
|
||||||
* their inverses, and use those instead of just checking
|
|
||||||
* getOwnPropertyDescriptor.
|
|
||||||
*
|
|
||||||
* == Public methods of IdlArray ==
|
|
||||||
*
|
|
||||||
* IdlArray objects can be obtained with new IdlArray(). Anything not
|
|
||||||
* documented in this section should be considered an implementation detail,
|
|
||||||
* and outside callers should not use it.
|
|
||||||
*
|
|
||||||
* add_idls(idl_string):
|
|
||||||
* Parses idl_string (throwing on parse error) and adds the results to the
|
|
||||||
* IdlArray. All the definitions will be tested when you run test(). If
|
|
||||||
* some of the definitions refer to other definitions, those must be present
|
|
||||||
* too. For instance, if idl_string says that Document inherits from Node,
|
|
||||||
* the Node interface must also have been provided in some call to add_idls()
|
|
||||||
* or add_untested_idls().
|
|
||||||
*
|
|
||||||
* add_untested_idls(idl_string):
|
|
||||||
* Like add_idls(), but the definitions will not be tested. If an untested
|
|
||||||
* interface is added and then extended with a tested partial interface, the
|
|
||||||
* members of the partial interface will still be tested. Also, all the
|
|
||||||
* members will still be tested for objects added with add_objects(), because
|
|
||||||
* you probably want to test that (for instance) window.document has all the
|
|
||||||
* properties from Node, not just Document, even if the Node interface itself
|
|
||||||
* is tested in a different test suite.
|
|
||||||
*
|
|
||||||
* add_objects(dict):
|
|
||||||
* dict should be an object whose keys are the names of interfaces or
|
|
||||||
* exceptions, and whose values are arrays of strings. When an interface or
|
|
||||||
* exception is tested, every string registered for it with add_objects()
|
|
||||||
* will be evaluated, and tests will be run on the result to verify that it
|
|
||||||
* correctly implements that interface or exception. This is the only way to
|
|
||||||
* test anything about [NoInterfaceObject] interfaces, and there are many
|
|
||||||
* tests that can't be run on any interface without an object to fiddle with.
|
|
||||||
*
|
|
||||||
* The interface has to be the *primary* interface of all the objects
|
|
||||||
* provided. For example, don't pass {Node: ["document"]}, but rather
|
|
||||||
* {Document: ["document"]}. Assuming the Document interface was declared to
|
|
||||||
* inherit from Node, this will automatically test that document implements
|
|
||||||
* the Node interface too.
|
|
||||||
*
|
|
||||||
* Warning: methods will be called on any provided objects, in a manner that
|
|
||||||
* WebIDL requires be safe. For instance, if a method has mandatory
|
|
||||||
* arguments, the test suite will try calling it with too few arguments to
|
|
||||||
* see if it throws an exception. If an implementation incorrectly runs the
|
|
||||||
* function instead of throwing, this might have side effects, possibly even
|
|
||||||
* preventing the test suite from running correctly.
|
|
||||||
*
|
|
||||||
* prevent_multiple_testing(name):
|
|
||||||
* This is a niche method for use in case you're testing many objects that
|
|
||||||
* implement the same interfaces, and don't want to retest the same
|
|
||||||
* interfaces every single time. For instance, HTML defines many interfaces
|
|
||||||
* that all inherit from HTMLElement, so the HTML test suite has something
|
|
||||||
* like
|
|
||||||
* .add_objects({
|
|
||||||
* HTMLHtmlElement: ['document.documentElement'],
|
|
||||||
* HTMLHeadElement: ['document.head'],
|
|
||||||
* HTMLBodyElement: ['document.body'],
|
|
||||||
* ...
|
|
||||||
* })
|
|
||||||
* and so on for dozens of element types. This would mean that it would
|
|
||||||
* retest that each and every one of those elements implements HTMLElement,
|
|
||||||
* Element, and Node, which would be thousands of basically redundant tests.
|
|
||||||
* The test suite therefore calls prevent_multiple_testing("HTMLElement").
|
|
||||||
* This means that once one object has been tested to implement HTMLElement
|
|
||||||
* and its ancestors, no other object will be. Thus in the example code
|
|
||||||
* above, the harness would test that document.documentElement correctly
|
|
||||||
* implements HTMLHtmlElement, HTMLElement, Element, and Node; but
|
|
||||||
* document.head would only be tested for HTMLHeadElement, and so on for
|
|
||||||
* further objects.
|
|
||||||
*
|
|
||||||
* test():
|
|
||||||
* Run all tests. This should be called after you've called all other
|
|
||||||
* methods to add IDLs and objects.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notes for people who want to edit this file (not just use it as a library):
|
* Notes for people who want to edit this file (not just use it as a library):
|
||||||
@ -550,6 +426,9 @@ IdlArray.prototype.assert_type_is = function(value, type)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case "DOMString":
|
case "DOMString":
|
||||||
|
case "ByteString":
|
||||||
|
case "USVString":
|
||||||
|
// TODO: https://github.com/w3c/testharness.js/issues/92
|
||||||
assert_equals(typeof value, "string");
|
assert_equals(typeof value, "string");
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1312,6 +1191,249 @@ IdlInterface.prototype.test_self = function()
|
|||||||
}.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property');
|
}.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//@}
|
||||||
|
IdlInterface.prototype.test_member_const = function(member)
|
||||||
|
//@{
|
||||||
|
{
|
||||||
|
test(function()
|
||||||
|
{
|
||||||
|
assert_own_property(window, this.name,
|
||||||
|
"window does not have own property " + format_value(this.name));
|
||||||
|
|
||||||
|
// "For each constant defined on an interface A, there must be
|
||||||
|
// a corresponding property on the interface object, if it
|
||||||
|
// exists."
|
||||||
|
assert_own_property(window[this.name], member.name);
|
||||||
|
// "The value of the property is that which is obtained by
|
||||||
|
// converting the constant’s IDL value to an ECMAScript
|
||||||
|
// value."
|
||||||
|
assert_equals(window[this.name][member.name], constValue(member.value),
|
||||||
|
"property has wrong value");
|
||||||
|
// "The property has attributes { [[Writable]]: false,
|
||||||
|
// [[Enumerable]]: true, [[Configurable]]: false }."
|
||||||
|
var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name);
|
||||||
|
assert_false("get" in desc, "property has getter");
|
||||||
|
assert_false("set" in desc, "property has setter");
|
||||||
|
assert_false(desc.writable, "property is writable");
|
||||||
|
assert_true(desc.enumerable, "property is not enumerable");
|
||||||
|
assert_false(desc.configurable, "property is configurable");
|
||||||
|
}.bind(this), this.name + " interface: constant " + member.name + " on interface object");
|
||||||
|
// "In addition, a property with the same characteristics must
|
||||||
|
// exist on the interface prototype object."
|
||||||
|
test(function()
|
||||||
|
{
|
||||||
|
assert_own_property(window, this.name,
|
||||||
|
"window does not have own property " + format_value(this.name));
|
||||||
|
|
||||||
|
if (this.has_extended_attribute("Callback")) {
|
||||||
|
assert_false("prototype" in window[this.name],
|
||||||
|
this.name + ' should not have a "prototype" property');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_own_property(window[this.name], "prototype",
|
||||||
|
'interface "' + this.name + '" does not have own property "prototype"');
|
||||||
|
|
||||||
|
assert_own_property(window[this.name].prototype, member.name);
|
||||||
|
assert_equals(window[this.name].prototype[member.name], constValue(member.value),
|
||||||
|
"property has wrong value");
|
||||||
|
var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name);
|
||||||
|
assert_false("get" in desc, "property has getter");
|
||||||
|
assert_false("set" in desc, "property has setter");
|
||||||
|
assert_false(desc.writable, "property is writable");
|
||||||
|
assert_true(desc.enumerable, "property is not enumerable");
|
||||||
|
assert_false(desc.configurable, "property is configurable");
|
||||||
|
}.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object");
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//@}
|
||||||
|
IdlInterface.prototype.test_member_attribute = function(member)
|
||||||
|
//@{
|
||||||
|
{
|
||||||
|
test(function()
|
||||||
|
{
|
||||||
|
assert_own_property(window, this.name,
|
||||||
|
"window does not have own property " + format_value(this.name));
|
||||||
|
assert_own_property(window[this.name], "prototype",
|
||||||
|
'interface "' + this.name + '" does not have own property "prototype"');
|
||||||
|
|
||||||
|
if (member["static"]) {
|
||||||
|
assert_own_property(window[this.name], member.name,
|
||||||
|
"The interface object must have a property " +
|
||||||
|
format_value(member.name));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert_true(member.name in window[this.name].prototype,
|
||||||
|
"The prototype object must have a property " +
|
||||||
|
format_value(member.name));
|
||||||
|
|
||||||
|
if (!member.has_extended_attribute("LenientThis")) {
|
||||||
|
assert_throws(new TypeError(), function() {
|
||||||
|
window[this.name].prototype[member.name];
|
||||||
|
}.bind(this), "getting property on prototype object must throw TypeError");
|
||||||
|
} else {
|
||||||
|
assert_equals(window[this.name].prototype[member.name], undefined,
|
||||||
|
"getting property on prototype object must return undefined");
|
||||||
|
}
|
||||||
|
do_interface_attribute_asserts(window[this.name].prototype, member);
|
||||||
|
}
|
||||||
|
}.bind(this), this.name + " interface: attribute " + member.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
//@}
|
||||||
|
IdlInterface.prototype.test_member_operation = function(member)
|
||||||
|
//@{
|
||||||
|
{
|
||||||
|
test(function()
|
||||||
|
{
|
||||||
|
assert_own_property(window, this.name,
|
||||||
|
"window does not have own property " + format_value(this.name));
|
||||||
|
|
||||||
|
if (this.has_extended_attribute("Callback")) {
|
||||||
|
assert_false("prototype" in window[this.name],
|
||||||
|
this.name + ' should not have a "prototype" property');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_own_property(window[this.name], "prototype",
|
||||||
|
'interface "' + this.name + '" does not have own property "prototype"');
|
||||||
|
|
||||||
|
// "For each unique identifier of an operation defined on the
|
||||||
|
// interface, there must be a corresponding property on the
|
||||||
|
// interface prototype object (if it is a regular operation) or
|
||||||
|
// the interface object (if it is a static operation), unless
|
||||||
|
// the effective overload set for that identifier and operation
|
||||||
|
// and with an argument count of 0 (for the ECMAScript language
|
||||||
|
// binding) has no entries."
|
||||||
|
//
|
||||||
|
var prototypeOrInterfaceObject;
|
||||||
|
if (member["static"]) {
|
||||||
|
assert_own_property(window[this.name], member.name,
|
||||||
|
"interface prototype object missing static operation");
|
||||||
|
prototypeOrInterfaceObject = window[this.name];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert_own_property(window[this.name].prototype, member.name,
|
||||||
|
"interface prototype object missing non-static operation");
|
||||||
|
prototypeOrInterfaceObject = window[this.name].prototype;
|
||||||
|
}
|
||||||
|
|
||||||
|
var desc = Object.getOwnPropertyDescriptor(prototypeOrInterfaceObject, member.name);
|
||||||
|
// "The property has attributes { [[Writable]]: true,
|
||||||
|
// [[Enumerable]]: true, [[Configurable]]: true }."
|
||||||
|
assert_false("get" in desc, "property has getter");
|
||||||
|
assert_false("set" in desc, "property has setter");
|
||||||
|
assert_true(desc.writable, "property is not writable");
|
||||||
|
assert_true(desc.enumerable, "property is not enumerable");
|
||||||
|
assert_true(desc.configurable, "property is not configurable");
|
||||||
|
// "The value of the property is a Function object whose
|
||||||
|
// behavior is as follows . . ."
|
||||||
|
assert_equals(typeof prototypeOrInterfaceObject[member.name], "function",
|
||||||
|
"property must be a function");
|
||||||
|
// "The value of the Function object’s “length” property is
|
||||||
|
// a Number determined as follows:
|
||||||
|
// ". . .
|
||||||
|
// "Return the length of the shortest argument list of the
|
||||||
|
// entries in S."
|
||||||
|
//
|
||||||
|
// TODO: Doesn't handle overloading or variadic arguments.
|
||||||
|
assert_equals(prototypeOrInterfaceObject[member.name].length,
|
||||||
|
member.arguments.filter(function(arg) {
|
||||||
|
return !arg.optional;
|
||||||
|
}).length,
|
||||||
|
"property has wrong .length");
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
if (!member["static"]) {
|
||||||
|
assert_throws(new TypeError(), function() {
|
||||||
|
window[this.name].prototype[member.name].apply(null, args);
|
||||||
|
}, "calling operation with this = null didn't throw TypeError");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ". . . If O is not null and is also not a platform object
|
||||||
|
// that implements interface I, throw a TypeError."
|
||||||
|
//
|
||||||
|
// TODO: Test a platform object that implements some other
|
||||||
|
// interface. (Have to be sure to get inheritance right.)
|
||||||
|
assert_throws(new TypeError(), function() {
|
||||||
|
window[this.name].prototype[member.name].apply({}, args);
|
||||||
|
}, "calling operation with this = {} didn't throw TypeError");
|
||||||
|
}.bind(this), this.name + " interface: operation " + member.name +
|
||||||
|
"(" + member.arguments.map(function(m) { return m.idlType.idlType; }) +
|
||||||
|
")");
|
||||||
|
};
|
||||||
|
|
||||||
|
//@}
|
||||||
|
IdlInterface.prototype.test_member_stringifier = function(member)
|
||||||
|
//@{
|
||||||
|
{
|
||||||
|
test(function()
|
||||||
|
{
|
||||||
|
assert_own_property(window, this.name,
|
||||||
|
"window does not have own property " + format_value(this.name));
|
||||||
|
|
||||||
|
if (this.has_extended_attribute("Callback")) {
|
||||||
|
assert_false("prototype" in window[this.name],
|
||||||
|
this.name + ' should not have a "prototype" property');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_own_property(window[this.name], "prototype",
|
||||||
|
'interface "' + this.name + '" does not have own property "prototype"');
|
||||||
|
|
||||||
|
// ". . . the property exists on the interface prototype object."
|
||||||
|
var interfacePrototypeObject = window[this.name].prototype;
|
||||||
|
assert_own_property(window[this.name].prototype, "toString",
|
||||||
|
"interface prototype object missing non-static operation");
|
||||||
|
|
||||||
|
var desc = Object.getOwnPropertyDescriptor(interfacePrototypeObject, "toString");
|
||||||
|
// "The property has attributes { [[Writable]]: B,
|
||||||
|
// [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the
|
||||||
|
// stringifier is unforgeable on the interface, and true otherwise."
|
||||||
|
assert_false("get" in desc, "property has getter");
|
||||||
|
assert_false("set" in desc, "property has setter");
|
||||||
|
assert_true(desc.writable, "property is not writable");
|
||||||
|
assert_true(desc.enumerable, "property is not enumerable");
|
||||||
|
assert_true(desc.configurable, "property is not configurable");
|
||||||
|
// "The value of the property is a Function object, which behaves as
|
||||||
|
// follows . . ."
|
||||||
|
assert_equals(typeof interfacePrototypeObject.toString, "function",
|
||||||
|
"property must be a function");
|
||||||
|
// "The value of the Function object’s “length” property is the Number
|
||||||
|
// value 0."
|
||||||
|
assert_equals(interfacePrototypeObject.toString.length, 0,
|
||||||
|
"property has wrong .length");
|
||||||
|
|
||||||
|
// "Let O be the result of calling ToObject on the this value."
|
||||||
|
assert_throws(new TypeError(), function() {
|
||||||
|
window[this.name].prototype.toString.apply(null, []);
|
||||||
|
}, "calling stringifier with this = null didn't throw TypeError");
|
||||||
|
|
||||||
|
// "If O is not an object that implements the interface on which the
|
||||||
|
// stringifier was declared, then throw a TypeError."
|
||||||
|
//
|
||||||
|
// TODO: Test a platform object that implements some other
|
||||||
|
// interface. (Have to be sure to get inheritance right.)
|
||||||
|
assert_throws(new TypeError(), function() {
|
||||||
|
window[this.name].prototype.toString.apply({}, []);
|
||||||
|
}, "calling stringifier with this = {} didn't throw TypeError");
|
||||||
|
}.bind(this), this.name + " interface: stringifier");
|
||||||
|
};
|
||||||
|
|
||||||
//@}
|
//@}
|
||||||
IdlInterface.prototype.test_members = function()
|
IdlInterface.prototype.test_members = function()
|
||||||
//@{
|
//@{
|
||||||
@ -1319,195 +1441,37 @@ IdlInterface.prototype.test_members = function()
|
|||||||
for (var i = 0; i < this.members.length; i++)
|
for (var i = 0; i < this.members.length; i++)
|
||||||
{
|
{
|
||||||
var member = this.members[i];
|
var member = this.members[i];
|
||||||
if (member.untested)
|
if (member.untested) {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (member.type == "const")
|
|
||||||
{
|
|
||||||
test(function()
|
|
||||||
{
|
|
||||||
assert_own_property(window, this.name,
|
|
||||||
"window does not have own property " + format_value(this.name));
|
|
||||||
|
|
||||||
// "For each constant defined on an interface A, there must be
|
switch (member.type) {
|
||||||
// a corresponding property on the interface object, if it
|
case "const":
|
||||||
// exists."
|
this.test_member_const(member);
|
||||||
assert_own_property(window[this.name], member.name);
|
break;
|
||||||
// "The value of the property is that which is obtained by
|
|
||||||
// converting the constant’s IDL value to an ECMAScript
|
|
||||||
// value."
|
|
||||||
assert_equals(window[this.name][member.name], constValue(member.value),
|
|
||||||
"property has wrong value");
|
|
||||||
// "The property has attributes { [[Writable]]: false,
|
|
||||||
// [[Enumerable]]: true, [[Configurable]]: false }."
|
|
||||||
var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name);
|
|
||||||
assert_false("get" in desc, "property has getter");
|
|
||||||
assert_false("set" in desc, "property has setter");
|
|
||||||
assert_false(desc.writable, "property is writable");
|
|
||||||
assert_true(desc.enumerable, "property is not enumerable");
|
|
||||||
assert_false(desc.configurable, "property is configurable");
|
|
||||||
}.bind(this), this.name + " interface: constant " + member.name + " on interface object");
|
|
||||||
// "In addition, a property with the same characteristics must
|
|
||||||
// exist on the interface prototype object."
|
|
||||||
test(function()
|
|
||||||
{
|
|
||||||
assert_own_property(window, this.name,
|
|
||||||
"window does not have own property " + format_value(this.name));
|
|
||||||
|
|
||||||
if (this.has_extended_attribute("Callback")) {
|
case "attribute":
|
||||||
assert_false("prototype" in window[this.name],
|
// For unforgeable attributes, we do the checks in
|
||||||
this.name + ' should not have a "prototype" property');
|
// test_interface_of instead.
|
||||||
return;
|
if (!member.has_extended_attribute("Unforgeable")) {
|
||||||
}
|
this.test_member_attribute(member);
|
||||||
|
|
||||||
assert_own_property(window[this.name], "prototype",
|
|
||||||
'interface "' + this.name + '" does not have own property "prototype"');
|
|
||||||
|
|
||||||
assert_own_property(window[this.name].prototype, member.name);
|
|
||||||
assert_equals(window[this.name].prototype[member.name], constValue(member.value),
|
|
||||||
"property has wrong value");
|
|
||||||
var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name);
|
|
||||||
assert_false("get" in desc, "property has getter");
|
|
||||||
assert_false("set" in desc, "property has setter");
|
|
||||||
assert_false(desc.writable, "property is writable");
|
|
||||||
assert_true(desc.enumerable, "property is not enumerable");
|
|
||||||
assert_false(desc.configurable, "property is configurable");
|
|
||||||
}.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object");
|
|
||||||
}
|
|
||||||
else if (member.type == "attribute")
|
|
||||||
{
|
|
||||||
if (member.has_extended_attribute("Unforgeable"))
|
|
||||||
{
|
|
||||||
// We do the checks in test_interface_of instead
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
test(function()
|
break;
|
||||||
{
|
|
||||||
assert_own_property(window, this.name,
|
|
||||||
"window does not have own property " + format_value(this.name));
|
|
||||||
assert_own_property(window[this.name], "prototype",
|
|
||||||
'interface "' + this.name + '" does not have own property "prototype"');
|
|
||||||
|
|
||||||
if (member["static"]) {
|
case "operation":
|
||||||
assert_own_property(window[this.name], member.name,
|
|
||||||
"The interface object must have a property " +
|
|
||||||
format_value(member.name));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert_true(member.name in window[this.name].prototype,
|
|
||||||
"The prototype object must have a property " +
|
|
||||||
format_value(member.name));
|
|
||||||
|
|
||||||
// TODO: Needs to test for LenientThis.
|
|
||||||
assert_throws(new TypeError(), function() {
|
|
||||||
window[this.name].prototype[member.name];
|
|
||||||
}.bind(this), "getting property on prototype object must throw TypeError");
|
|
||||||
do_interface_attribute_asserts(window[this.name].prototype, member);
|
|
||||||
}
|
|
||||||
}.bind(this), this.name + " interface: attribute " + member.name);
|
|
||||||
}
|
|
||||||
else if (member.type == "operation")
|
|
||||||
{
|
|
||||||
// TODO: Need to correctly handle multiple operations with the same
|
// TODO: Need to correctly handle multiple operations with the same
|
||||||
// identifier.
|
// identifier.
|
||||||
if (!member.name)
|
if (member.name) {
|
||||||
{
|
this.test_member_operation(member);
|
||||||
// Unnamed getter or such
|
} else if (member.stringifier) {
|
||||||
continue;
|
this.test_member_stringifier(member);
|
||||||
}
|
}
|
||||||
test(function()
|
break;
|
||||||
{
|
|
||||||
assert_own_property(window, this.name,
|
|
||||||
"window does not have own property " + format_value(this.name));
|
|
||||||
|
|
||||||
if (this.has_extended_attribute("Callback")) {
|
default:
|
||||||
assert_false("prototype" in window[this.name],
|
// TODO: check more member types.
|
||||||
this.name + ' should not have a "prototype" property');
|
break;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_own_property(window[this.name], "prototype",
|
|
||||||
'interface "' + this.name + '" does not have own property "prototype"');
|
|
||||||
|
|
||||||
// "For each unique identifier of an operation defined on the
|
|
||||||
// interface, there must be a corresponding property on the
|
|
||||||
// interface prototype object (if it is a regular operation) or
|
|
||||||
// the interface object (if it is a static operation), unless
|
|
||||||
// the effective overload set for that identifier and operation
|
|
||||||
// and with an argument count of 0 (for the ECMAScript language
|
|
||||||
// binding) has no entries."
|
|
||||||
//
|
|
||||||
var prototypeOrInterfaceObject;
|
|
||||||
if (member["static"]) {
|
|
||||||
assert_own_property(window[this.name], member.name,
|
|
||||||
"interface prototype object missing static operation");
|
|
||||||
prototypeOrInterfaceObject = window[this.name];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert_own_property(window[this.name].prototype, member.name,
|
|
||||||
"interface prototype object missing non-static operation");
|
|
||||||
prototypeOrInterfaceObject = window[this.name].prototype;
|
|
||||||
}
|
|
||||||
|
|
||||||
var desc = Object.getOwnPropertyDescriptor(prototypeOrInterfaceObject, member.name);
|
|
||||||
// "The property has attributes { [[Writable]]: true,
|
|
||||||
// [[Enumerable]]: true, [[Configurable]]: true }."
|
|
||||||
assert_false("get" in desc, "property has getter");
|
|
||||||
assert_false("set" in desc, "property has setter");
|
|
||||||
assert_true(desc.writable, "property is not writable");
|
|
||||||
assert_true(desc.enumerable, "property is not enumerable");
|
|
||||||
assert_true(desc.configurable, "property is not configurable");
|
|
||||||
// "The value of the property is a Function object whose
|
|
||||||
// behavior is as follows . . ."
|
|
||||||
assert_equals(typeof prototypeOrInterfaceObject[member.name], "function",
|
|
||||||
"property must be a function");
|
|
||||||
// "The value of the Function object’s “length” property is
|
|
||||||
// a Number determined as follows:
|
|
||||||
// ". . .
|
|
||||||
// "Return the length of the shortest argument list of the
|
|
||||||
// entries in S."
|
|
||||||
//
|
|
||||||
// TODO: Doesn't handle overloading or variadic arguments.
|
|
||||||
assert_equals(prototypeOrInterfaceObject[member.name].length,
|
|
||||||
member.arguments.filter(function(arg) {
|
|
||||||
return !arg.optional;
|
|
||||||
}).length,
|
|
||||||
"property has wrong .length");
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
if (!member["static"]) {
|
|
||||||
assert_throws(new TypeError(), function() {
|
|
||||||
window[this.name].prototype[member.name].apply(null, args);
|
|
||||||
}, "calling operation with this = null didn't throw TypeError");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ". . . If O is not null and is also not a platform object
|
|
||||||
// that implements interface I, throw a TypeError."
|
|
||||||
//
|
|
||||||
// TODO: Test a platform object that implements some other
|
|
||||||
// interface. (Have to be sure to get inheritance right.)
|
|
||||||
assert_throws(new TypeError(), function() {
|
|
||||||
window[this.name].prototype[member.name].apply({}, args);
|
|
||||||
}, "calling operation with this = {} didn't throw TypeError");
|
|
||||||
}.bind(this), this.name + " interface: operation " + member.name +
|
|
||||||
"(" + member.arguments.map(function(m) { return m.idlType.idlType; }) +
|
|
||||||
")");
|
|
||||||
}
|
}
|
||||||
// TODO: check more member types, like stringifier
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1748,11 +1712,14 @@ function do_interface_attribute_asserts(obj, member)
|
|||||||
// value 0."
|
// value 0."
|
||||||
assert_equals(typeof desc.get, "function", "getter must be Function");
|
assert_equals(typeof desc.get, "function", "getter must be Function");
|
||||||
assert_equals(desc.get.length, 0, "getter length must be 0");
|
assert_equals(desc.get.length, 0, "getter length must be 0");
|
||||||
// TODO: Account for LenientThis
|
if (!member.has_extended_attribute("LenientThis")) {
|
||||||
assert_throws(new TypeError(), function()
|
assert_throws(new TypeError(), function() {
|
||||||
{
|
desc.get.call({});
|
||||||
desc.get.call({});
|
}.bind(this), "calling getter on wrong object type must throw TypeError");
|
||||||
}.bind(this), "calling getter on wrong object type must throw TypeError");
|
} else {
|
||||||
|
assert_equals(desc.get.call({}), undefined,
|
||||||
|
"calling getter on wrong object type must return undefined");
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Test calling setter on the interface prototype (should throw
|
// TODO: Test calling setter on the interface prototype (should throw
|
||||||
// TypeError in most cases).
|
// TypeError in most cases).
|
||||||
@ -1774,6 +1741,14 @@ function do_interface_attribute_asserts(obj, member)
|
|||||||
{
|
{
|
||||||
assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes");
|
assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes");
|
||||||
assert_equals(desc.set.length, 1, "setter length must be 1");
|
assert_equals(desc.set.length, 1, "setter length must be 1");
|
||||||
|
if (!member.has_extended_attribute("LenientThis")) {
|
||||||
|
assert_throws(new TypeError(), function() {
|
||||||
|
desc.set.call({});
|
||||||
|
}.bind(this), "calling setter on wrong object type must throw TypeError");
|
||||||
|
} else {
|
||||||
|
assert_equals(desc.set.call({}), undefined,
|
||||||
|
"calling setter on wrong object type must return undefined");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//@}
|
//@}
|
||||||
@ -1826,6 +1801,8 @@ function create_suitable_object(type)
|
|||||||
return 7;
|
return 7;
|
||||||
|
|
||||||
case "DOMString":
|
case "DOMString":
|
||||||
|
case "ByteString":
|
||||||
|
case "USVString":
|
||||||
return "foo";
|
return "foo";
|
||||||
|
|
||||||
case "object":
|
case "object":
|
||||||
|
@ -90,3 +90,18 @@ table#results span.actual {
|
|||||||
white-space:pre;
|
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;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user