1. Introduction
This section is informative.
This standard defines an interface definition language, Web IDL, that can be used to describe interfaces that are intended to be implemented in web browsers. Web IDL is an IDL variant with a number of features that allow the behavior of common script objects in the web platform to be specified more readily. How interfaces described with Web IDL correspond to constructs within JavaScript execution environments is also detailed here.
Concretely, Web IDL provides a syntax for specifying the surface APIs of web platform objects, as well as JavaScript bindings that detail how those APIs manifest as JavaScript constructs. This ensures common tasks, such as installing global properties, processing numeric inputs, or exposing iteration behavior, remain uniform across web platform specifications: such specifications describe their interfaces using Web IDL, and then use prose to specify API-specific details.
The term "JavaScript" is used to refer to ECMA-262, rather than the official term ECMAScript, since the term JavaScript is more widely known.
2. Interface definition language
This section describes a language, Web IDL, which can be used to define
interfaces for APIs in the Web platform. A specification that defines Web APIs
can include one or more IDL fragments that
describe the interfaces (the state and behavior that objects can exhibit)
for the APIs defined by that specification.
An IDL fragment is
a sequence of definitions that matches the
The different kinds of definitions that can appear in an IDL fragment are: interfaces, partial interface definitions, interface mixins, partial mixin definitions, callback functions, callback interfaces, namespaces, partial namespace definitions, dictionaries, partial dictionary definitions, typedefs and includes statements. These are all defined in the following sections.
Each definition
(matching
[extended_attributes ]interface identifier { /* interface_members... */ };
Definitions ::ExtendedAttributeList Definition Definitions ε
Definition ::CallbackOrInterfaceOrMixin Namespace Partial Dictionary Enum Typedef IncludesStatement
The following is an example of an IDL fragment.
[Exposed =Window ]interface Paint { }; [Exposed =Window ]interface SolidColor :Paint {attribute double red ;attribute double green ;attribute double blue ; }; [Exposed =Window ]interface Pattern :Paint {attribute DOMString imageURL ; }; [Exposed =Window ]interface GraphicalWindow {constructor ();readonly attribute unsigned long width ;readonly attribute unsigned long height ;attribute Paint currentPaint ;undefined drawRectangle (double x ,double y ,double width ,double height );undefined drawText (double x ,double y ,DOMString text ); };
Here, four interfaces
are being defined.
The GraphicalWindow
interface has two
read only attributes,
one writable attribute, and two operations
defined on it. Objects that implement the GraphicalWindow
interface
will expose these attributes and operations in a manner appropriate to the
particular language being used.
In JavaScript, the attributes on the IDL interfaces will be exposed as accessor
properties and the operations as data properties whose value is a built-in function object on a
prototype object for all GraphicalWindow
objects; each JavaScript object that implements GraphicalWindow
will have that prototype object in its prototype chain.
The constructor operation that appears on GraphicalWindow
causes a constructor to exist in JavaScript implementations,
so that calling new GraphicalWindow()
would return a new object
that implemented the interface.
All interfaces have the [Exposed
] extended attribute, which ensures the interfaces
are only available in realms whose global object is a Window
object.
2.1. Names
Every interface,
partial interface definition,
namespace,
partial namespace definition,
dictionary,
partial dictionary definition,
enumeration,
callback function,
callback interface and
typedef (together called named definitions)
and every constant,
attribute,
and dictionary member has an
identifier, as do some
operations.
The identifier is determined by an
-
For named definitions, the
identifier token that appears directly after theinterface ,namespace ,dictionary ,enum orcallback keyword determines the identifier of that definition.interface interface_identifier { /* interface_members... */ };partial interface interface_identifier { /* interface_members... */ };namespace namespace_identifier { /* namespace_members... */ };partial namespace namespace_identifier { /* namespace_members... */ };dictionary dictionary_identifier { /* dictionary_members... */ };partial dictionary dictionary_identifier { /* dictionary_members... */ };enum enumeration_identifier {"enum" ,"values" /* , ... */ };callback callback_identifier =return_type (/* arguments... */);callback interface callback_interface_identifier { /* interface_members... */ }; -
For attributes, typedefs and dictionary members, the final
identifier token before the semicolon at the end of the declaration determines the identifier.[
extended_attributes ]interface identifier {attribute type attribute_identifier ; };typedef type typedef_identifier ;dictionary identifier {type dictionary_member_identifier ; }; -
For constants, the
identifier token before the equals sign determines the identifier.const type constant_identifier = 42; -
For operations, the
identifier token that appears after the return type but before the opening parenthesis (that is, one that is matched as part of theOptionalOperationName grammar symbol in anOperationRest ) determines the identifier of the operation. If there is no suchidentifier token, then the operation does not have an identifier.interface interface_identifier {return_type operation_identifier (/* arguments... */); };
Note: Operations can have no identifier when they are being used to declare a special kind of operation, such as a getter or setter.
For all of these constructs, the identifier
is the value of the
Note: A leading U+005F (_) is used to escape an identifier from looking
like a reserved word so that, for example, an interface named "interface
" can be
defined. The leading U+005F (_) is dropped to unescape the
identifier.
Operation arguments can take a slightly wider set of identifiers. In an operation
declaration, the identifier of an argument is specified immediately after its
type and is given by either an
interface interface_identifier {return_type operation_identifier (argument_type argument_identifier /* , ... */); };
ArgumentNameKeyword ::attribute callback const constructor deleter dictionary enum getter includes inherit interface iterable maplike mixin namespace partial readonly required setlike setter static stringifier typedef unrestricted
If an
The identifier of any of the abovementioned
IDL constructs (except operation arguments) must not be "constructor
",
"toString
",
or begin with a U+005F (_). These
are known as reserved identifiers.
Although the "toJSON
" identifier is not a reserved identifier,
it must only be used for regular operations
that convert objects to JSON types,
as described in § 2.5.3.1 toJSON.
Note: Further restrictions on identifier names for particular constructs can be made in later sections.
Within the set of IDL fragments that a given implementation supports, the identifier of every interface, namespace, dictionary, enumeration, callback function, callback interface and typedef must not be the same as the identifier of any other interface, namespace, dictionary, enumeration, callback function, callback interface or typedef.
Within an IDL fragment, a reference to a definition need not appear after the declaration of the referenced definition. References can also be made across IDL fragments.
Therefore, the following IDL fragment is valid:
[Exposed =Window ]interface B :A {undefined f (SequenceOfLongs x ); }; [Exposed =Window ]interface A { };typedef sequence <long >SequenceOfLongs ;
The following IDL fragment demonstrates how identifiers are given to definitions and interface members.
// Typedef identifier: "number"typedef double number ; // Interface identifier: "System" [Exposed =Window ]interface System { // Operation identifier: "createObject" // Operation argument identifier: "interface"object createObject (DOMString _interface ); // Operation argument identifier: "interface"sequence <object >getObjects (DOMString interface ); // Operation has no identifier; it declares a getter.getter DOMString (DOMString keyName ); }; // Interface identifier: "TextField" [Exposed =Window ]interface TextField { // Attribute identifier: "const"attribute boolean _const ; // Attribute identifier: "value"attribute DOMString ?_value ; };
Note that while the second attribute
on the TextField
interface
need not have been escaped with an underscore (because "value
" is
not a keyword in the IDL grammar), it is still unescaped
to obtain the attribute’s identifier.
2.2. Interfaces
IDL fragments are used to
describe object oriented systems. In such systems, objects are entities
that have identity and which are encapsulations of state and behavior.
An interface is a definition (matching
[extended_attributes ]interface identifier { /* interface_members... */ };
An interface is a specification of a set of
interface members
(matching
Interfaces in Web IDL describe how objects that implement the interface behave. In bindings for object oriented languages, it is expected that an object that implements a particular IDL interface provides ways to inspect and modify the object’s state and to invoke the behavior described by the interface.
An interface can be defined to inherit from another interface. If the identifier of the interface is followed by a U+003A (:) and an identifier, then that identifier identifies the inherited interface. An object that implements an interface that inherits from another also implements that inherited interface. The object therefore will also have members that correspond to the interface members from the inherited interface.
interface identifier :identifier_of_inherited_interface { /* interface_members... */ };
The order that members appear in has significance for property enumeration in the JavaScript binding.
Interfaces may specify an interface member that has the same name as one from an inherited interface. Objects that implement the derived interface will expose the member on the derived interface. It is language binding specific whether the overridden member can be accessed on the object.
Consider the following two interfaces.
[Exposed =Window ]interface A {undefined f ();undefined g (); }; [Exposed =Window ]interface B :A {undefined f ();undefined g (DOMString x ); };
In the JavaScript language binding, an instance of B
will have a prototype chain that looks like the following:
[Object.prototype: the Object prototype object] ↑ [A.prototype: interface prototype object for A] ↑ [B.prototype: interface prototype object for B] ↑ [instanceOfB]
Calling instanceOfB.f()
in JavaScript will invoke the f defined
on B
. However, the f from A
can still be invoked on an object that implements B
by
calling A.prototype.f.call(instanceOfB)
.
The inherited interfaces of a given interface A is the set of all interfaces that A inherits from, directly or indirectly. If A does not inherit from another interface, then the set is empty. Otherwise, the set includes the interface B that A inherits from and all of B’s inherited interfaces.
An interface must not be declared such that its inheritance hierarchy has a cycle. That is, an interface A cannot inherit from itself, nor can it inherit from another interface B that inherits from A, and so on.
Note that general multiple inheritance of interfaces is not supported, and objects also cannot implement arbitrary sets of interfaces. Objects can be defined to implement a single given interface A, which means that it also implements all of A’s inherited interfaces. In addition, an includes statement can be used to define that objects implementing an interface A will always also include the members of the interface mixins A includes.
Each interface member can be preceded by a list of extended attributes (matching
[extended_attributes ]interface identifier { [extended_attributes ]const type constant_identifier = 42; [extended_attributes ]attribute type identifier ; [extended_attributes ]return_type identifier (/* arguments... */); };
The IDL for interfaces can be split into multiple parts by using
partial interface definitions
(matching
interface SomeInterface { /* interface_members... */ };partial interface SomeInterface { /* interface_members... */ };
Note: Partial interface definitions are intended for use as a specification editorial aide, allowing the definition of an interface to be separated over more than one section of the document, and sometimes multiple documents.
The order of appearance of an interface definition and any of its partial interface definitions does not matter.
Note: A partial interface definition cannot specify that the interface inherits from another interface. Inheritance is to be specified on the original interface definition.
The relevant language binding determines how interfaces correspond to constructs in the language.
The following extended attributes are applicable to interfaces:
[CrossOriginIsolated
],
[Exposed
],
[Global
],
[LegacyFactoryFunction
],
[LegacyNoInterfaceObject
],
[LegacyOverrideBuiltIns
],
[LegacyWindowAlias
], and
[SecureContext
].
The following extended attributes are applicable to partial interfaces:
[CrossOriginIsolated
],
[Exposed
],
[LegacyOverrideBuiltIns
], and
[SecureContext
].
Interfaces must be annotated with an [Exposed
] extended attribute.
The qualified name of an interface interface is defined as follows:
-
Let identifier be the identifier of interface.
-
If interface has a [
LegacyNamespace
] extended attribute, then:-
Let namespace be the identifier argument of the [
LegacyNamespace
] extended attribute. -
Return the concatenation of « namespace, identifier » with separator U+002E (.).
-
-
Return identifier.
CallbackOrInterfaceOrMixin ::callback CallbackRestOrInterface interface InterfaceOrMixin
InterfaceOrMixin ::InterfaceRest MixinRest
InterfaceRest ::identifier Inheritance { InterfaceMembers } ;
Partial ::partial PartialDefinition
PartialDefinition ::interface PartialInterfaceOrPartialMixin PartialDictionary Namespace
PartialInterfaceOrPartialMixin ::PartialInterfaceRest MixinRest
PartialInterfaceRest ::identifier { PartialInterfaceMembers } ;
InterfaceMembers ::ExtendedAttributeList InterfaceMember InterfaceMembers ε
InterfaceMember ::PartialInterfaceMember Constructor
PartialInterfaceMembers ::ExtendedAttributeList PartialInterfaceMember PartialInterfaceMembers ε
PartialInterfaceMember ::Const Operation Stringifier StaticMember Iterable AsyncIterable ReadOnlyMember ReadWriteAttribute ReadWriteMaplike ReadWriteSetlike InheritAttribute
Inheritance ::: identifier ε
The following IDL fragment
demonstrates the definition of two mutually referential interfaces.
Both Human
and Dog
inherit from Animal
. Objects that implement
either of those two interfaces will thus have a name
attribute.
[Exposed =Window ]interface Animal {attribute DOMString name ; }; [Exposed =Window ]interface Human :Animal {attribute Dog ?pet ; }; [Exposed =Window ]interface Dog :Animal {attribute Human ?owner ; };
The following IDL fragment defines simplified versions of a DOM interfaces and a callback interface.
[Exposed =Window ]interface Node {readonly attribute DOMString nodeName ;readonly attribute Node ?parentNode ;Node appendChild (Node newChild );undefined addEventListener (DOMString type ,EventListener listener ); };callback interface EventListener {undefined handleEvent (Event event ); };
Plain objects can implement a callback interface like
EventListener
:
var node= getNode(); // Obtain an instance of Node. var listener= { handleEvent: function ( event) { // ... } }; node. addEventListener( "click" , listener); // This works. node. addEventListener( "click" , function () { ... }); // As does this.
It is not possible for such an object to implement an interface like
Node
, however:
var node= getNode(); // Obtain an instance of Node. var newNode= { nodeName: "span" , parentNode: null , appendChild: function ( newchild) { // ... }, addEventListener: function ( type, listener) { // ... } }; node. appendChild( newNode); // This will throw a TypeError exception.
2.3. Interface mixins
An interface mixin is a definition (matching
interface mixin identifier { /* mixin_members... */ };
Note: Interface mixins, much like partial interfaces, are intended for use as a specification editorial aide, allowing a coherent set of functionalities to be grouped together, and included in multiple interfaces, possibly across documents. They are not meant to be exposed through language bindings. Guidance on when to choose partial interfaces, interface mixins, or partial interface mixins can be found in § 2.3.1 Using mixins and partials.
An interface mixin is a specification of a set of interface mixin members
(matching
These constants, regular operations, regular attributes, and stringifiers describe the behaviors that can be implemented by an object, as if they were specified on the interface that includes them.
Static attributes, static operations, special operations, and iterable, asynchronously iterable, maplike, and setlike declarations cannot appear in interface mixin declarations.
As with interfaces, the IDL for interface mixins can be split into multiple parts by using
partial interface mixin definitions
(matching
interface mixin SomeMixin { /* mixin_members... */ };partial interface mixin SomeMixin { /* mixin_members... */ };
The order that members appear in has significance for property enumeration in the JavaScript binding.
Note that unlike interfaces or dictionaries, interface mixins do not create types.
Of the extended attributes defined in this specification,
only the [CrossOriginIsolated
], [Exposed
], and [SecureContext
] extended attributes
are applicable to interface mixins.
An includes statement is a definition
(matching
interface_identifier includes mixin_identifier ;
The first identifier must reference a interface I. The second identifier must reference an interface mixin M.
Each member of M is considered to be a member of each interface I, J, K, … that includes M, as if a copy of each member had been made. So for a given member m of M, interface I is considered to have a member mI, interface J is considered to have a member mJ, interface K is considered to have a member mK, and so on. The host interfaces of mI, mJ, and mK, are I, J, and K respectively.
Note: In JavaScript, this implies that each regular operation declared as a member of interface mixin M, and exposed as a data property with a built-in function object value, is a distinct built-in function object in each interface prototype object whose associated interface includes M. Similarly, for attributes, each copy of the accessor property has distinct built-in function objects for its getters and setters.
The order of appearance of includes statements affects the order in which interface mixin are included by their host interface.
Member order isn’t clearly specified, in particular when interface mixins are defined in separate documents. It is discussed in issue #432.
No extended attributes defined in this specification are applicable to includes statements.
The following IDL fragment defines an interface, Entry
,
and an interface mixin, Observable
.
The includes statement specifies that
Observable
’s members
are always included on objects implementing Entry
.
interface Entry {readonly attribute unsigned short entryType ; // ... };interface mixin Observable {undefined addEventListener (DOMString type ,EventListener listener ,boolean useCapture ); // ... };Entry includes Observable ;
A JavaScript implementation would thus have an addEventListener
property in the prototype chain of every Entry
:
var e= getEntry(); // Obtain an instance of Entry. typeof e. addEventListener; // Evaluates to "function".
CallbackOrInterfaceOrMixin ::callback CallbackRestOrInterface interface InterfaceOrMixin
Partial ::partial PartialDefinition
MixinRest ::mixin identifier { MixinMembers } ;
MixinMembers ::ExtendedAttributeList MixinMember MixinMembers ε
MixinMember ::Const RegularOperation Stringifier OptionalReadOnly AttributeRest
IncludesStatement ::identifier includes identifier ;
2.3.1. Using mixins and partials
This section is informative.
Interface mixins allow the sharing of attributes, constants, and operations across multiple interfaces. If you’re only planning to extend a single interface, you might consider using a partial interface instead.
For example, instead of:
interface mixin WindowSessionStorage {readonly attribute Storage sessionStorage ; };Window includes WindowSessionStorage ;
do:
partial interface Window {readonly attribute Storage sessionStorage ; };
Additionally, you can rely on extending interface mixins exposed by other specifications to target common use cases, such as exposing a set of attributes, constants, or operations across both window and worker contexts.
For example, instead of the common but verbose:
interface mixin GlobalCrypto {readonly attribute Crypto crypto ; };Window includes GlobalCrypto ;WorkerGlobalScope includes GlobalCrypto ;
you can extend the WindowOrWorkerGlobalScope
interface mixin using a partial interface mixin:
partial interface mixin WindowOrWorkerGlobalScope {readonly attribute Crypto crypto ; };
2.4. Callback interfaces
A callback interface is a definition matching
Note: A callback interface is not an interface. The name and syntax are left over from earlier versions of this standard, where these concepts had more in common.
A callback interface is a specification of a set of
callback interface members
(matching
callback interface identifier { /* interface_members... */ };
Note: See also the similarly named callback function definition.
Callback interfaces must define exactly one regular operation.
Specification authors should not define callback interfaces unless required to describe the requirements of existing APIs. Instead, a callback function should be used.
The definition of EventListener
as a
callback interface
is an example of an existing API that needs to allow
objects with a
given property (in this case handleEvent
) to be considered to implement the interface.
For new APIs, and those for which there are no compatibility concerns,
using a callback function will allow
only a function object (in the JavaScript
language binding).
Callback interfaces which declare constants
must be annotated with an [Exposed
] extended attribute.
CallbackRestOrInterface ::CallbackRest interface identifier { CallbackInterfaceMembers } ;
CallbackInterfaceMembers ::ExtendedAttributeList CallbackInterfaceMember CallbackInterfaceMembers ε
CallbackInterfaceMember ::Const RegularOperation
2.5. Members
Interfaces, interface mixins, and namespaces are specifications of a set of
members (respectively matching
When an interface includes an interface mixin, each member of the interface mixin is also considered a member of the interface. In contrast, inherited interface members are not considered members of the interface.
The constructor steps, getter steps, setter steps, and method steps for the various members defined on an interface or interface mixin have access to a this value, which is an IDL value of the interface type that the member is declared on or that includes the interface mixin the member is declared on.
Setter steps also have access to the given value, which is an IDL value of the type the attribute is declared as.
Interfaces, interface mixins, callback interfaces and namespaces each support a different set of members, which are specified in § 2.2 Interfaces, § 2.3 Interface mixins, § 2.4 Callback interfaces, and § 2.6 Namespaces, and summarized in the following informative table:
2.5.1. Constants
A constant is a declaration (matching
Constants have in the past primarily been used to define named integer codes in the style of an enumeration. The Web platform is moving away from this design pattern in favor of the use of strings. Editors who wish to use this feature are strongly advised to discuss this by filing an issue before proceeding.
const type constant_identifier = 42;
The identifier of a
constant
must not be the same as the identifier
of another interface member or callback interface member
defined on the same interface or callback interface.
The identifier also must not
be "length
", "name
" or "prototype
".
Note: These three names are the names of properties that are defined on the interface object in the JavaScript language binding.
The type of a constant (matching
The
Note: These values – in addition to strings and the empty sequence – can also be used to specify the
default value of a dictionary member or
of an optional argument.
Note that strings, the
empty sequence
The value of the boolean literal tokens boolean
values
true
and false
.
The value of an
-
Let S be the sequence of scalar values matched by the
integer token. -
Let sign be −1 if S begins with U+002D (-), and 1 otherwise.
-
Let base be the base of the number based on the scalar values that follow the optional leading U+002D (-):
- U+0030 (0), U+0058 (X)
- U+0030 (0), U+0078 (x)
-
The base is 16.
- U+0030 (0)
-
The base is 8.
- Otherwise
-
The base is 10.
-
Let number be the result of interpreting all remaining scalar values following the optional leading U+002D (-) character and any scalar values indicating the base as an integer specified in base base.
-
Return sign × number.
The type of an
-
Let S be the sequence of scalar values matched by the
decimal token. -
Let result be the Mathematical Value that would be obtained if S were parsed as a JavaScript
NumericLiteral . -
If the
decimal token is being used as the value for afloat
orunrestricted float
, then the value of thedecimal token is the IEEE 754 single-precision floating point number closest to result. -
Otherwise, the
decimal token is being used as the value for adouble
orunrestricted double
, and the value of thedecimal token is the IEEE 754 double-precision floating point number closest to result. [IEEE-754]
The value of a constant value specified as
- Type
unrestricted float
, constant valueInfinity -
The value is the IEEE 754 single-precision positive infinity value.
- Type
unrestricted double
, constant valueInfinity -
The value is the IEEE 754 double-precision positive infinity value.
- Type
unrestricted float
, constant value-Infinity -
The value is the IEEE 754 single-precision negative infinity value.
- Type
unrestricted double
, constant value-Infinity -
The value is the IEEE 754 double-precision negative infinity value.
- Type
unrestricted float
, constant valueNaN -
The value is the IEEE 754 single-precision NaN value with the bit pattern 0x7fc00000.
- Type
unrestricted double
, constant valueNaN -
The value is the IEEE 754 double-precision NaN value with the bit pattern 0x7ff8000000000000.
The type of a float
or double
.
The value of the
If VT is the type of the value assigned to a constant, and DT is the type of the constant, dictionary member or optional argument itself, then these types must be compatible, which is the case if DT and VT are identical, or DT is a nullable type whose inner type is VT.
Constants are not associated with particular instances of the interface or callback interface on which they appear. It is language binding specific whether constants are exposed on instances.
The JavaScript language binding does however allow constants to be accessed through objects implementing the IDL interfaces on which the constants are declared. For example, with the following IDL:
[Exposed =Window ]interface A {const short rambaldi = 47; };
the constant value can be accessed in JavaScript either as
A.rambaldi
or instanceOfA.rambaldi
.
The following extended attributes are applicable to constants:
[CrossOriginIsolated
],
[Exposed
], and
[SecureContext
].
Const ::const ConstType identifier = ConstValue ;
ConstValue ::BooleanLiteral FloatLiteral integer
BooleanLiteral ::true false
FloatLiteral ::decimal -Infinity Infinity NaN
ConstType ::PrimitiveType identifier
The following IDL fragment demonstrates how constants of the above types can be defined.
[Exposed =Window ]interface Util {const boolean DEBUG =false ;const octet LF = 10;const unsigned long BIT_MASK = 0x0000fc00;const double AVOGADRO = 6.022e23; };
2.5.2. Attributes
An attribute is an interface member or namespace member
(matching
-
regular attributes, which are those used to declare that objects implementing the interface will have a data field member with the given identifier
interface interface_identifier {attribute type identifier ; }; -
static attributes, which are used to declare attributes that are not associated with a particular object implementing the interface
interface interface_identifier {static attribute type identifier ; };
If an attribute has no
The getter steps of an attribute attr should be introduced using text of the form “The
attr
getter steps are:” followed by a list, or “The attr
getter steps
are to” followed by an inline description.
The setter steps of an attribute attr should be introduced using text of the form “The
attr
setter steps are:” followed by a list, or “The attr
setter steps
are to” followed by an inline description.
Note: When defining getter steps, you implicitly have access to this. When defining setter steps, you implicitly have access to this and the given value.
The identifier of an
attribute
must not be the same as the identifier
of another interface member
defined on the same interface.
The identifier of a static attribute must not
be "prototype
".
The type of the attribute is given by the type (matching
The type of the attribute, after resolving typedefs, must not be a nullable or non-nullable version of any of the following types:
-
a union type that has a nullable or non-nullable sequence type, dictionary, or record as one of its flattened member types
The attribute is read only if the
interface interface_identifier {readonly attribute type identifier ; };
Attributes whose type is a promise type must be read only. Additionally, they cannot have
any of the extended attributes [LegacyLenientSetter
], [PutForwards
], [Replaceable
], or
[SameObject
].
A regular attribute
that is not read only
can be declared to inherit its getter
from an ancestor interface. This can be used to make a read only attribute
in an ancestor interface be writable on a derived interface. An attribute
inherits its getter if
its declaration includes
Note: The grammar ensures that
[Exposed =Window ]interface Ancestor {readonly attribute TheType theIdentifier ; }; [Exposed =Window ]interface Derived :Ancestor {inherit attribute TheType theIdentifier ; };
When the
interface interface_identifier {stringifier attribute DOMString identifier ; };
The following extended attributes
are applicable to regular and static attributes:
[CrossOriginIsolated
],
[Exposed
],
[SameObject
], and
[SecureContext
].
The following extended attributes
are applicable only to regular attributes:
[LegacyLenientSetter
],
[LegacyLenientThis
],
[PutForwards
],
[Replaceable
],
[LegacyUnforgeable
].
ReadOnlyMember ::readonly ReadOnlyMemberRest
ReadOnlyMemberRest ::AttributeRest MaplikeRest SetlikeRest
ReadWriteAttribute ::AttributeRest
InheritAttribute ::inherit AttributeRest
AttributeRest ::attribute TypeWithExtendedAttributes AttributeName ;
AttributeName ::AttributeNameKeyword identifier
AttributeNameKeyword ::required
OptionalReadOnly ::readonly ε
The following IDL fragment demonstrates how attributes can be declared on an interface:
[Exposed =Window ]interface Animal { // A simple attribute that can be set to any string value.readonly attribute DOMString name ; // An attribute whose value can be assigned to.attribute unsigned short age ; }; [Exposed =Window ]interface Person :Animal { // An attribute whose getter behavior is inherited from Animal, and need not be // specified in the description of Person.inherit attribute DOMString name ; };
2.5.3. Operations
An operation is an interface member, callback interface member or namespace member
(matching
-
regular operations, which are those used to declare that objects implementing the interface will have a method with the given identifier
interface interface_identifier {return_type identifier (/* arguments... */); }; -
special operations, which are used to declare special behavior on objects implementing the interface, such as object indexing and stringification
interface interface_identifier { /* special_keyword */return_type identifier (/* arguments... */); /* special_keyword */return_type (/* arguments... */); }; -
static operations, which are used to declare operations that are not associated with a particular object implementing the interface
interface interface_identifier {static return_type identifier (/* arguments... */); };
If an operation has an identifier but no
If an operation has no identifier, then it must be declared to be a special operation using one of the special keywords.
The identifier of a regular operation or static operation
must not be the same as the identifier
of a constant or attribute
defined on the same interface, callback interface or namespace.
The identifier of a static operation must not be "prototype
".
Note: The identifier can be the same as that of another operation on the interface, however. This is how operation overloading is specified.
Note: The identifier of a static operation can be the same as the identifier of a regular operation defined on the same interface.
The return type of the operation is given
by the type (matching
An operation’s arguments (matching
Note: For expressiveness, the identifier of an operation argument can also be specified
as one of the keywords matching the
If the
If the operation argument type, after resolving typedefs, is a nullable type, its inner type must not be a dictionary type.
interface interface_identifier {return_type identifier (type identifier ,type identifier /* , ... */); };
The identifier of each argument must not be the same as the identifier of another argument in the same operation declaration.
Each argument can be preceded by a list of
extended attributes (matching
interface interface_identifier {return_type identifier ([extended_attributes ]type identifier , [extended_attributes ]type identifier /* , ... */); };
The following IDL fragment demonstrates how regular operations can be declared on an interface:
[Exposed =Window ]interface Dimensions {attribute unsigned long width ;attribute unsigned long height ; }; [Exposed =Window ]interface Button { // An operation that takes no arguments and returns a boolean.boolean isMouseOver (); // Overloaded operations.undefined setDimensions (Dimensions size );undefined setDimensions (unsigned long width ,unsigned long height ); };
An operation or constructor operation is considered to be
variadic
if the final argument uses the
interface interface_identifier {return_type identifier (type ...identifier );return_type identifier (type identifier ,type ...identifier ); };
Extended attributes
that take an argument list
([LegacyFactoryFunction
], of those
defined in this specification) and callback functions
are also considered to be variadic
when the
The following IDL fragment defines an interface that has two variadic operations:
[Exposed =Window ]interface IntegerSet {readonly attribute unsigned long cardinality ;undefined union (long ...ints );undefined intersection (long ...ints ); };
In the JavaScript binding, variadic operations are implemented by functions that can accept the subsequent arguments:
var s= getIntegerSet(); // Obtain an instance of IntegerSet. s. union(); // Passing no arguments corresponding to 'ints'. s. union( 1 , 4 , 7 ); // Passing three arguments corresponding to 'ints'.
A binding for a language that does not support variadic functions might specify that an explicit array or list of integers be passed to such an operation.
An argument is considered to be an optional argument
if it is declared with the
interface interface_identifier {return_type identifier (type identifier ,optional type identifier ); };
Optional arguments can also have a default value
specified. If the argument’s identifier is followed by a U+003D (=)
and a value (matching
interface interface_identifier {return_type identifier (type identifier ,optional type identifier = "value"); };
It is strongly suggested not to use a default value
of boolean
-typed arguments,
as this can be confusing for authors who might otherwise expect the default
conversion of
If the type of an argument is a dictionary type or a union type that has a dictionary type as one of its flattened member types, and that dictionary type and its ancestors have no required members, and the argument is either the final argument or is followed only by optional arguments, then the argument must be specified as optional and have a default value provided.
This is to encourage API designs that do not require authors to pass an empty dictionary value when they wish only to use the dictionary’s default values.
Usually the default value provided will be
When a boolean literal token (
When the undefined
value.
-
Let S be the sequence of scalar values matched by the
string token with its leading and trailing U+0022 (") scalar values removed. -
Depending on the type of the argument:
DOMString
USVString
- an enumeration type
-
The value of the
string token is S. ByteString
-
Assert: S doesn’t contain any code points higher than U+00FF.
The value of the
string token is the isomorphic encoding of S.
If the type of the optional argument is an enumeration, then its default value if specified must be one of the enumeration’s values.
Optional argument default values can also be specified using the
two token value
Optional argument default values can also be specified using the two token value
The following IDL fragment defines an interface with a single operation that can be invoked with two different argument list lengths:
[Exposed =Window ]interface ColorCreator {object createColor (double v1 ,double v2 ,double v3 ,optional double alpha ); };
It is equivalent to an interface that has two overloaded operations:
[Exposed =Window ]interface ColorCreator {object createColor (double v1 ,double v2 ,double v3 );object createColor (double v1 ,double v2 ,double v3 ,double alpha ); };
The following IDL fragment defines an interface with an operation that takes a dictionary argument:
dictionary LookupOptions {boolean caseSensitive =false ; }; [Exposed =Window ]interface AddressBook {boolean hasAddressForName (USVString name ,optional LookupOptions options = {}); };
If hasAddressForName
is called with only one argument, the
second argument will be a default-initialized LookupOptions
dictionary, which will cause caseSensitive
to be set to
The following extended attributes are applicable to operations:
[CrossOriginIsolated
],
[Default
],
[Exposed
],
[LegacyUnforgeable
],
[NewObject
], and
[SecureContext
].
The method steps of an operation operation should be introduced using text of the form
“The operation(arg1, arg2, ...)
method
steps are:” followed by a list, or “The operation(arg1,
arg2, ...)
method steps are to” followed by an inline description.
Note: When defining method steps, you implicitly have access to this.
DefaultValue ::ConstValue string [ ] { } null undefined
Operation ::RegularOperation SpecialOperation
RegularOperation ::Type OperationRest
SpecialOperation ::Special RegularOperation
Special ::getter setter deleter
OperationRest ::OptionalOperationName ( ArgumentList ) ;
OptionalOperationName ::OperationName ε
OperationName ::OperationNameKeyword identifier
OperationNameKeyword ::includes
ArgumentList ::Argument Arguments ε
Arguments ::, Argument Arguments ε
Argument ::ExtendedAttributeList ArgumentRest
ArgumentRest ::optional TypeWithExtendedAttributes ArgumentName Default Type Ellipsis ArgumentName
ArgumentName ::ArgumentNameKeyword identifier
Ellipsis ::... ε
ArgumentNameKeyword ::attribute callback const constructor deleter dictionary enum getter includes inherit interface iterable maplike mixin namespace partial readonly required setlike setter static stringifier typedef unrestricted
2.5.3.1. toJSON
By declaring a toJSON
regular operation,
an interface specifies how to convert the objects that implement it to JSON types.
The toJSON
regular operation is reserved for this usage.
It must take zero arguments and return a JSON type.
The JSON types are:
-
nullable types whose inner type is a JSON type,
-
annotated types whose inner type is a JSON type,
-
union types whose member types are JSON types,
-
typedefs whose type being given a new name is a JSON type,
-
sequence types whose parameterized type is a JSON type,
-
frozen array types whose parameterized type is a JSON type,
-
dictionary types where the types of all members declared on the dictionary and all its inherited dictionaries are JSON types,
-
records where all of their values are JSON types,
-
interface types that have a
toJSON
operation declared on themselves or one of their inherited interfaces.
How the toJSON
regular operation
is made available on an object in a language binding,
and how exactly the JSON types are converted into a JSON string,
is language binding specific.
Note: In the JavaScript language binding,
this is done by exposing a toJSON
method
which returns the JSON type
converted into a JavaScript value
that can be turned into a JSON string
by the JSON.stringify()
function.
Additionally, in the JavaScript language binding,
the toJSON
operation can take a [Default
] extended attribute,
in which case the default toJSON steps are exposed instead.
The following IDL fragment defines an interface Transaction
that has a toJSON
method defined in prose:
[Exposed =Window ]interface Transaction {readonly attribute DOMString from ;readonly attribute DOMString to ;readonly attribute double amount ;readonly attribute DOMString description ;readonly attribute unsigned long number ;TransactionJSON toJSON (); };dictionary TransactionJSON {Account from ;Account to ;double amount ;DOMString description ; };
The toJSON
regular operation of Transaction
interface
could be defined as follows:
The toJSON()
method steps are:
-
Let json be a new map.
-
For each attribute identifier attr in « "from", "to", "amount", "description" »:
-
Let value be the result of running the getter steps of attr on this.
-
Set json[attr] to value.
-
-
Return json.
In the JavaScript language binding, there would exist a toJSON()
method on
Transaction
objects:
// Get an instance of Transaction. var txn= getTransaction(); // Evaluates to an object like this: // { // from: "Bob", // to: "Alice", // amount: 50, // description: "books" // } txn. toJSON(); // Evaluates to a string like this: // '{"from":"Bob","to":"Alice","amount":50,"description":"books"}' JSON. stringify( txn);
2.5.4. Constructor operations
If an interface has a constructor operation member (matching
Multiple constructor operations may appear on a given interface. For each constructor operation on the interface, there will be a way to attempt to construct an instance by passing the specified arguments.
The constructor steps of a constructor operation that is a member of an interface
named interface should be introduced using text of the form “The
new interface(arg1, arg2, ...)
constructor steps
are:” followed by a list, or “The
new interface(arg1, arg2, ...)
constructor steps
are to” followed by an inline description.
The constructor steps must do nothing, initialize the value passed as this, or throw an exception.
If the constructor does not initialize
this, one can write “The new Example(init)
constructor
steps are to do nothing.”
See § 3.7.1 Interface object for details on how a constructor operation is to be implemented.
The following IDL defines two interfaces. The second has constructor operations, while the first does not.
[Exposed =Window ]interface NodeList {Node item (unsigned long index );readonly attribute unsigned long length ; }; [Exposed =Window ]interface Circle {constructor ();constructor (double radius );attribute double r ;attribute double cx ;attribute double cy ;readonly attribute double circumference ; };
A JavaScript implementation supporting these interfaces would implement a [[Construct]]
internal method on the Circle
interface object which would return a
new object that implements the interface.
It would take either zero or one argument.
It is unclear whether the NodeList
interface
object would implement a [[Construct]] internal method. In any case, trying to use it as a
constructor will cause a TypeError
to be thrown. [whatwg/webidl Issue #698]
var x= new Circle(); // This uses the zero-argument constructor to create a // reference to a platform object that implements the // Circle interface. var y= new Circle( 1.25 ); // This also creates a Circle object, this time using // the one-argument constructor. var z= new NodeList(); // This would throw a TypeError, since no // constructor is declared.
Constructor ::constructor ( ArgumentList ) ;
Ellipsis ::... ε
ArgumentNameKeyword ::attribute callback const constructor deleter dictionary enum getter includes inherit interface iterable maplike mixin namespace partial readonly required setlike setter static stringifier typedef unrestricted
2.5.5. Stringifiers
When an interface has a stringifier, it indicates that objects
that implement the interface have a non-default conversion to a string. Stringifiers can be
specified using a
interface interface_identifier {stringifier ; };
Prose accompanying the interface must define the stringification behavior of the interface.
The DOMString
or USVString
.
It also must not be placed on
a static attribute.
interface interface_identifier {stringifier attribute DOMString identifier ; };
On a given interface, there must exist at most one stringifier.
Stringifier ::stringifier StringifierRest
StringifierRest ::OptionalReadOnly AttributeRest ;
The following IDL fragment
defines an interface that will stringify to the value of its
name
attribute:
[Exposed =Window ]interface Student {constructor ();attribute unsigned long id ;stringifier attribute DOMString name ; };
In the JavaScript binding, using a Student
object in a context where a string is expected will result in the
value of the object’s name
property being
used:
var s= new Student(); s. id= 12345678 ; s. name= '周杰倫' ; var greeting= 'Hello, ' + s+ '!' ; // Now greeting == 'Hello, 周杰倫!'.
The following IDL fragment defines an interface that has custom stringification behavior that is not specified in the IDL itself.
[Exposed =Window ]interface Student {constructor ();attribute unsigned long id ;attribute DOMString ?familyName ;attribute DOMString givenName ;stringifier ; };
Thus, there needs to be prose to explain the stringification behavior.
We assume that the familyName
and
givenName
attributes are backed by
family name and given name concepts, respectively.
The stringification behavior steps are:
A JavaScript implementation of the IDL would behave as follows:
var s= new Student(); s. id= 12345679 ; s. familyName= 'Smithee' ; s. givenName= 'Alan' ; var greeting= 'Hi ' + s; // Now greeting == 'Hi Alan Smithee'.
2.5.6. Special operations
A special operation is a declaration of a certain kind of special behavior on objects implementing the interface on which the special operation declarations appear. Special operations are declared by using a special keyword in an operation declaration.
There are three kinds of special operations. The table below indicates for a given kind of special operation what special keyword is used to declare it and what the purpose of the special operation is:
Special operation | Keyword | Purpose |
---|---|---|
Getters | Defines behavior for when an object is indexed for property retrieval. | |
Setters | Defines behavior for when an object is indexed for property assignment or creation. | |
Deleters | Defines behavior for when an object is indexed for property deletion. |
Not all language bindings support all of the four kinds of special object behavior. When special operations are declared using operations with no identifier, then in language bindings that do not support the particular kind of special operations there simply will not be such functionality.
The following IDL fragment defines an interface with a getter and a setter:
[Exposed =Window ]interface Dictionary {readonly attribute unsigned long propertyCount ;getter double (DOMString propertyName );setter undefined (DOMString propertyName ,double propertyValue ); };
In language bindings that do not support property getters and setters, objects implementing Dictionary will not have that special behavior.
Defining a special operation with an identifier is equivalent to separating the special operation out into its own declaration without an identifier. This approach is allowed to simplify method steps of an interface’s operations.
The following two interfaces are equivalent:
[Exposed =Window ]interface Dictionary {readonly attribute unsigned long propertyCount ;getter double getProperty (DOMString propertyName );setter undefined setProperty (DOMString propertyName ,double propertyValue ); };
[Exposed =Window ]interface Dictionary {readonly attribute unsigned long propertyCount ;double getProperty (DOMString propertyName );undefined setProperty (DOMString propertyName ,double propertyValue );getter double (DOMString propertyName );setter undefined (DOMString propertyName ,double propertyValue ); };
A given special keyword must not appear twice on an operation.
Getters and setters come in two varieties: ones that
take a DOMString
as a property name,
known as
named property getters and
named property setters,
and ones that take an unsigned long
as a property index, known as
indexed property getters and
indexed property setters.
There is only one variety of deleter:
named property deleters.
See § 2.5.6.1 Indexed properties
and § 2.5.6.2 Named properties
for details.
On a given interface, there must exist at most one named property deleter, and at most one of each variety of getter and setter.
If an interface has a setter of a given variety, then it must also have a getter of that variety. If it has a named property deleter, then it must also have a named property getter.
Special operations declared using operations must not be variadic nor have any optional arguments.
If an object implements more than one interface that defines a given special operation, then it is undefined which (if any) special operation is invoked for that operation.
2.5.6.1. Indexed properties
An interface that defines an indexed property getter is said to support indexed properties. By extension, a platform object is said to support indexed properties if it implements an interface that itself does.
If an interface supports indexed properties, then the interface definition must be accompanied by a description of what indices the object can be indexed with at any given time. These indices are called the supported property indices.
Interfaces that support indexed properties must define
an integer-typed attribute named "length
".
Indexed property getters must
be declared to take a single unsigned long
argument.
Indexed property setters must
be declared to take two arguments, where the first is an unsigned long
.
interface interface_identifier {getter type identifier (unsigned long identifier );setter type identifier (unsigned long identifier ,type identifier );getter type (unsigned long identifier );setter type (unsigned long identifier ,type identifier ); };
The following requirements apply to the definitions of indexed property getters and setters:
-
If an indexed property getter was specified using an operation with an identifier, then the value returned when indexing the object with a given supported property index is the value that would be returned by invoking the operation, passing the index as its only argument. If the operation used to declare the indexed property getter did not have an identifier, then the interface definition must be accompanied by a description of how to determine the value of an indexed property for a given index.
-
If an indexed property setter was specified using an operation with an identifier, then the behavior that occurs when indexing the object for property assignment with a given supported property index and value is the same as if the operation is invoked, passing the index as the first argument and the value as the second argument. If the operation used to declare the indexed property setter did not have an identifier, then the interface definition must be accompanied by a description of how to set the value of an existing indexed property and how to set the value of a new indexed property for a given property index and value.
Note that if an indexed property getter or setter is specified using an operation with an identifier, then indexing an object with an integer that is not a supported property index does not necessarily elicit the same behavior as invoking the operation with that index. The actual behavior in this case is language binding specific.
In the JavaScript language binding, a regular property lookup is done. For example, take the following IDL:
[Exposed =Window ]interface A {getter DOMString toWord (unsigned long index ); };
Assume that an object implementing A
has supported property indices
in the range 0 ≤ index < 2. Also assume that toWord is defined to return
its argument converted into an English word. The behavior when invoking the
operation with an out of range index
is different from indexing the object directly:
var a= getA(); a. toWord( 0 ); // Evalautes to "zero". a[ 0 ]; // Also evaluates to "zero". a. toWord( 5 ); // Evaluates to "five". a[ 5 ]; // Evaluates to undefined, since there is no property "5".
The following IDL fragment defines an interface
OrderedMap
which allows
retrieving and setting values by name or by index number:
[Exposed =Window ]interface OrderedMap {readonly attribute unsigned long size ;getter any getByIndex (unsigned long index );setter undefined setByIndex (unsigned long index ,any value );getter any get (DOMString name );setter undefined set (DOMString name ,any value ); };
Since all of the special operations are declared using
operations with identifiers, the only additional prose
that is necessary is that which describes what keys those sets
have. Assuming that the get()
operation is
defined to return OrderedMap
, then the following
two sentences would suffice:
An object map implementing
OrderedMap
supports indexed properties with indices in the range 0 ≤ index <map.size
.Such objects also support a named property for every name that, if passed to
get()
, would return a non-null value.
As described in § 3.9 Legacy platform objects,
a JavaScript implementation would create
properties on a legacy platform object implementing
OrderedMap
that correspond to
entries in both the named and indexed property sets.
These properties can then be used to interact
with the object in the same way as invoking the object’s
methods, as demonstrated below:
// Assume map is a legacy platform object implementing the OrderedMap interface. var map= getOrderedMap(); var x, y; x= map[ 0 ]; // If map.length > 0, then this is equivalent to: // // x = map.getByIndex(0) // // since a property named "0" will have been placed on map. // Otherwise, x will be set to undefined, since there will be // no property named "0" on map. map[ 1 ] = false ; // This will do the equivalent of: // // map.setByIndex(1, false) y= map. apple; // If there exists a named property named "apple", then this // will be equivalent to: // // y = map.get('apple') // // since a property named "apple" will have been placed on // map. Otherwise, y will be set to undefined, since there // will be no property named "apple" on map. map. berry= 123 ; // This will do the equivalent of: // // map.set('berry', 123) delete map. cake; // If a named property named "cake" exists, then the "cake" // property will be deleted, and then the equivalent to the // following will be performed: // // map.remove("cake")
2.5.6.2. Named properties
An interface that defines a named property getter is said to support named properties. By extension, a platform object is said to support named properties if it implements an interface that itself does.
If an interface supports named properties, then the interface definition must be accompanied by a description of the ordered set of names that can be used to index the object at any given time. These names are called the supported property names.
Named property getters and deleters must
be declared to take a single DOMString
argument.
Named property setters must
be declared to take two arguments, where the first is a DOMString
.
interface interface_identifier {getter type identifier (DOMString identifier );setter type identifier (DOMString identifier ,type identifier );deleter type identifier (DOMString identifier );getter type (DOMString identifier );setter type (DOMString identifier ,type identifier );deleter type (DOMString identifier ); };
The following requirements apply to the definitions of named property getters, setters and deleters:
-
If a named property getter was specified using an operation with an identifier, then the value returned when indexing the object with a given supported property name is the value that would be returned by invoking the operation, passing the name as its only argument. If the operation used to declare the named property getter did not have an identifier, then the interface definition must be accompanied by a description of how to determine the value of a named property for a given property name.
-
If a named property setter was specified using an operation with an identifier, then the behavior that occurs when indexing the object for property assignment with a given supported property name and value is the same as if the operation is invoked, passing the name as the first argument and the value as the second argument. If the operation used to declare the named property setter did not have an identifier, then the interface definition must be accompanied by a description of how to set the value of an existing named property and how to set the value of a new named property for a given property name and value.
-
If a named property deleter was specified using an operation with an identifier, then the behavior that occurs when indexing the object for property deletion with a given supported property name is the same as if the operation is invoked, passing the name as the only argument. If the operation used to declare the named property deleter did not have an identifier, then the interface definition must be accompanied by a description of how to delete an existing named property for a given property name.
Note: As with indexed properties, if an named property getter, setter or deleter is specified using an operation with an identifier, then indexing an object with a name that is not a supported property name does not necessarily elicit the same behavior as invoking the operation with that name; the behavior is language binding specific.
2.5.7. Static attributes and operations
Static attributes and
static operations are ones that
are not associated with a particular instance of the
interface
on which it is declared, and is instead associated with the interface
itself. Static attributes and operations are declared by using the
It is language binding specific whether it is possible to invoke a static operation or get or set a static attribute through a reference to an instance of the interface.
StaticMember ::static StaticMemberRest
StaticMemberRest ::OptionalReadOnly AttributeRest RegularOperation
The following IDL fragment defines an interface
Circle
that has a static
operation declared on it:
[Exposed =Window ]interface Point { /* ... */ }; [Exposed =Window ]interface Circle {attribute double cx ;attribute double cy ;attribute double radius ;static readonly attribute long triangulationCount ;static Point triangulate (Circle c1 ,Circle c2 ,Circle c3 ); };
In the JavaScript language binding, the function object for
triangulate
and the accessor property for triangulationCount
will exist on the interface object
for Circle
:
var circles= getCircles(); // an Array of Circle objects typeof Circle. triangulate; // Evaluates to "function" typeof Circle. triangulationCount; // Evaluates to "number" Circle. prototype. triangulate; // Evaluates to undefined Circle. prototype. triangulationCount; // Also evaluates to undefined circles[ 0 ]. triangulate; // As does this circles[ 0 ]. triangulationCount; // And this // Call the static operation var triangulationPoint= Circle. triangulate( circles[ 0 ], circles[ 1 ], circles[ 2 ]); // Find out how many triangulations we have done window. alert( Circle. triangulationCount);
2.5.8. Overloading
If a regular operation or static operation defined on an interface has an identifier that is the same as the identifier of another operation on that interface of the same kind (regular or static), then the operation is said to be overloaded. When the identifier of an overloaded operation is used to invoke one of the operations on an object that implements the interface, the number and types of the arguments passed to the operation determine which of the overloaded operations is actually invoked. Constructor operations can be overloaded too. There are some restrictions on the arguments that overloaded operations and constructors can be specified to take, and in order to describe these restrictions, the notion of an effective overload set is used.
A set of overloaded operations must either:
-
contain no operations whose return type is a promise type;
-
only contain operations whose return type is a promise type.
Operations must not be overloaded across interface, partial interface, interface mixin, and partial interface mixin definitions.
For example, the overloads for both f and g are disallowed:
[Exposed =Window ]interface A {undefined f (); };partial interface A {undefined f (double x );undefined g (); };partial interface A {undefined g (DOMString x ); };
Note that constructor operations and
[LegacyFactoryFunction
]
extended attributes are disallowed from appearing
on partial interface definitions,
so there is no need to also disallow overloading for constructors.
An effective overload set
represents the allowable invocations for a particular
operation,
constructor (specified with a constructor operation
or [LegacyFactoryFunction
]), or
callback function.
The algorithm to compute the effective overload set
operates on one of the following four types of IDL constructs, and listed with them below are
the inputs to the algorithm needed to compute the set.
- For regular operations
- For static operations
-
-
the interface on which the operations are to be found
-
the identifier of the operations
-
the number of arguments to be passed
-
- For constructors
-
-
the interface on which the constructor operations are to be found
-
the number of arguments to be passed
-
- For legacy factory functions
-
-
the interface on which the [
LegacyFactoryFunction
] extended attributes are to be found -
the identifier of the legacy factory function
-
the number of arguments to be passed
-
An effective overload set is used, among other things, to determine whether there are ambiguities in the overloaded operations and constructors specified on an interface.
The items of an effective overload set are tuples of the form (callable, type list, optionality list) whose items are described below:
-
A callable is an operation if the effective overload set is for regular operations, static operations, or constructor operations; and it is an extended attribute if the effective overload set is for legacy factory functions.
-
A type list is a list of IDL types.
-
An optionality list is a list of three possible optionality values – "required", "optional" or "variadic" – indicating whether the argument at a given index was declared as being optional or corresponds to a variadic argument.
Each tuple represents an allowable invocation of the operation, constructor, or callback function with an argument value list of the given types. Due to the use of optional arguments and variadic operations and constructors, there may be multiple items in an effective overload set identifying the same operation or constructor.
-
the identifier of the operation or legacy factory function is A
-
the argument count is N
-
the interface is I
Whenever an argument of an extended attribute is mentioned, it is referring to an argument of the extended attribute’s named argument list.
-
Let S be an ordered set.
-
Let F be an ordered set with items as follows, according to the kind of effective overload set:
- For regular operations
-
The elements of F are the regular operations with identifier A defined on interface I.
- For static operations
-
The elements of F are the static operations with identifier A defined on interface I.
- For constructors
-
The elements of F are the constructor operations on interface I.
- For legacy factory functions
-
The elements of F are the [
LegacyFactoryFunction
] extended attributes on interface I whose named argument lists' identifiers are A.
-
Let maxarg be the maximum number of arguments the operations, legacy factory functions, or callback functions in F are declared to take. For variadic operations and legacy factory functions, the argument on which the ellipsis appears counts as a single argument.
Note: So
undefined f(long x, long... y);
is considered to be declared to take two arguments. -
Let max be max(maxarg, N).
-
For each operation or extended attribute X in F:
-
Let arguments be the list of arguments X is declared to take.
-
Let n be the size of arguments.
-
Let types be a type list.
-
Let optionalityValues be an optionality list.
-
For each argument in arguments:
-
If X is declared to be variadic, then:
-
Let i be n − 1.
-
While i ≥ 0:
-
If arguments[i] is not optional (i.e., it is not marked as "optional" and is not a final, variadic argument), then break.
-
Let t be a type list.
-
Let o be an optionality list.
-
Append the tuple (X, t, o) to S.
Note: if i is 0, this means to add to S the tuple (X, « », « »); (where "« »" represents an empty list).
-
Set i to i − 1.
-
-
-
Return S.
For the following interface:
[Exposed =Window ]interface A { /* f1 */undefined f (DOMString a ); /* f2 */undefined f (Node a ,DOMString b ,double ...c ); /* f3 */undefined f (); /* f4 */undefined f (Event a ,DOMString b ,optional DOMString c ,double ...d ); };
assuming Node
and Event
are two other interfaces of which no object can implement both,
the effective overload set
for regular operations with
identifier f
and argument count 4 is:
« (f1, « DOMString », « required »), (f2, « Node, DOMString », « required, required »), (f2, « Node, DOMString, double », « required, required, variadic »), (f2, « Node, DOMString, double, double », « required, required, variadic, variadic »), (f3, « », « »), (f4, « Event, DOMString », « required, required »), (f4, « Event, DOMString, DOMString », « required, required, optional »), (f4, « Event, DOMString, DOMString, double », « required, required, optional, variadic ») »
Two types are distinguishable if the following algorithm returns true.
-
If one type includes a nullable type and the other type either includes a nullable type, is a union type with flattened member types including a dictionary type, or is a dictionary type, return false.
-
If both types are either a union type or nullable union type, return true if each member type of the one is distinguishable with each member type of the other, or false otherwise.
-
If one type is a union type or nullable union type, return true if each member type of the union type is distinguishable with the non-union type, or false otherwise.
-
Consider the two "innermost" types derived by taking each type’s inner type if it is an annotated type, and then taking its inner type inner type if the result is a nullable type. If these two innermost types appear or are in categories appearing in the following table and there is a “●” mark in the corresponding entry or there is a letter in the corresponding entry and the designated additional requirement below the table is satisfied, then return true. Otherwise return false.
Categories:
- interface-like
- dictionary-like
- sequence-like
undefinedbooleannumeric typesbigintstring typesobjectsymbolinterface-likecallback functiondictionary-likeasync sequencesequence-likeundefined ● ● ● ● ● ● ● ● ● ● boolean ● ● ● ● ● ● ● ● ● ● numeric types (b) ● ● ● ● ● ● ● ● bigint ● ● ● ● ● ● ● ● string types ● ● ● ● ● (d) ● object ● symbol ● ● ● ● ● interface-like (a) ● ● ● ● callback function (c) ● ● dictionary-like ● ● async sequence sequence-like -
The two identified interface-like types are not the same, and no single platform object implements both interface-like types.
-
The types are distinguishable, but there is a separate restriction on their use in overloading below. Please also note the advice about using unions of these types.
-
A callback function that does not have [
LegacyTreatNonObjectAsNull
] extended attribute is distinguishable from a type in the dictionary-like category.For example, when converting an ECMAScript value to union type which includes a callback function and a dictionary-like type, if the value is callable, then it is converted to a callback function. Otherwise, it is converted to a dictionary-like type. -
The types are distinguishable, but when converting from an ECMAScript value, a string object is never converted to an async sequence type (even if it has a
%Symbol.iterator%
method), if a string type is also in the overload set or union.
double
andDOMString
are distinguishable because there is a ● at the intersection of numeric types with string types.double
andlong
are not distinguishable because they are both numeric types, and there is no ● or letter at the intersection of numeric types with numeric types.Given:callback interface CBIface {undefined handle (); }; [Exposed =Window ]interface Iface {attribute DOMString attr2 ; };dictionary Dict {DOMString field1 ; };CBIface
is distinguishable fromIface
because there’s a ● at the intersection of dictionary-like and interface-like, but it is not distinguishable fromDict
because there’s no ● at the intersection of dictionary-like and itself.Promise types do not appear in the above table, and as a consequence are not distinguishable with any other type.
If there is more than one item in an effective overload set that has a given type list size, then for those items there must be an index i such that for each pair of items the types at index i are distinguishable. The lowest such index is termed the distinguishing argument index for the items of the effective overload set with the given type list size.
An effective overload set must not contain more than
one item with the same type list size, where one item has a
bigint
argument at the distinguishing argument index and another has a numeric type
argument at the distinguishing argument index.
Consider the effective overload set shown in the previous example.
There are multiple items in the set with type lists 2, 3 and 4.
For each of these type list size, the distinguishing argument index is 0, since Node
and
Event
are distinguishable.
The following use of overloading however is invalid:
[Exposed =Window ]interface B {undefined f (DOMString x );undefined f (USVString x ); };
In addition, for each index j, where j is less than the distinguishing argument index for a given type list size, the types at index j in all of the items' type lists must be the same, and the optionality values at index j in all of the items' optionality lists must be the same.
The following is invalid:
[Exposed =Window ]interface B { /* f1 */undefined f (DOMString w ); /* f2 */undefined f (long w ,double x ,Node y ,Node z ); /* f3 */undefined f (double w ,double x ,DOMString y ,Node z ); };
For argument count 4, the effective overload set is:
« (f1, « DOMString », « required »), (f2, « long, double, Node, Node », « required, required, required, required »), (f3, « double, double, DOMString, Node », « required, required, required, required ») »
Looking at items with type list size 4, the
distinguishing argument index
is 2, since Node
and
DOMString
are distinguishable.
However, since the arguments in these two overloads at index 0 are different,
the overloading is invalid.
2.5.8.1. Overloading vs. union types
This section is informative.
For specifications defining IDL operations, it might seem that overloads and a combination of union types and optional arguments have some feature overlap.
It is first important to note that overloads have different behaviors than union types or optional arguments, and one cannot be fully defined using the other (unless,
of course, additional prose is provided, which can defeat the purpose of the Web IDL type system).
For example, consider the stroke()
operations defined on the CanvasDrawPath
interface [HTML]:
interface CanvasDrawPathExcerpt {undefined stroke ();undefined stroke (Path2D path ); };
Per the JavaScript language binding, calling stroke(undefined)
on an object
implementing CanvasDrawPathExcerpt
would attempt to call the second
overload, yielding a TypeError
since Path2D
. However, if the operations were instead
defined with optional arguments and merged into one,
interface CanvasDrawPathExcerptOptional {undefined stroke (optional Path2D path ); };
the overload resolution algorithm would treat the path argument as missing
given the same call stroke(undefined)
, and not throw any exceptions.
Note: For this particular example, the latter behavior is actually what Web developers would
generally expect. If CanvasDrawPath
were to be designed today, optional arguments would be
used for stroke()
.
Additionally, there are semantic differences as well. Union types are usually used in the sense that "any of the types would work in about the same way". In contrast, overloaded operations are designed to map well to language features such as C++ overloading, and are usually a better fit for operations with more substantial differences in what they do given arguments of different types. However, in most cases, operations with such substantial differences are best off with different names to avoid confusion for Web developers, since the JavaScript language does not provide language-level overloading. As such, overloads are rarely appropriate for new APIs, instead often appearing in legacy APIs or in specialized circumstances.
That being said, we offer the following recommendations and examples in case of difficulties to determine what Web IDL language feature to use:
-
In the unusual case where the operation needs to return values of different types for different argument types, overloading will result in more expressive IDL fragments. This is almost never appropriate API design, and separate operations with distinct names usually are a better choice for such cases.
Suppose there is an operation
calculate()
that accepts along
,DOMString
, orCalculatableInterface
(an interface type) as its only argument, and returns a value of the same type as its argument. It would be clearer to write the IDL fragment using overloaded operations asinterface A {long calculate (long input );DOMString calculate (DOMString input );CalculatableInterface calculate (CalculatableInterface input ); };than using a union type with a typedef as
typedef (long or DOMString or CalculatableInterface )Calculatable ;interface A {Calculatable calculate (Calculatable input ); };which does not convey the fact that the return value is always of the same type as input.
If the specified
calculate()
is a new API and does not have any compatibility concerns, it is suggested to use different names for the overloaded operations, perhaps asinterface A {long calculateNumber (long input );DOMString calculateString (DOMString input );CalculatableInterface calculateCalculatableInterface (CalculatableInterface input ); };which allows Web developers to write explicit and unambiguous code.
-
When the operation has significantly different semantics for different argument types or lengths, overloading is preferred. Again, in such scenarios, it is usually better to create separate operations with distinct names, but legacy APIs sometimes follow this pattern.
As an example, the
supports(property, value)
andsupports(conditionText)
operations of theCSS
interface are defined as the following IDL fragment [CSS3-CONDITIONAL] [CSSOM].partial interface CSS {static boolean supports (CSSOMString property ,CSSOMString value );static boolean supports (CSSOMString conditionText ); };Using optional arguments one can rewrite the IDL fragment as follows:
partial interface CSSExcerptOptional {static boolean supports (CSSOMString propertyOrConditionText ,optional CSSOMString value ); };Even though the IDL is shorter in the second version, two distinctively different concepts are conflated in the first argument. Without overloads, the question "is property or conditionText paired with value?" is much more difficult to answer without reading the method steps of the operation. This makes the second version remarkably less readable than the first.
Another consideration is that the method steps for overloaded operations can be specified in separate blocks, which can aid in both reading and writing specifications. This is not the case for optional arguments. This means that in the first case the specification author can write the method steps of the operations as:
The
supports(property, value)
method steps are:-
…
The
supports(conditionText)
method steps are:-
…
Yet using value as an optional argument, the specification author has to use more boilerplate-style text to effectively replicate the overload resolution algorithm.
The
supports(propertyOrConditionText, value)
method steps are:-
If value is given, then:
-
Let property be propertyOrConditionText.
-
…
-
-
Otherwise:
-
Let conditionText be propertyOrConditionText.
-
…
-
If the two overloads have little to no shared parts, it is better to leave overload resolution to the IDL mechanism.
-
-
If the operation accepts multiple types for multiple arguments with no coupling between types of different arguments, union types can sometimes be the only viable solution.
typedef (long long or DOMString or CalculatableInterface )SupportedArgument ;interface A {undefined add (SupportedArgument operand1 ,SupportedArgument operand2 ); };For the
add()
operation above, to specify it using overloads would requireinterface A {undefined add (long long operand1 ,long long operand2 );undefined add (long long operand1 ,DOMString operand2 );undefined add (long long operand1 ,CalculatableInterface operand2 );undefined add (DOMString operand1 ,long long operand2 );undefined add (DOMString operand1 ,DOMString operand2 );undefined add (DOMString operand1 ,CalculatableInterface operand2 );undefined add (CalculatableInterface operand1 ,long long operand2 );undefined add (CalculatableInterface operand1 ,DOMString operand2 );undefined add (CalculatableInterface operand1 ,CalculatableInterface operand2 ); };and nine times the corresponding prose!
-
Specification authors are encouraged to treat missing argument and
undefined argument the same way in the JavaScript language binding.Given the following IDL fragment:
interface A {undefined foo ();undefined foo (Node ?arg ); };Using the JavaScript language binding, calling
foo(undefined)
andfoo(null)
would both run the steps corresponding to thefoo(arg)
operation, with arg set to null, whilefoo()
alone would go to the first overload. This can be a surprising behavior for many API users. Instead, specification authors are encouraged to use an optional argument, which would categorize bothfoo()
andfoo(undefined)
as "arg is missing".interface A {undefined foo (optional Node ?arg ); };In general, optionality is best expressed using the
optional keyword, and not using overloads.
When the case fits none of the categories above, it is up to the specification author to choose the style, since it is most likely that either style would sufficiently and conveniently describe the intended behavior. However, the definition and conversion algorithms of union types and optional arguments are simpler to implement and reason about than those of overloads, and usually result in more idiomatic APIs in the JavaScript language binding. Thus, unless any other considerations apply, union types, optional arguments, or both are the default choice.
Specifications are also free to mix and match union types and overloads, if the author finds it appropriate and convenient.
2.5.9. Iterable declarations
An interface can be declared to be
iterable by using an iterable declaration
(matching
interface interface_identifier {iterable <value_type >;iterable <key_type ,value_type >; };
Objects implementing an interface that is declared to be iterable support being iterated over to obtain a sequence of values.
Note: In the JavaScript language binding, an interface that is iterable
will have entries
, forEach
,
keys
, values
, and %Symbol.iterator%
properties on its interface prototype object.
If a single type parameter is given, then the interface has a value iterator and provides values of the specified type. If two type parameters are given, then the interface has a pair iterator and provides value pairs with the given types.
A value pair, given a key type and a value type, is a struct with two items:
-
an item whose name is "key", which is referred to as the value pair’s key, and whose value is an IDL value of the key type;
-
an item whose name is "value", which is referred to as the value pair’s value, and whose value is an IDL value of the value type.
A value iterator must only be declared on an interface that supports indexed properties. The value-type of the value iterator must be the same as the type returned by the indexed property getter. A value iterator is implicitly defined to iterate over the object’s indexed properties.
A pair iterator must not be declared on an interface that supports indexed properties.
Prose accompanying an interface with a pair iterator must define a list of value pairs for each instance of the interface, which is the list of value pairs to iterate over.
The JavaScript forEach method that is generated for a value iterator invokes its callback like Array.prototype.forEach does, and the forEach method for a pair iterator invokes its callback like Map.prototype.forEach does.
Since value iterators are currently allowed only on interfaces that support indexed properties, it makes sense to use an Array-like forEach method. There could be a need for value iterators (a) on interfaces that do not support indexed properties, or (b) with a forEach method that instead invokes its callback like Set.prototype.forEach (where the key is the same as the value). If you’re creating an API that needs such a forEach method, please file an issue.
Note: This is how array iterator objects work.
For interfaces that support indexed properties,
the iterator objects returned by entries
,
keys
, values
, and %Symbol.iterator%
are
actual array iterator objects.
Interfaces with an iterable declaration must not have any attributes, constants, or
regular operations named "entries
", "forEach
", "keys
", or
"values
", or have any inherited interfaces that have attributes, constants,
or regular operations with these names.
Consider the following interface SessionManager
, which allows access to
a number of Session
objects keyed by username:
[Exposed =Window ]interface SessionManager {Session getSessionForUser (DOMString username );iterable <DOMString ,Session >; }; [Exposed =Window ]interface Session {readonly attribute DOMString username ; // ... };
The behavior of the iterator could be defined like so:
The value pairs to iterate over are the list of value pairs with the key being the username and the value being the open
Session
object on theSessionManager
object corresponding to that username, sorted by username.
In the JavaScript language binding, the interface prototype object
for the SessionManager
interface
has a values
method that is a function, which, when invoked,
returns an iterator object that itself has a next
method that returns the
next value to be iterated over. It has keys
and entries
methods that iterate over the usernames of session objects
and username/Session
object pairs, respectively. It also has
a %Symbol.iterator%
method that allows a SessionManager
to be used in a for..of
loop that has the same value as the
entries
method:
// Get an instance of SessionManager. // Assume that it has sessions for two users, "anna" and "brian". var sm= getSessionManager(); typeof SessionManager. prototype. values; // Evaluates to "function" var it= sm. values(); // values() returns an iterator object String( it); // Evaluates to "[object SessionManager Iterator]" typeof it. next; // Evaluates to "function" // This loop will log "anna" and then "brian". for (;;) { let result= it. next(); if ( result. done) { break ; } let session= result. value; console. log( session. username); } // This loop will also log "anna" and then "brian". for ( let usernameof sm. keys()) { console. log( username); } // Yet another way of accomplishing the same. for ( let [ username, session] of sm) { console. log( username); }
An interface must not have more than one iterable declaration. The inherited interfaces of an interface with an iterable declaration must not also have an iterable declaration. An interface with an iterable declaration and its inherited interfaces must not have a maplike declaration, setlike declaration, or asynchronously iterable declaration.
The following extended attributes are applicable to iterable declarations:
[CrossOriginIsolated
],
[Exposed
], and
[SecureContext
].
Iterable ::iterable < TypeWithExtendedAttributes OptionalType > ;
OptionalType ::, TypeWithExtendedAttributes ε
2.5.10. Asynchronously iterable declarations
An interface can be declared to be asynchronously iterable by using an
asynchronously iterable declaration
(matching
interface interface_identifier {async_iterable <value_type >;async_iterable <value_type >(/* arguments... */);async_iterable <key_type ,value_type >;async_iterable <key_type ,value_type >(/* arguments... */); };
Objects that implement an interface that is declared to be asynchronously iterable support being iterated over asynchronously to obtain a sequence of values.
If a single type parameter is given, then the interface has a value asynchronously iterable declaration and asynchronously provides values of the specified type. If two type parameters are given, then the interface has a pair asynchronously iterable declaration and asynchronously provides value pairs with the given types.
If given, an asynchronously iterable declaration’s arguments (matching
%Symbol.asyncIterator%
and values
properties on its
interface prototype object. If the interface has a
pair asynchronously iterable declaration, it will additionally have
entries
and keys
properties. All of these
methods can be passed optional arguments, which correspond to the argument list in the
asynchronously iterable declaration, and are processed by the
asynchronous iterator initialization steps, if any exist.
With this in mind, the requirement that all arguments be optional ensures that, in the
JavaScript binding, for
-await
-of
can work directly on
instances of the interface, since for
-await
-of
calls the
%Symbol.asyncIterator%
method with no arguments.
Prose accompanying an interface with an asynchronously iterable declaration must define a
get the next iteration result algorithm.
This algorithm receives the instance of the interface that is being iterated, as well as the
async iterator itself (which can be useful for storing state).
It must return a Promise
that either rejects, resolves with a special end of
iteration value to signal the end of the iteration, or resolves with one of the following:
- for value asynchronously iterable declarations:
-
a value of the type given in the declaration;
- for pair asynchronously iterable declarations:
-
a tuple containing a value of the first type given in the declaration, and a value of the second type given in the declaration.
The prose may also define an asynchronous iterator return algorithm. This
algorithm receives the instance of the interface that is being iterated, the async iterator
itself, and a single argument value of type any
. This algorithm is invoked in the case of
premature termination of the async iterator. It must return a Promise
; if that promise
fulfills, its fulfillment value will be ignored, but if it rejects, that failure will be passed on
to users of the async iterator API.
In the JavaScript binding, this algorithm allows customizing the behavior when the
async iterator’s return()
method is invoked. This most commonly occurs when a
break
or return
statement causes an exit from a
for
-await
-of
loop.
We could add a similar hook for throw()
. So far there has been no need,
but if you are creating an API that needs such capabilities, please
file an issue.
The prose may also define asynchronous iterator initialization steps. These receive the instance of the interface being iterated, the newly-created iterator object, and a list of IDL values representing the arguments passed, if any.
Interfaces with an asynchronously iterable declaration must not have any attributes,
constants, or regular operations named "entries
", "keys
", or
"values
", or have any inherited interfaces that have attributes, constants,
or regular operations with these names.
Consider the following interface SessionManager
, which allows access
to a number of Session
objects keyed by username:
[Exposed =Window ]interface SessionManager {Session getSessionForUser (DOMString username );async_iterable <DOMString ,Session >; }; [Exposed =Window ]interface Session {readonly attribute DOMString username ; // ... };
The behavior of the iterator could be defined like so:
The asynchronous iterator initialization steps for a
SessionManager
async iterator iterator are:
Set iterator’s current state to "not yet started".
To get the next iteration result for a
SessionManager
manager and its async iterator iterator:
Let promise be a new promise.
Let key be the following value, if it exists, or null otherwise:
- If iterator’s current state is "not yet started"
the smallest username in manager’s open sessions, in lexicographical order
- Otherwise
the smallest username in manager’s open sessions that is greater than iterator’s current state, in lexicographical order
Note: iterator’s current state might no longer be present in the open sessions.
If key is null, then:
Resolve promise with end of iteration.
Otherwise:
Let session be the
Session
object corresponding to key.Resolve promise with (username, session).
Set iterator’s current state to username.
Return promise.
In the JavaScript language binding, the interface prototype object for the
SessionManager
interface has a values
method that is a
function, which, when invoked, returns an asynchronous iterator object that itself has a
next
method that returns the next value to be iterated over.
It has keys
and entries
methods that iterate over the usernames of
session objects and (username, Session
) object pairs, respectively.
It also has a %Symbol.asyncIterator%
method that allows a SessionManager
to be used in a for await..of
loop that has the same value as the
entries
method:
// Get an instance of SessionManager. // Assume that it has sessions for two users, "anna" and "brian". var sm= getSessionManager(); typeof SessionManager. prototype. values; // Evaluates to "function" var it= sm. values(); // values() returns an iterator object typeof it. next; // Evaluates to "function" // This loop will log "anna" and then "brian". for await ( let usernameof sm. keys()) { console. log( username); } // Yet another way of accomplishing the same. for await ( let [ username, session] of sm) { console. log( username); }
An interface must not have more than one asynchronously iterable declaration. The inherited interfaces of an interface with an asynchronously iterable declaration must not also have an asynchronously iterable declaration. An interface with an asynchronously iterable declaration and its inherited interfaces must not have a maplike declaration, setlike declaration, or iterable declaration.
The following extended attributes are applicable to asynchronously iterable declarations:
[CrossOriginIsolated
],
[Exposed
], and
[SecureContext
].
these extended attributes are not currently taken into account. When they are, the effect will be as you would expect.
AsyncIterable ::async_iterable < TypeWithExtendedAttributes OptionalType > OptionalArgumentList ;
OptionalArgumentList ::( ArgumentList ) ε
2.5.11. Maplike declarations
An interface can be declared to be
maplike by using a maplike declaration
(matching
interface interface_identifier {readonly maplike <key_type ,value_type >;maplike <key_type ,value_type >; };
Objects implementing an interface that is declared to be maplike represent an ordered map of key–value pairs, initially empty, known as that object’s map entries. The types used for the keys and values are given in the angle brackets of the maplike declaration. Keys are required to be unique.
Specification authors can modify the contents of the map entries, which will automatically be reflected in the contents of the object as observed by JavaScript code.
Maplike interfaces support an API for querying the map entries
appropriate for the language binding. If the
Note: In the JavaScript language binding, the API for interacting
with the map entries is similar to that available on JavaScript
Map
objects. If the entries
,
forEach
, get
,
has
, keys
,
values
, %Symbol.iterator%
methods, and a
size
getter.
For read–write maplikes, it also includes clear
,
delete
, and set
methods.
Maplike interfaces must not have any attributes, constants, or regular operations named
"entries
", "forEach
", "get
", "has
",
"keys
", "size
", or "values
", or have any
inherited interfaces that have attributes, constants, or regular operations with
these names.
Read–write maplike interfaces must not
have any attributes
or constants named
"clear
", "delete
",
or "set
", or have any inherited interfaces
that have attributes or constants with these names.
Note: Read-write maplike interfaces can have regular operations named
"clear
", "delete
", or "set
", which will override the default
implementation of those methods (defined in § 3.7.11 Maplike declarations). If such regular operations are
defined, they must match the input and output expectations of each method, defined in their default
implementation sections.
An interface must not have more than one maplike declaration. The inherited interfaces of a maplike interface must not also have a maplike declaration. A maplike interface and its inherited interfaces must not have an iterable declaration, an asynchronously iterable declaration, a setlike declaration, or an indexed property getter.
ReadOnlyMember ::readonly ReadOnlyMemberRest
ReadWriteMaplike ::MaplikeRest
MaplikeRest ::maplike < TypeWithExtendedAttributes , TypeWithExtendedAttributes > ;
No extended attributes defined in this specification are applicable to maplike declarations.
Add example.
2.5.12. Setlike declarations
An interface can be declared to be
setlike by using a setlike declaration
(matching
interface interface_identifier {readonly setlike <type >;setlike <type >; };
Objects implementing an interface that is declared to be setlike represent an ordered set of values, initially empty, known as that object’s set entries. The type of the values is given in the angle brackets of the setlike declaration. Values are required to be unique.
Specification authors can modify the contents of the set entries, which will automatically be reflected in the contents of the object as observed by JavaScript code.
Setlike interfaces support an API for querying the set entries
appropriate for the language binding. If the
Note: In the JavaScript language binding, the API for interacting
with the set entries is similar to that available on JavaScript
Set
objects. If the entries
,
forEach
, has
,
keys
, values
,
%Symbol.iterator%
methods, and a size
getter.
For read–write setlikes, it also includes add
,
clear
, and delete
methods.
Setlike interfaces must not have any attributes, constants, or regular operations named
"entries
", "forEach
", "has
", "keys
",
"size
", or "values
", or have any inherited interfaces that have
attributes, constants, or regular operations with these names.
Read–write setlike interfaces must not
have any attributes
or constants named
"add
", "clear
",
or "delete
", or have any inherited interfaces
that have attributes or constants with these names.
Note: Read-write setlike interfaces can have regular operations named
"add
", "clear
", or "delete
", which will override the default
implementation of those methods (defined in § 3.7.12 Setlike declarations). If such regular operations are
defined, they must match the input and output expectations of each method, defined in their default
implementation sections.
An interface must not have more than one setlike declaration. The inherited interfaces of a setlike interface must not also have a setlike declaration. A setlike interface and its inherited interfaces must not have an iterable declaration, an asynchronously iterable declaration, a maplike declaration, or an indexed property getter.
ReadOnlyMember ::readonly ReadOnlyMemberRest
ReadWriteSetlike ::SetlikeRest
SetlikeRest ::setlike < TypeWithExtendedAttributes > ;
No extended attributes defined in this specification are applicable to setlike declarations.
Add example.
2.6. Namespaces
A namespace is a definition (matching
namespace identifier { /* namespace_members... */ };
A namespace is a specification of a set of
namespace members
(matching
As with interfaces, the IDL for namespaces can be split into multiple parts by using
partial namespace definitions
(matching
namespace SomeNamespace { /* namespace_members... */ };partial namespace SomeNamespace { /* namespace_members... */ };
Note: As with partial interface definitions, partial namespace definitions are intended for use as a specification editorial aide, allowing the definition of a namespace to be separated over more than one section of the document, and sometimes multiple documents.
The order that members appear in has significance for property enumeration in the JavaScript binding.
Note that unlike interfaces or dictionaries, namespaces do not create types.
Of the extended attributes defined in this specification, only the [CrossOriginIsolated
], [Exposed
], and [SecureContext
] extended attributes are applicable to namespaces.
Namespaces must be annotated with the [Exposed
] extended attribute.
Partial ::partial PartialDefinition
Namespace ::namespace identifier { NamespaceMembers } ;
NamespaceMembers ::ExtendedAttributeList NamespaceMember NamespaceMembers ε
NamespaceMember ::RegularOperation readonly AttributeRest Const
The following IDL fragment defines a namespace.
namespace VectorUtils {readonly attribute Vector unit ;double dotProduct (Vector x ,Vector y );Vector crossProduct (Vector x ,Vector y ); };
A JavaScript implementation would then expose a global VectorUtils
data property which was a simple object (with prototype
%Object.prototype%
) with enumerable data properties for each declared operation, and
enumerable get-only accessors for each declared attribute:
Object. getPrototypeOf( VectorUtils); // Evaluates to Object.prototype. Object. keys( VectorUtils); // Evaluates to ["dotProduct", "crossProduct"]. Object. getOwnPropertyDescriptor( VectorUtils, "dotProduct" ); // Evaluates to { value: <a function>, enumerable: true, configurable: true, writable: true }. Object. getOwnPropertyDescriptor( VectorUtils, "unit" ); // Evaluates to { get: <a function>, enumerable: true, configurable: true }.
2.7. Dictionaries
A dictionary is a definition (matching
dictionary identifier { /* dictionary_members... */ };
Dictionary instances do not retain a reference to their language-specific representations (e.g., the corresponding JavaScript object). So for example, returning a dictionary from an operation will result in a new JavaScript object being created from the current values of the dictionary. And, an operation that accepts a dictionary as an argument will perform a one-time conversion from the given JavaScript value into the dictionary, based on the current properties of the JavaScript object. Modifications to the dictionary will not be reflected in the corresponding JavaScript object, and vice-versa.
Dictionaries must not be used as the type of an attribute or constant.
A dictionary can be defined to inherit from another dictionary. If the identifier of the dictionary is followed by a colon and a identifier, then that identifier identifies the inherited dictionary. The identifier must identify a dictionary.
A dictionary must not be declared such that its inheritance hierarchy has a cycle. That is, a dictionary A cannot inherit from itself, nor can it inherit from another dictionary B that inherits from A, and so on.
dictionary Base { /* dictionary_members... */ };dictionary Derived :Base { /* dictionary_members... */ };
The inherited dictionaries of a given dictionary D is the set of all dictionaries that D inherits from, directly or indirectly. If D does not inherit from another dictionary, then the set is empty. Otherwise, the set includes the dictionary E that D inherits from and all of E’s inherited dictionaries.
Dictionary members can be specified as required, meaning that converting a language-specific value to a dictionary requires providing a value for that member. Any dictionary member that is not required is optional.
Note that specifying dictionary members as required only has an observable effect when converting other representations of dictionaries (like a JavaScript value supplied as an argument to an operation) to an IDL dictionary. Specification authors should leave the members optional in all other cases, including when a dictionary type is used solely as the return type of operations.
A given dictionary value of type D can have entries for each of the dictionary members defined on D and on any of D’s inherited dictionaries. Dictionary members that are specified as required, or that are specified as having a default value, will always have such corresponding entries. Other members' entries might or might not exist in the dictionary value.
In the JavaScript binding, a value of
As with operation argument default values, it is strongly
encouraged not to use boolean
-typed dictionary members, as this can be confusing for authors who might
otherwise expect the default conversion of
An ordered map with string keys can be implicitly treated as a dictionary value of a specific dictionary D if all of its entries correspond to dictionary members, as long as those entries have the correct types, and there are entries present for any required or defaulted dictionary members.
dictionary Descriptor {DOMString name ;sequence <unsigned long >serviceIdentifiers ; };
A Descriptor
dictionary could be created as in the following steps:
-
Let identifiers be « 1, 3, 7 ».
-
Return «[ "name" → "test", "serviceIdentifiers" → identifiers ]».
Each dictionary member (matching
If the type of the dictionary member, after resolving typedefs, is a nullable type, its inner type must not be a dictionary type.
dictionary identifier {type identifier ; };
If the identifier for an optional dictionary member is followed by a
U+003D (=) and a value (matching
dictionary identifier {type identifier = "value"; };
When a boolean literal token (
If the type of the dictionary member is an enumeration, then its default value if specified must be one of the enumeration’s values.
If the type of the dictionary member is preceded by the
dictionary identifier {required type identifier ; };
The type of a dictionary member must not include the dictionary it appears on. A type includes a dictionary D if at least one of the following is true:
-
the type is D
-
the type is a dictionary that inherits from D
-
the type is a nullable type whose inner type includes D
-
the type is a sequence type or frozen array whose element type includes D
-
the type is a union type, one of whose member types includes D
-
the type is a dictionary, one of whose members or inherited members has a type that includes D
-
the type is
record<K, V>
where V includes D
As with interfaces, the IDL for dictionaries can be split into multiple parts
by using partial dictionary definitions
(matching
dictionary SomeDictionary { /* dictionary_members... */ };partial dictionary SomeDictionary { /* dictionary_members... */ };
Note: As with partial interface definitions, partial dictionary definitions are intended for use as a specification editorial aide, allowing the definition of an interface to be separated over more than one section of the document, and sometimes multiple documents.
The order of the dictionary members on a given dictionary is such that inherited dictionary members are ordered before non-inherited members, and the dictionary members on the one dictionary definition (including any partial dictionary definitions) are ordered lexicographically by the Unicode codepoints that comprise their identifiers.
For example, with the following definitions:
dictionary B :A {long b ;long a ; };dictionary A {long c ;long g ; };dictionary C :B {long e ;long f ; };partial dictionary A {long h ;long d ; };
the order of the dictionary members
of a dictionary value of type C
is
c, d, g, h, a, b, e, f.
Dictionaries need to have their members ordered because in some language bindings the behavior observed when passing a dictionary value to a platform object depends on the order the dictionary members are fetched. For example, consider the following additional interface:
[Exposed =Window ]interface Something {undefined f (A a ); };
and this JavaScript code:
var something= getSomething(); // Get an instance of Something. var x= 0 ; var dict= { }; Object. defineProperty( dict, "d" , { get: function () { return ++ x; } }); Object. defineProperty( dict, "c" , { get: function () { return ++ x; } }); something. f( dict);
The order that the dictionary members are fetched in determines
what values they will be taken to have. Since the order for
A
is defined to be c then d,
the value for c will be 1 and the value for d will be 2.
The identifier of a dictionary member must not be the same as that of another dictionary member defined on the dictionary or on that dictionary’s inherited dictionaries.
No extended attributes are applicable to dictionaries.
Partial ::partial PartialDefinition
Dictionary ::dictionary identifier Inheritance { DictionaryMembers } ;
DictionaryMembers ::DictionaryMember DictionaryMembers ε
DictionaryMember ::ExtendedAttributeList DictionaryMemberRest
DictionaryMemberRest ::required TypeWithExtendedAttributes identifier ; Type identifier Default ;
PartialDictionary ::dictionary identifier { DictionaryMembers } ;
Default ::= DefaultValue ε
DefaultValue ::ConstValue string [ ] { } null undefined
Inheritance ::: identifier ε
One use of dictionary types is to allow a number of optional arguments to an operation without being constrained as to the order they are specified at the call site. For example, consider the following IDL fragment:
[Exposed =Window ]interface Point {constructor ();attribute double x ;attribute double y ; };dictionary PaintOptions {DOMString fillPattern = "black";DOMString strokePattern ;Point position ; }; [Exposed =Window ]interface GraphicsContext {undefined drawRectangle (double width ,double height ,optional PaintOptions options ); };
In a JavaScript implementation of the IDL, an Object
can be passed in for the optional
PaintOptions
dictionary:
// Get an instance of GraphicsContext. var ctx= getGraphicsContext(); // Draw a rectangle. ctx. drawRectangle( 300 , 200 , { fillPattern: "red" , position: new Point( 10 , 10 ) });
The members of PaintOptions
are optional.
If fillPattern
is omitted, the definition of
drawRectangle
can assume that it has the given default values
and not include explicit wording to handle its omission.
drawRectangle
needs to explicitly handle the case where
strokePattern
and position
are omitted.
2.8. Exceptions
An exception is a type of object that represents an error and
which can be thrown or treated as a first class value by implementations. Web IDL has a number of
pre-defined exceptions that specifications can reference and throw in their definition of
operations, attributes, and so on. Custom exception types can also be defined, as interfaces
that inherit from DOMException
.
A simple exception is identified by one of the following types:
-
EvalError
-
RangeError
-
ReferenceError
-
TypeError
-
URIError
These correspond to all of the JavaScript error objects (apart from
SyntaxError
and Error
, which are deliberately
omitted as they are reserved for use by the JavaScript parser and by authors, respectively). The
meaning of each simple exception matches its corresponding error object in the JavaScript
specification.
The second kind of exception is a DOMException
, which provides further
programmatically-introspectable detail on the error that occurred by giving a name.
Such names are drawn from the DOMException
names table below.
As DOMException
is an interface type, it can be used as a type in IDL. This
allows for example an operation to be declared to have a DOMException
return type. This
is generally a bad pattern, however, as exceptions are meant to be thrown and not returned.
The final kind of exception is a derived interface of DOMException
. These are more complicated,
and thus described in the dedicated section § 2.8.2 DOMException derived interfaces.
Simple exceptions can be
created by providing their
type name. A DOMException
can be created by providing its name
followed by DOMException
. Exceptions can also be
thrown, by providing the same details
required to create one. In both cases, the caller may provide additional information
about what the exception indicates, which is useful when constructing the exception’s message.
Here is are some examples of wording to use to create and throw exceptions.
To throw a new simple exception whose type is TypeError
:
To throw a new DOMException
with name "NotAllowedError
":
To create a new DOMException
with name "SyntaxError
":
Let object be a newly created "
SyntaxError
"DOMException
.
To reject a promise with a new DOMException
with name
"OperationError
":
Reject p with an "OperationError
"DOMException
.
An example of including additional information used to construct the exception message would be:
Throw a "SyntaxError
"DOMException
indicating that the given value had disallowed trailing spaces.
Such additional context is most helpful to implementers when it is not immediately obvious
why the exception is being thrown, e.g., because there are many different steps in the algorithm
which throw a "SyntaxError
" DOMException
. In contrast, if your specification throws a
"NotAllowedError
" DOMException
immediately after checking if the user has provided
permission to use a given feature, it’s fairly obvious what sort of message the implementation
ought to construct, and so specifying it is not necessary.
The resulting behavior from creating and throwing an exception is language binding specific.
See § 3.14.3 Creating and throwing exceptions for details on what creating and throwing an exception entails in the JavaScript language binding.
2.8.1. Base DOMException
error names
The DOMException
names table below lists all the
allowed names for instances of the base DOMException
interface, along with a
description of what such names mean, and legacy numeric error code values.
Interfaces inheriting from