WebIDL bindings

  • Revision slug: Mozilla/WebIDL_bindings
  • Revision title: WebIDL bindings
  • Revision id: 293936
  • Created:
  • Creator: Bzbarsky
  • Is current revision? No
  • Comment 178 words added

Revision Content

The WebIDL bindings are generated at build time based on two things: the actual WebIDL file and a configuration file that lists some metadata about how the WebIDL should be reflected into Gecko-internal code.

All WebIDL files should be placed in dom/webidl and added to the list in the WebIDL.mk file in that directory.

The configuration file, dom/bindings/Bindings.conf, is basically a Python dict that maps interface names to information about the interface, called a descriptor.  There are all sorts of possible options here that handle various edge cases, but most descriptors can be very simple.

All the generated code is placed in the mozilla::dom namespace.  For each interface, a namespace whose name is the name of the interface with Binding appended is created, and all the things pertaining to that interface's binding go in that namespace.

There are various helper objects and utility methods in dom/bindings that are also all in the mozilla::dom namespace and whose headers are all exported into mozilla/dom.

Adding WebIDL bindings to a class


To add a WebIDL binding for interface MyInterface to a class MyClass that's supposed to implement that interface, you need to do the following:

  1. Inherit from nsWrapperCache and hook up the class to the cycle collector so it will trace the wrapper cache properly.
  2. Implement a GetParentObject override that, for any given instance of your class, returns the same object every time.  The idea is that walking the GetParentObject chain will eventually get you to a Window, so that every WebIDL object is associated with a particular Window.  For example, nsINode::GetParentObject returns the node's owner document.  The return value of GetParentObject must either singly-inherit from nsISupports or have a corresponding ToSupports() method that can produce an nsISupports from it.  If many instances of MyClass are expected to be created quicky, the return value of GetParentObject should itself inherit from nsWrapperCache for optimal performance.
  3. Add the WebIDL for MyInterface in dom/webidl and to the list in dom/webidl/WebIDL.mk.
  4. Add an entry to dom/bindings/Bindings.conf that sets the 'nativeType' for MyInterface to MyClass.  If MyClass is not in the header file one gets by replacing '::' with '/' and appending '.h', the add a corresponding 'headerFile' annotation.
  5. Implement a WrapObject override on MyClass that just calls through to mozilla::dom::MyInterfaceBinding::Wrap.
  6. Expose whatever methods the interface needs on MyClass.  These can be inline, virtual, have any calling convention, and so forth, as long as they have the right argument types and return types.
Note: If your object can only be reflected into JS by creating it, not by retrieving it from somewhere, you can skip steps 1 and 2 above and instead add 'wrapperCache': False to your descriptor.  If your object already has classinfo, it should be using the nsNewDOMBindingNoWrapperCacheSH scriptable helper in this case.  You will need to flag the functions that return your object as [Creator] in the WebIDL.
Note: If your object needs to be reflected in Workers, you will need to do more work here.  XXXbz need to document.

C++ reflections of WebIDL constructs

 

C++ reflections of WebIDL operations (methods)


A WebIDL operation is turned into a method call on the underlying C++ object.  The return type and argument types are determined as described below.  In addition to those, all fallible (allowed to throw) methods will get an ErrorResult& argument appended to their argument list.  Methods that use certain WebIDL types like any or object will get a JSContext* argument prepended to the argument list.

The name of the C++ method is simply the name of the WebIDL operation with the first letter converted to uppercase.

WebIDL overloads are turned into C++ overloads: they simply call C++ methods with the same name and different signatures.

For example, this webidl:

interface MyInterface 
{
  void doSomething(long number);
  double doSomething(MyInterface? otherInstance);

  MyInterface doSomethingElse(optional long maybeNumber);
  void doSomethingElse(MyInterface otherInstance);

  void doTheOther(any something);
};

will require these method declarations:

class MyClass
{
  void DoSomething(int32_t aNumber, ErrorResult& rv);
  double DoSomething(MyClass* aOtherInstance, ErrorResult& rv);

