diff --git a/testing/web-platform/tests/resources/idlharness.js b/testing/web-platform/tests/resources/idlharness.js
index e7a3abbec447..f68ad1dd7d81 100644
--- a/testing/web-platform/tests/resources/idlharness.js
+++ b/testing/web-platform/tests/resources/idlharness.js
@@ -1507,6 +1507,17 @@ IdlInterface.prototype.test = function()
this.test_members();
};
+// This supports both Constructor extended attributes and constructor
+// operations until all idl fragments have been updated.
+IdlInterface.prototype.constructors = function()
+{
+ var extendedAttributes = this.extAttrs
+ .filter(function(attr) { return attr.name == "Constructor"; });
+ var operations = this.members
+ .filter(function(m) { return m.type == "constructor"; });
+ return extendedAttributes.concat(operations);
+}
+
IdlInterface.prototype.test_self = function()
{
subsetTestByKey(this.name, test, function()
@@ -1594,7 +1605,7 @@ IdlInterface.prototype.test_self = function()
"prototype of self's property " + format_value(this.name) + " is not Function.prototype");
}
- if (!this.has_extended_attribute("Constructor")) {
+ if (!this.constructors().length) {
// "The internal [[Call]] method of the interface object behaves as
// follows . . .
//
@@ -1629,8 +1640,7 @@ IdlInterface.prototype.test_self = function()
assert_false(desc.enumerable, this.name + ".length should not be enumerable");
assert_true(desc.configurable, this.name + ".length should be configurable");
- var constructors = this.extAttrs
- .filter(function(attr) { return attr.name == "Constructor"; });
+ var constructors = this.constructors();
var expected_length = minOverloadLength(constructors);
assert_equals(this.get_interface_object().length, expected_length, "wrong value for " + this.name + ".length");
}.bind(this), this.name + " interface object length");
diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/constructors.html b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/constructors.html
new file mode 100644
index 000000000000..93cc7f42cc55
--- /dev/null
+++ b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/constructors.html
@@ -0,0 +1,25 @@
+
+
IdlInterface.prototype.constructors()
+
+
+
+
+
+
+
diff --git a/testing/web-platform/tests/resources/webidl2/lib/webidl2.js b/testing/web-platform/tests/resources/webidl2/lib/webidl2.js
index 900694b095df..98216ab750de 100644
--- a/testing/web-platform/tests/resources/webidl2/lib/webidl2.js
+++ b/testing/web-platform/tests/resources/webidl2/lib/webidl2.js
@@ -103,10 +103,10 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _lib_webidl2_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "parse", function() { return _lib_webidl2_js__WEBPACK_IMPORTED_MODULE_0__["parse"]; });
-/* harmony import */ var _lib_writer_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(29);
+/* harmony import */ var _lib_writer_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(30);
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "write", function() { return _lib_writer_js__WEBPACK_IMPORTED_MODULE_1__["write"]; });
-/* harmony import */ var _lib_validator_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(30);
+/* harmony import */ var _lib_validator_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(31);
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "validate", function() { return _lib_validator_js__WEBPACK_IMPORTED_MODULE_2__["validate"]; });
@@ -128,10 +128,10 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _productions_typedef_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(17);
/* harmony import */ var _productions_callback_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(18);
/* harmony import */ var _productions_interface_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(19);
-/* harmony import */ var _productions_mixin_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(24);
-/* harmony import */ var _productions_dictionary_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(25);
-/* harmony import */ var _productions_namespace_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(27);
-/* harmony import */ var _productions_callback_interface_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(28);
+/* harmony import */ var _productions_mixin_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(25);
+/* harmony import */ var _productions_dictionary_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(26);
+/* harmony import */ var _productions_namespace_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(28);
+/* harmony import */ var _productions_callback_interface_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(29);
@@ -299,6 +299,7 @@ const nonRegexTerminals = [
"async",
"boolean",
"byte",
+ "constructor",
"double",
"false",
"float",
@@ -501,12 +502,13 @@ function lastLine(text) {
/**
* @typedef {object} WebIDL2ErrorOptions
* @property {"error" | "warning"} level
+ * @property {Function} autofix
*
* @param {string} message error message
* @param {"Syntax" | "Validation"} kind error type
* @param {WebIDL2ErrorOptions} [options]
*/
-function error(source, position, current, message, kind, { level = "error" } = {}) {
+function error(source, position, current, message, kind, { level = "error", autofix } = {}) {
/**
* @param {number} count
*/
@@ -556,6 +558,7 @@ function error(source, position, current, message, kind, { level = "error" } = {
line,
sourceName: source.name,
level,
+ autofix,
input: subsequentText,
tokens: subsequentTokens
};
@@ -664,12 +667,15 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "type_with_extended_attributes", function() { return type_with_extended_attributes; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "return_type", function() { return return_type; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "stringifier", function() { return stringifier; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "autofixAddExposedWindow", function() { return autofixAddExposedWindow; });
/* harmony import */ var _type_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
/* harmony import */ var _argument_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
/* harmony import */ var _token_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(13);
/* harmony import */ var _extended_attributes_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11);
/* harmony import */ var _operation_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(14);
/* harmony import */ var _attribute_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(15);
+/* harmony import */ var _tokeniser_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(2);
+
@@ -838,6 +844,29 @@ function stringifier(tokeniser) {
return member;
}
+/**
+ * @param {object} def
+ * @param {import("./extended-attributes.js").ExtendedAttributes} def.extAttrs
+ */
+function autofixAddExposedWindow(def) {
+ return () => {
+ if (def.extAttrs.length){
+ const tokeniser = new _tokeniser_js__WEBPACK_IMPORTED_MODULE_6__["Tokeniser"]("Exposed=Window,");
+ const exposed = _extended_attributes_js__WEBPACK_IMPORTED_MODULE_3__["SimpleExtendedAttribute"].parse(tokeniser);
+ exposed.tokens.separator = tokeniser.consume(",");
+ const existing = def.extAttrs[0];
+ if (!/^\s/.test(existing.tokens.name.trivia)) {
+ existing.tokens.name.trivia = ` ${existing.tokens.name.trivia}`;
+ }
+ def.extAttrs.unshift(exposed);
+ } else {
+ def.extAttrs = _extended_attributes_js__WEBPACK_IMPORTED_MODULE_3__["ExtendedAttributes"].parse(new _tokeniser_js__WEBPACK_IMPORTED_MODULE_6__["Tokeniser"]("[Exposed=Window]"));
+ def.extAttrs.tokens.open.trivia = def.tokens.base.trivia;
+ def.tokens.base.trivia = " ";
+ }
+ };
+}
+
/***/ }),
/* 6 */
@@ -1065,25 +1094,15 @@ class Base {
"use strict";
__webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "dictionaryWithinUnion", function() { return dictionaryWithinUnion; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "idlTypeIncludesDictionary", function() { return idlTypeIncludesDictionary; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "referencesTypedef", function() { return referencesTypedef; });
-/**
- * Yields direct references to dictionary within union.
- */
-function* dictionaryWithinUnion(subtypes, defs) {
- for (const subtype of subtypes) {
- const def = defs.unique.get(subtype.idlType);
- if (def && def.type === "dictionary") {
- yield subtype;
- }
- }
-}
-
/**
+ * @param {*} idlType
+ * @param {*[]} defs
+ * @param {object} [options]
+ * @param {boolean} [options.useNullableInner] use when the input idlType is nullable and you want to use its inner type
* @return the type reference that ultimately includes dictionary.
*/
-function idlTypeIncludesDictionary(idlType, defs) {
+function idlTypeIncludesDictionary(idlType, defs, { useNullableInner } = {}) {
if (!idlType.union) {
const def = defs.unique.get(idlType.idlType);
if (!def) {
@@ -1103,7 +1122,7 @@ function idlTypeIncludesDictionary(idlType, defs) {
return idlType;
}
}
- if (def.type === "dictionary") {
+ if (def.type === "dictionary" && (useNullableInner || !idlType.nullable)) {
return idlType;
}
}
@@ -1118,14 +1137,6 @@ function idlTypeIncludesDictionary(idlType, defs) {
}
}
-/**
- * @return true if the idlType directly references a typedef.
- */
-function referencesTypedef(idlType, defs) {
- const result = defs.unique.get(idlType.idlType);
- return result && result.type === "typedef";
-}
-
/***/ }),
/* 9 */
@@ -1189,19 +1200,29 @@ class Argument extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
*validate(defs) {
yield* this.idlType.validate(defs);
- if (Object(_validators_helpers_js__WEBPACK_IMPORTED_MODULE_6__["idlTypeIncludesDictionary"])(this.idlType, defs)) {
- if (this.optional && !this.default) {
- const message = `Optional dictionary arguments must have a default value of \`{}\`.`;
- yield Object(_error_js__WEBPACK_IMPORTED_MODULE_5__["validationError"])(this.source, this.tokens.name, this, message);
- }
+ if (Object(_validators_helpers_js__WEBPACK_IMPORTED_MODULE_6__["idlTypeIncludesDictionary"])(this.idlType, defs, { useNullableInner: true })) {
if (this.idlType.nullable) {
const message = `Dictionary arguments cannot be nullable.`;
yield Object(_error_js__WEBPACK_IMPORTED_MODULE_5__["validationError"])(this.source, this.tokens.name, this, message);
+ } else if (this.optional && !this.default) {
+ const message = `Optional dictionary arguments must have a default value of \`{}\`.`;
+ yield Object(_error_js__WEBPACK_IMPORTED_MODULE_5__["validationError"])(this.source, this.tokens.name, this, message, {
+ autofix: autofixOptionalDictionaryDefaultValue(this)
+ });
}
}
}
}
+/**
+ * @param {Argument} arg
+ */
+function autofixOptionalDictionaryDefaultValue(arg) {
+ return () => {
+ arg.default = _default_js__WEBPACK_IMPORTED_MODULE_1__["Default"].parse(new _tokeniser_js__WEBPACK_IMPORTED_MODULE_4__["Tokeniser"](" = {}"));
+ };
+}
+
/***/ }),
/* 10 */
@@ -1259,6 +1280,7 @@ class Default extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
"use strict";
__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SimpleExtendedAttribute", function() { return SimpleExtendedAttribute; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ExtendedAttributes", function() { return ExtendedAttributes; });
/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7);
/* harmony import */ var _array_base_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12);
@@ -1715,6 +1737,8 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(5);
/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3);
/* harmony import */ var _validators_interface_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(23);
+/* harmony import */ var _constructor_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(24);
+
@@ -1747,6 +1771,7 @@ class Interface extends _container_js__WEBPACK_IMPORTED_MODULE_0__["Container"]
inheritable: !partial,
allowedMembers: [
[_constant_js__WEBPACK_IMPORTED_MODULE_3__["Constant"].parse],
+ [_constructor_js__WEBPACK_IMPORTED_MODULE_8__["Constructor"].parse],
[static_member],
[_helpers_js__WEBPACK_IMPORTED_MODULE_5__["stringifier"]],
[_iterable_js__WEBPACK_IMPORTED_MODULE_4__["IterableLike"].parse],
@@ -1772,7 +1797,9 @@ To fix, add, for example, \`[Exposed=Window]\`. Please also consider carefully \
if your interface should also be exposed in a Worker scope. Refer to the \
[WebIDL spec section on Exposed](https://heycam.github.io/webidl/#Exposed) \
for more information.`;
- yield Object(_error_js__WEBPACK_IMPORTED_MODULE_6__["validationError"])(this.source, this.tokens.name, this, message);
+ yield Object(_error_js__WEBPACK_IMPORTED_MODULE_6__["validationError"])(this.source, this.tokens.name, this, message, {
+ autofix: Object(_helpers_js__WEBPACK_IMPORTED_MODULE_5__["autofixAddExposedWindow"])(this)
+ });
}
yield* super.validate(defs);
@@ -2033,6 +2060,43 @@ function* checkInterfaceMemberDuplication(defs, i) {
/* 24 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Constructor", function() { return Constructor; });
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
+
+
+
+class Constructor extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+ /**
+ * @param {import("../tokeniser").Tokeniser} tokeniser
+ */
+ static parse(tokeniser) {
+ const base = tokeniser.consume("constructor");
+ if (!base) {
+ return;
+ }
+ const tokens = { base };
+ tokens.open = tokeniser.consume("(") || tokeniser.error("No argument list in constructor");
+ const args = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["argument_list"])(tokeniser);
+ tokens.close = tokeniser.consume(")") || tokeniser.error("Unterminated constructor");
+ tokens.termination = tokeniser.consume(";") || tokeniser.error("No semicolon after constructor");
+ const ret = new Constructor({ tokens });
+ ret.arguments = args;
+ return ret;
+ }
+
+ get type() {
+ return "constructor";
+ }
+}
+
+
+/***/ }),
+/* 25 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Mixin", function() { return Mixin; });
@@ -2075,14 +2139,14 @@ class Mixin extends _container_js__WEBPACK_IMPORTED_MODULE_0__["Container"] {
/***/ }),
-/* 25 */
+/* 26 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Dictionary", function() { return Dictionary; });
/* harmony import */ var _container_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(20);
-/* harmony import */ var _field_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(26);
+/* harmony import */ var _field_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(27);
@@ -2112,7 +2176,7 @@ class Dictionary extends _container_js__WEBPACK_IMPORTED_MODULE_0__["Container"]
/***/ }),
-/* 26 */
+/* 27 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -2161,7 +2225,7 @@ class Field extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
/***/ }),
-/* 27 */
+/* 28 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -2171,6 +2235,8 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _attribute_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(15);
/* harmony import */ var _operation_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(14);
/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(3);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(5);
+
@@ -2206,7 +2272,9 @@ To fix, add, for example, [Exposed=Window]. Please also consider carefully \
if your namespace should also be exposed in a Worker scope. Refer to the \
[WebIDL spec section on Exposed](https://heycam.github.io/webidl/#Exposed) \
for more information.`;
- yield Object(_error_js__WEBPACK_IMPORTED_MODULE_3__["validationError"])(this.source, this.tokens.name, this, message);
+ yield Object(_error_js__WEBPACK_IMPORTED_MODULE_3__["validationError"])(this.source, this.tokens.name, this, message, {
+ autofix: Object(_helpers_js__WEBPACK_IMPORTED_MODULE_4__["autofixAddExposedWindow"])(this)
+ });
}
yield* super.validate(defs);
}
@@ -2214,7 +2282,7 @@ for more information.`;
/***/ }),
-/* 28 */
+/* 29 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -2255,7 +2323,7 @@ class CallbackInterface extends _container_js__WEBPACK_IMPORTED_MODULE_0__["Cont
/***/ }),
-/* 29 */
+/* 30 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -2416,6 +2484,17 @@ function write(ast, { templates: ts = templates } = {}) {
]), { data: it, parent });
}
+ function constructor(it, parent) {
+ return ts.definition(ts.wrap([
+ extended_attributes(it.extAttrs),
+ token(it.tokens.base),
+ token(it.tokens.open),
+ ts.wrap(it.arguments.map(argument)),
+ token(it.tokens.close),
+ token(it.tokens.termination)
+ ]), { data: it, parent });
+ }
+
function inheritance(inh) {
if (!inh.tokens.inheritance) {
return "";
@@ -2538,6 +2617,7 @@ function write(ast, { templates: ts = templates } = {}) {
namespace: container,
operation,
attribute,
+ constructor,
dictionary: container,
field,
const: const_,
@@ -2570,7 +2650,7 @@ function write(ast, { templates: ts = templates } = {}) {
/***/ }),
-/* 30 */
+/* 31 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";