Wiring up new WebExtensions WebIDL files into mozilla-central
=============================================================

Add a new entry in ``dom/bindings/Bindings.conf``
-------------------------------------------------

New WebIDL bindings should be added as new entries in ``dom/bindings/Bindings.conf``. The new entry should be
added in alphabetic order and nearby the other WebExtensions API bindings already listed in this config file
(look for the ``ExtensionBrowser`` webidl definition and the other existing WebIDL bindings related to the
WebExtensions APIs):

.. code-block::

    # WebExtension API
    ...
    'ExtensionRuntime': {
      'headerFile': 'mozilla/extensions/ExtensionRuntime.h',
      'nativeType': 'mozilla::extensions::ExtensionRuntime',
    },

.. warning::

    `mach build` will fail if the entries in `dom/bindings/Bindings.conf` are not in alphabetic order,
    or if the `headerFile` referenced does not exist yet.

Add a new entry in ``dom/webidl/moz.build``
-------------------------------------------

The new ``.webidl`` file has to be also listed in "dom/webidl/moz.build", it should be added in

- the existing group of ``WEBIDL_FILES`` entries meant specifically for the WebExtensions API bindings
- or in the group of ``PREPROCESSED_WEBIDL_FILES`` entries meant specifically for the WebExtensions
  API bindings, **if the generated `.webidl` includes preprocessing macros** (e.g. when part of an API
  is not available in all builds, e.g. subset of APIs that are only available in Desktop builds).

.. code-block::

    # WebExtensions API.
    WEBIDL_FILES += [
      ...
      "ExtensionRuntime.webidl"
      ...
    ]

    PREPROCESSED_WEBIDL_FILES += [
      ...
    ]

.. warning::

   The group of PREPROCESSED_WERBIDL_FILES meant to list WebExtensions APIs ``.webidl`` files
   may not exist yet (one will be added right after the existing `WEBIDL_FILES` when the first
   preprocessed `.webidl` will be added).


Add new entries in ``toolkit/components/extensions/webidl-api/moz.build``
-------------------------------------------------------------------------

The new C++ files for the WebExtensions API binding needs to be added to ``toolkit/components/extensions/webidl-api/moz.build``
to make them part of the build, The new ``.cpp`` file has to be added into the ``UNIFIED_SOURCES`` group
where the other WebIDL bindings are being listed. Similarly, the new ``.h`` counterpart has to be added to
``EXPORTS.mozilla.extensions`` (which ensures that the header file will be placed into the path set earlier
in ``dom/bindings/Bindings.conf``):

.. code-block::

    # WebExtensions API namespaces.
    UNIFIED_SOURCES += [
      ...
      "ExtensionRuntime.cpp",
      ...
    ]

    EXPORTS.mozilla.extensions += [
      ...
      "ExtensionRuntime.h",
      ...
    ]

Wiring up the new API into ``dom/webidl/ExtensionBrowser.webidl``
-----------------------------------------------------------------

To make the new WebIDL bindings part of the ``browser`` global, a new attribute has to be added to
``dom/webidl/ExtensionBrowser.webidl``:

.. code-block::

    // `browser.runtime` API namespace.
    [Replaceable, SameObject, BinaryName="GetExtensionRuntime",
     Func="mozilla::extensions::ExtensionRuntime::IsAllowed"]
    readonly attribute ExtensionRuntime runtime;

.. note::
    ``chrome`` is defined as an alias of the ``browser`` global, and so by adding the new attribute
    into ``ExtensionBrowser` the same attribute will also be available in the ``chrome`` global.
    Unlike the "Privileged JS"-based WebExtensions API, the ``chrome`` and ``browser`` APIs are
    exactly the same and a the async methods return a Promise if no callback has been passed
    (similarly to Safari versions where the WebExtensions APIs are supported).

The additional attribute added into ``ExtensionBrowser.webidl`` will require some addition to the ``ExtensionBrowser``
C++ class as defined in ``toolkit/components/extensions/webidl-api/ExtensionBrowser.h``:

- the definition of a new corresponding **public method** (by convention named ``GetExtensionMyNamespace``)
- a ``RefPtr`` as a new **private data member named** (by convention named ``mExtensionMyNamespace``)

.. code-block::

    ...
    namespace extensions {

    ...
    class ExtensionRuntime;
    ...

    class ExtensionBrowser final : ... {
      ...
      RefPtr<ExtensionRuntime> mExtensionRuntime;
      ...

      public:
        ...
        ExtensionRuntime* GetExtensionRuntime();
    }
    ...


And then in its ``toolkit/components/extensions/webidl-api/ExtensionBrowser.cpp`` counterpart:

- the implementation of the new public method
- the addition of the new private member data ``RefPtr`` in the ``NS_IMPL_CYCLE_COLLECTION_UNLINK``
  and ``NS_IMPL_CYCLE_COLLECTION_TRAVERSE`` macros

.. code-block::

    ...
    #include "mozilla/extensions/ExtensionRuntime.h"
    ...
    NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ExtensionBrowser)
      ...
      NS_IMPL_CYCLE_COLLECTION_UNLINK(mExtensionRuntime)
      ...
    NS_IMPL_CYCLE_COLLECTION_UNLINK_END

    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ExtensionBrowser)
      ...
      NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExtensionRuntime)
      ...
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    ...

    ExtensionRuntime* ExtensionBrowser::GetExtensionRuntime() {
      if (!mExtensionRuntime) {
        mExtensionRuntime = new ExtensionRuntime(mGlobal, this);
      }

      return mExtensionRuntime
    }

.. warning::

   Forgetting to add the new ``RefPtr`` into the cycle collection traverse and unlink macros
   will not result in a build error, but it will result into a leak.

   Make sure to don't forget to double-check these macros, especially if some tests are failing
   because of detected shutdown leaks.