  already_AddRefed<MyInterface> DoSomethingElse(Optional<int32_t> aMaybeNumber,
                                                ErrorResult& rv);
  void DoSomethingElse(MyClass& aOtherInstance, ErrorResult& rv);
  
  void DoTheOther(JSContext* cx, JS::Value aSomething, ErrorResult& rv);
}

C++ reflections of WebIDL attributes

A WebIDL attribute is turned into a pair of method calls for the getter and setter on the underlying C++ object.  A readonly attribute only has a getter and no setter.

The getter's name is Get followed by the name of the attribute with the first letter converted to uppercase.  The method signature looks just like an operation with no arguments and the attribute's type as the return type.

The setter's name is Set followed by the name of the attribute with the first letter converted to uppercase.  The method signature looks just like an operation with a void return value and a single argument whose type as the attribute's type.

C++ reflections of WebIDL constructors

A WebIDL constructor is turned into a static class method named Constructor.  The arguments of this method will be the arguments of the WebIDL constructor, with an nsISupports* for the global (typically the Window) prepended.  If a JSContext* is also needed due to some of the argument types, it will come before the global.  The return value of the constructor for MyInterface is exactly the same as that of a method returning an instance of MyInterface.

For example, this IDL:

[Constructor,
 Constructor(unsigned long someNumber)]
interface MyInterface 
{
};

will require the following declarations in MyClass:

class MyClass {
  // Various nsISupports stuff or whatnot
  static
  already_AddRefed<MyClass> Constructor(nsISupports* aGlobal,
                                        ErrorResult& rv);
  static
  already_AddRefed<MyClass> Constructor(nsISupports* aGlobal,
                                        uint32_t aSomeNumber,
                                        ErrorResult& rv);  
};

C++ reflections of WebIDL types

Any

The any WebIDL type is represented as a JS::Value.  Methods that return any just have a JS::Value return value.

For example, this WebIDL:

interface Test {
  attribute any myAttr;
  any myMethod(any arg);
};

will correspond to these C++ function declarations:

JS::Value GetMyAttr(ErrorResult& rv);
void SetMyAttr(JS::Value value, ErrorResult& rv);
JS::Value MyMethod(JS::Value arg, ErrorResult& rv);

Integer types

Integer WebIDL types are mapped to the corresponding C99 stdint types.  For example, webIDL long becomes C++ int32_t.  Integer return values are just the return value of the C++ method.

For example, this WebIDL:

interface Test {
  attribute short myAttr;
  long long myMethod(unsigned long arg);
};

will correspond to these C++ function declarations:

int16_t GetMyAttr(ErrorResult& rv);
void SetMyAttr(int16_t value, ErrorResult& rv);
int64_t MyMethod(uint32_t arg, ErrorResult& rv);

Floating point types

Floating point WebIDL types are mapped to the C++ type of the same name.  So float and unrestricted float become a C++ float, while double and unrestricted double become a C++ double.  Floating point return values are just the return value of the C++ method.

For example, this WebIDL:

interface Test {
  float myAttr;
  double myMethod(unrestricted double arg);
};

will correspond to these C++ function declarations:

float GetMyAttr(ErrorResult& rv);
void SetMyAttr(float value, ErrorResult& rv);
double MyMethod(double arg, ErrorResult& rv);

DOMString

Strings are reflected in three different ways:

  • String arguments become const nsAString&.
  • String return values become a nsString& out param appended to the argument list.  This comes after all IDL arguments, but before the ErrorResult&, if any, for the method.
  • Strings in sequences and dictionaries become nsString.

 

WebIDL Type C++ Type
WebIDL to C++ type mapping for arguments
any JS::Value
boolean bool
octet uint8_t
byte int8_t
short int16_t
unsigned short uint16_t
long int32_t
unsigned long uint32_t
long long int64_t
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   

Throwing exceptions from WebIDL methods, getters, and setters

All WebIDL methods, getters, and setters that are not explicitly marked as infallible have an ErrorResult& argument as their last argument.  To throw an exception, simply call Throw() on the ErrorResult& and return from your C++ back into the binding code.

