A user account is required in order to edit this wiki, but we've had to disable public user registrations due to spam.
To request an account, ask an autoconfirmed user on Chat (such as one of these permanent autoconfirmed members).
Component Model IDL Brainstorming
Overview
Components, along with other web constructs such as workers and <iframe>
elements, may benefit from having their interfaces declared explicitly.
This would be especially useful in the case of confined components, who cannot be trusted to run scripts to effect subclassing and forwarding function calls between the hosting element and the shadow DOM.
The following is therefore a proposal for a declarative way to define the interface, that the browser then can safely inspect and use as a basis to set up such forwarding automatically.
Perceived Advantages
All of the following may look very verbose, and could probably be implemented much quicker in pure JavaScript. However, this approach still conceivably has the following advantages:
- Can be used where JavaScript cannot be run, e.g. due to confinement
- Browser can automatically use the definitions to set up and abstract away
postMessage()
calls, etc., that are necessary to bridge the separation - Browser can even use magic to handle the forwarding and does not have to rely on
postMessage()
or other functions available in JavaScript
- Browser can automatically use the definitions to set up and abstract away
- Not component-specific, could be used for workers,
<iframes>
, intents - Same definitions can be used cross-language (not limited to JavaScript)
Example
The following could be the IDL declaration for a contact in a contact list. User-defined names MUST start with an upper case letter.
<idl name="Contact" type="object"> <property name="firstName" type="string" optional/> <property name="lastName" type="string" optional/> <property name="name" type="string" get="getName"/> <property name="phone" type="string" optional/> <property name="email" type="string" optional/> </idl>
The contact list itself can make use of this type:
<idl name="ContactList" type="interface"> <function name="addContact" type="unsigned long"> <param name="contact" type="Contact" throws="OUT_OF_BOUNDS_EXCEPTION"/> <param name="index" type="unsigned integer" optional/> </function> <function name="removeContact" type="unsigned long" throws="OUT_OF_BOUNDS_EXCEPTION"> <param name="index" type="unsigned integer"> </function> <function name="clear" type="void"/> <property name="selectedIndex" type="unsigned integer" get set throws="OUT_OF_BOUNDS_EXCEPTION"/> </idl>
Also, setting up a user-defined event to communicate between a contact and a contact list:
<idl name="ContactSelectedEvent" type="event"> <property name="index" type="unsigned integer"/> </idl>
Using the above types, we declare a component that implements a single contact:
<element name="x-contact" class="ContactElement"> <implements idl="Contact"/> <events> <listen type="click"/> <emit type="ContactSelectedEvent"/> <events> <script type="javascript"> function onclick() { dispatchEvent(new ContactSelectedEvent()); } get name() { var s = ""; if (firstName) s = firstName; if (s && lastName) s += " "; if (lastName) s += lastName; return lastName; } </script> <template> <div> ... nicely formatted contact here ... </div> </template> </element>
... and an element that handles the whole list:
<element name="x-contactlist" class="ContactListElement"> <implements idl="ContactList"/> <events> <listen type="ContactSelectedEvent"/> <events> <script type="javascript"> function onContactSelectedEvent(evt) { } function addContact(contact, index) { appendChild(new ContactElement()); } function removeContact(index) { ... } function clear(evt) { ... } </script> <template> <div> ... nicely formatted contact list here, consisting of <x-contact> elements ... </div> </template> </element>
ISSUE: Avoid the complexity of XBL1's handler
element!
Conversion Example
The above could be converted to the following boilerplate by the browser, in the case of confined components:
Document side
class Contact { firstName: null, lastName: null, phone: null, email: null, }
class ContactElement { constructor() { HTMLElement.call(this); addEventListener('click', onclick); } function addContact(contact, index) { /* native implementation, calling addContact on the shadow tree, return value is deferred */ } function removeContact(index) { /* native implementation, calling removeContact on the shadow tree */ } function clear() { /* native implementation, calling clear on the shadow tree */ } get selectedIndex() { /* native implementation, calling the getter for selectedIndex on the shadow tree, return value is deferred */ } set selectedIndex(value) { /* native implementation, calling the setter for selectedIndex on the shadow tree */ } } Element.register('x-contact', ContactElement);
class ContactListElement { constructor() { HTMLElement.call(this); } function addContact(contact, index) { /* native implementation, calling addContact on the shadow tree, return value is deferred */ } function removeContact(index) { /* native implementation, calling removeContact on the shadow tree */ } function clear() { /* native implementation, calling clear on the shadow tree */ } get selectedIndex() { /* native implementation, calling the getter for selectedIndex on the shadow tree, return value is deferred */ } set selectedIndex(value) { /* native implementation, calling the setter for selectedIndex on the shadow tree */ } } Element.register('x-contact', ContactElement);