Note: at the moment Throw() can only be called with an error nsresult, but in the future we expect to add ways to communicate more informative exception strings and other metainformation about the exception.

Revision Source

<p>The <a class="external" href="http://www.w3.org/TR/WebIDL/" title="http://www.w3.org/TR/WebIDL/">WebIDL</a> bindings are generated at build time based on two things: the actual WebIDL file and a configuration file that lists some metadata about how the WebIDL should be reflected into Gecko-internal code.</p>
<p>All WebIDL files should be placed in <a class="external" href="http://mxr.mozilla.org/mozilla-central/source/dom/webidl/" title="http://mxr.mozilla.org/mozilla-central/source/dom/webidl/"><code>dom/webidl</code></a> and added to the list in the <a class="external" href="http://mxr.mozilla.org/mozilla-central/source/dom/webidl/WebIDL.mk" title="http://mxr.mozilla.org/mozilla-central/source/dom/webidl/WebIDL.mk"><code>WebIDL.mk</code></a> file in that directory.</p>
<p>The configuration file, <code><a class="external" href="http://mxr.mozilla.org/mozilla-central/source/dom/bindings/Bindings.conf" title="http://mxr.mozilla.org/mozilla-central/source/dom/bindings/Bindings.conf">dom/bindings/Bindings.conf</a>,</code> is basically a Python dict that maps interface names to information about the interface, called a <em>descriptor</em>.  There are all sorts of possible options here that handle various edge cases, but most descriptors can be very simple.</p>
<p>All the generated code is placed in the <code>mozilla::dom</code> namespace.  For each interface, a namespace whose name is the name of the interface with <code>Binding</code> appended is created, and all the things pertaining to that interface's binding go in that namespace.</p>
<p>There are various helper objects and utility methods in <code>dom/bindings</code> that are also all in the <code>mozilla::dom</code> namespace and whose headers are all exported into <code>mozilla/dom</code>.</p>
<h2>Adding WebIDL bindings to a class</h2>
<p><br> To add a WebIDL binding for interface <code>MyInterface</code> to a class <code>MyClass</code> that's supposed to implement that interface, you need to do the following:</p>
<ol> <li>Inherit from <code>nsWrapperCache</code> and hook up the class to the cycle collector so it will trace the wrapper cache properly.</li> <li>Implement a <code>GetParentObject</code> override that, for any given instance of your class, returns the same object every time.  The idea is that walking the <code>GetParentObject</code> chain will eventually get you to a Window, so that every WebIDL object is associated with a particular Window.  For example, <code>nsINode::GetParentObject</code> returns the node's owner document.  The return value of <code>GetParentObject</code> must either singly-inherit from <code>nsISupports</code> or have a corresponding <code>ToSupports()</code> method that can produce an <code>nsISupports</code> from it.  If many instances of <code>MyClass</code> are expected to be created quicky, the return value of <code>GetParentObject</code> should itself inherit from <code>nsWrapperCache</code> for optimal performance.</li> <li>Add the WebIDL for <code>MyInterface</code> in <code>dom/webidl</code> and to the list in <code>dom/webidl/WebIDL.mk</code>.</li> <li>Add an entry to <code>dom/bindings/Bindings.conf</code> that sets the <code>'nativeType'</code> for <code>MyInterface</code> to <code>MyClass</code>.  If <code>MyClass</code> is not in the header file one gets by replacing '::' with '/' and appending '<code>.h</code>', the add a corresponding <code>'headerFile'</code> annotation.</li> <li>Implement a <code>WrapObject</code> override on <code>MyClass</code> that just calls through to <code>mozilla::dom::MyInterfaceBinding::Wrap</code>.</li> <li>Expose whatever methods the interface needs on <code>MyClass</code>.  These can be inline, virtual, have any calling convention, and so forth, as long as they have the right argument types and return types.</li>
</ol>
<div class="note"><strong>Note:</strong> If your object can only be reflected into JS by creating it, not by retrieving it from somewhere, you can skip steps 1 and 2 above and instead add <code>'wrapperCache': False</code> to your descriptor.  If your object already has classinfo, it should be using the <code>nsNewDOMBindingNoWrapperCacheSH</code> scriptable helper in this case.  You will need to flag the functions that return your object as <code>[Creator]</code> in the WebIDL.</div>
<div class="warning"><strong>Note:</strong> If your object needs to be reflected in Workers, you will need to do more work here.  XXXbz need to document.</div>
<h2>C++ reflections of WebIDL constructs</h2>
<p> </p>
<h3>C++ reflections of WebIDL operations (methods)</h3>
<p><br> A WebIDL operation is turned into a method call on the underlying C++ object.  The return type and argument types are determined <a href="/#typemapping" title="#typemapping">as described below</a>.  In addition to those, all fallible (allowed to throw) methods will get an <code>ErrorResult&amp;</code> argument appended to their argument list.  Methods that use certain WebIDL types like <code>any</code> or <code>object</code> will get a <code>JSContext*</code> argument prepended to the argument list.</p>
<p>The name of the C++ method is simply the name of the WebIDL operation with the first letter converted to uppercase.</p>
<p>WebIDL overloads are turned into C++ overloads: they simply call C++ methods with the same name and different signatures.</p>
<p>For example, this webidl:</p>
<pre>interface MyInterface 
{
  void doSomething(long number);
  double doSomething(MyInterface? otherInstance);

  MyInterface doSomethingElse(optional long maybeNumber);
  void doSomethingElse(MyInterface otherInstance);

  void doTheOther(any something);
};
</pre>
<p>will require these method declarations:</p>
<pre>class MyClass
{
  void DoSomething(int32_t aNumber, ErrorResult&amp; rv);
  double DoSomething(MyClass* aOtherInstance, ErrorResult&amp; rv);

  already_AddRefed&lt;MyInterface&gt; DoSomethingElse(Optional&lt;int32_t&gt; aMaybeNumber,
                                                ErrorResult&amp; rv);
  void DoSomethingElse(MyClass&amp; aOtherInstance, ErrorResult&amp; rv);
  
  void DoTheOther(JSContext* cx, JS::Value aSomething, ErrorResult&amp; rv);
}
</pre>
<h3>C++ reflections of WebIDL attributes</h3>
<p>A WebIDL attribute is turned into a pair of method calls for the getter and setter on the underlying C++ object.  A readonly attribute only has a getter and no setter.</p>
<p>The getter's name is <code>Get</code> followed by the name of the attribute with the first letter converted to uppercase.  The method signature looks just like an operation with no arguments and the attribute's type as the return type.</p>
<p>The setter's name is <code>Set</code> followed by the name of the attribute with the first letter converted to uppercase.  The method signature looks just like an operation with a void return value and a single argument whose type as the attribute's type.</p>
<h3>C++ reflections of WebIDL constructors</h3>
<p>A WebIDL constructor is turned into a static class method named <code>Constructor</code>.  The arguments of this method will be the arguments of the WebIDL constructor, with an <code>nsISupports*</code> for the global (typically the Window) prepended.  If a <code>JSContext*</code> is also needed due to some of the argument types, it will come before the global.  The return value of the constructor for <code>MyInterface</code> is exactly the same as that of a method returning an instance of <code>MyInterface</code>.</p>
<p>For example, this IDL:</p>
<pre>[Constructor,
 Constructor(unsigned long someNumber)]
interface MyInterface 
{
};
</pre>
<p>will require the following declarations in <code>MyClass</code>:</p>
<pre>class MyClass {
  // Various nsISupports stuff or whatnot
  static
  already_AddRefed&lt;MyClass&gt; Constructor(nsISupports* aGlobal,
                                        ErrorResult&amp; rv);
  static
  already_AddRefed&lt;MyClass&gt; Constructor(nsISupports* aGlobal,
                                        uint32_t aSomeNumber,
                                        ErrorResult&amp; rv);  
};
</pre>
<h3><a name="typemapping">C++ reflections of WebIDL types</a></h3><a name="typemapping">
<h4>Any</h4>
<p>The <code>any</code> WebIDL type is represented as a <code>JS::Value</code>.  Methods that return <code>any</code> just have a <code>JS::Value</code> return value.</p>
<p>For example, this WebIDL:</p>
<pre>interface Test {
  attribute any myAttr;
  any myMethod(any arg);
};
</pre>
<p>will correspond to these C++ function declarations:</p>
<pre>JS::Value GetMyAttr(ErrorResult&amp; rv);
void SetMyAttr(JS::Value value, ErrorResult&amp; rv);
JS::Value MyMethod(JS::Value arg, ErrorResult&amp; rv);
</pre>
<h4>Integer types</h4>
<p>Integer WebIDL types are mapped to the corresponding C99 stdint types.  For example, webIDL <code>long</code> becomes C++ <code>int32_t</code>.  Integer return values are just the return value of the C++ method.</p>
<p>For example, this WebIDL:</p>
<pre>interface Test {
  attribute short myAttr;
  long long myMethod(unsigned long arg);
};
</pre>
<p>will correspond to these C++ function declarations:</p>
<pre>int16_t GetMyAttr(ErrorResult&amp; rv);
void SetMyAttr(int16_t value, ErrorResult&amp; rv);
int64_t MyMethod(uint32_t arg, ErrorResult&amp; rv);
</pre>
<h4>Floating point types</h4>
<p>Floating point WebIDL types are mapped to the C++ type of the same name.  So <code>float</code> and <code>unrestricted float</code> become a C++ <code>float</code>, while <code>double</code> and <code>unrestricted double</code> become a C++ <code>double</code>.  Floating point return values are just the return value of the C++ method.</p>
<p>For example, this WebIDL:</p>
<pre>interface Test {
  float myAttr;
  double myMethod(unrestricted double arg);
};
</pre>
<p>will correspond to these C++ function declarations:</p>
<pre>float GetMyAttr(ErrorResult&amp; rv);
void SetMyAttr(float value, ErrorResult&amp; rv);
double MyMethod(double arg, ErrorResult&amp; rv);
</pre>

<h4>DOMString</h4>
<p>Strings are reflected in three different ways:</p>
<ul> <li>String arguments become <code>const nsAString&amp;</code>.</li> <li>String return values become a <code>nsString&amp;</code> out param appended to the argument list.  This comes after all IDL arguments, but before the <code>ErrorResult&amp;</code>, if any, for the method.</li> <li>Strings in sequences and dictionaries become <code>nsString</code>.</li>
</ul>
<p> </p>
<table align="center" border="1" cellpadding="1" cellspacing="1" style="width: 100%;"> <thead> <tr> <th scope="col">WebIDL Type</th> <th scope="col">C++ Type</th> </tr> </thead> <caption>WebIDL to C++ type mapping for arguments</caption> <tbody> <tr> <td>any</td> <td>JS::Value</td> </tr> <tr> <td>boolean</td> <td>bool</td> </tr> <tr> <td>octet</td> <td>uint8_t</td> </tr> <tr> <td>byte</td> <td>int8_t</td> </tr> <tr> <td>short</td> <td>int16_t</td> </tr> <tr> <td>unsigned short</td> <td>uint16_t</td> </tr> <tr> <td>long</td> <td>int32_t</td> </tr> <tr> <td>unsigned long</td> <td>uint32_t</td> </tr> <tr> <td>long long</td> <td>int64_t</td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> </tbody>
</table>
<h2>Throwing exceptions from WebIDL methods, getters, and setters</h2>
<p>All WebIDL methods, getters, and setters that are not explicitly marked as infallible have an <code>ErrorResult&amp;</code> argument as their last argument.  To throw an exception, simply call <code>Throw()</code> on the <code>ErrorResult&amp;</code> and return from your C++ back into the binding code.</p>
<div class="note"><strong>Note:</strong> at the moment <code>Throw()</code> can only be called with an error <code>nsresult</code>, but in the future we expect to add ways to communicate more informative exception strings and other metainformation about the exception.</div></a>
Revert to this revision