Bug 1852754: part 9) Add tests for dynamically loading <link rel="prefetch"> elements...
[gecko.git] / xpcom / docs / writing-xpcom-interface.rst
blob9eeb1c72a2569d06719b455f5b4e3e30e0b6fa55
1 .. _writing_xpcom_interface:
3 Tutorial for Writing a New XPCOM Interface
4 ==========================================
6 High Level Overview
7 -------------------
9 In order to write code that works in native code (C++, Rust), and JavaScript contexts, it's necessary to have a mechanism to do so. For chrome privileged contexts, this is the XPCOM Interface Class.
11 This mechanism starts with an :ref:`XPIDL` file to define the shape of the interface. In the `build system`_, this file is processed, and `Rust`_ and `C++`_ code is automatically generated.
13 .. _build system: https://searchfox.org/mozilla-central/source/xpcom/idl-parser/xpidl
14 .. _Rust: https://searchfox.org/mozilla-central/source/__GENERATED__/dist/xpcrs/rt
15 .. _C++: https://searchfox.org/mozilla-central/source/__GENERATED__/dist/include
17 Next, the interface's methods and attributes must be implemented. This can be done through either a JSM module, or through a C++ interface class. Once these steps are done, the new files must be added to the appropriate :code:`moz.build` files to ensure the build system knows how to find them and process them.
19 Often these XPCOM components are wired into the :code:`Services` JavaScript object to allow for ergonomic access to the interface. For example, open the `Browser Console`_ and type :code:`Services.` to interactively access these components.
21 .. _Browser Console: https://developer.mozilla.org/en-US/docs/Tools/Browser_Console
23 From C++, components can be accessed via :code:`mozilla::components::ComponentName::Create()` using the :code:`name` option in the :code:`components.conf`.
25 While :code:`Services` and :code:`mozilla::components` are the preferred means of accessing components, many are accessed through the historical (and somewhat arcane) :code:`createInstance` mechanism. New usage of these mechanisms should be avoided if possible.
27 .. code:: javascript
29     let component = Cc["@mozilla.org/component-name;1"].createInstance(
30       Ci.nsIComponentName
31     );
33 .. code:: c++
35     nsCOMPtr<nsIComponentName> component = do_CreateInstance(
36       "@mozilla.org/component-name;1");
38 Writing an XPIDL
39 ----------------
41 First decide on a name. Conventionally the interfaces are prefixed with :code:`nsI` (historically Netscape) or :code:`mozI` as they are defined in the global namespace. While the interface is global, the implementation of an interface can be defined in a namespace with no prefix. Historically many component implementations still use the :code:`ns` prefixes (notice that the :code:`I` was dropped), but this convention is no longer needed.
43 This tutorial assumes the component is located at :code:`path/to` with the name :code:`ComponentName`. The interface name will be :code:`nsIComponentName`, while the implementation will be :code:`mozilla::ComponentName`.
45 To start, create an :ref:`XPIDL` file:
47 .. code:: bash
49     touch path/to/nsIComponentName.idl
51 And hook it up to the :code:`path/to/moz.build`
53 .. code:: python
55     XPIDL_SOURCES += [
56         "nsIComponentName.idl",
57     ]
59 Next write the initial :code:`.idl` file: :code:`path/to/nsIComponentName.idl`
61 .. _contract_ids:
62 .. code:: c++
64     /* This Source Code Form is subject to the terms of the Mozilla Public
65      * License, v. 2.0. If a copy of the MPL was not distributed with this
66      * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
68     // This is the base include which defines nsISupports. This class defines
69     // the QueryInterface method.
70     #include "nsISupports.idl"
72     // `scriptable` designates that this object will be used with JavaScript
73     // `uuid`       The example below uses a UUID with all Xs. Replace the Xs with
74     //              your own UUID generated here:
75     //              http://mozilla.pettay.fi/cgi-bin/mozuuid.pl
77     /**
78      * Make sure to document your interface.
79      */
80     [scriptable, uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)]
81     interface nsIComponentName : nsISupports {
83       // Fill out your definition here. This example attribute only returns a bool.
85       /**
86        * Make sure to document your attributes.
87        */
88       readonly attribute bool isAlive;
89     };
91 This definition only includes one attribute, :code:`isAlive`, which will demonstrate that we've done our work correctly at the end. For a more comprehensive guide for this syntax, see the :ref:`XPIDL` docs.
93 Once :code:`./mach build` is run, the XPIDL parser will read this file, and give any warnings if the syntax is wrong. It will then auto-generate the C++ (or Rust) code for us. For this example the generated :code:`nsIComponentName` class will be located in:
95 :code:`{obj-directory}/dist/include/nsIComponentName.h`
97 It might be useful to check out what was automatically generated here, or see the existing `generated C++ header files on SearchFox <https://searchfox.org/mozilla-central/source/__GENERATED__/dist/>`_.
99 Writing the C++ implementation
100 ------------------------------
102 Now we have a definition for an interface, but no implementation. The interface could be backed by a JavaScript implementation using a JSM, but for this example we'll use a C++ implementation.
104 Add the C++ sources to :code:`path/to/moz.build`
106 .. code:: python
108     EXPORTS.mozilla += [
109         "ComponentName.h",
110     ]
112     UNIFIED_SOURCES += [
113         "ComponentName.cpp",
114     ]
116 Now write the header: :code:`path/to/ComponentName.h`
118 .. code:: c++
120     /* This Source Code Form is subject to the terms of the Mozilla Public
121      * License, v. 2.0. If a copy of the MPL was not distributed with this
122      * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
123     #ifndef mozilla_nsComponentName_h__
124     #define mozilla_nsComponentName_h__
126     // This will pull in the header auto-generated by the .idl file:
127     // {obj-directory}/dist/include/nsIComponentName.h
128     #include "nsIComponentName.h"
130     // The implementation can be namespaced, while the XPCOM interface is globally namespaced.
131     namespace mozilla {
133     // Notice how the class name does not need to be prefixed, as it is defined in the
134     // `mozilla` namespace.
135     class ComponentName final : public nsIComponentName {
136       // This first macro includes the necessary information to use the base nsISupports.
137       // This includes the QueryInterface method.
138       NS_DECL_ISUPPORTS
140       // This second macro includes the declarations for the attributes. There is
141       // no need to duplicate these declarations.
142       //
143       // In our case it includes a declaration for the isAlive attribute:
144       //   GetIsAlive(bool *aIsAlive)
145       NS_DECL_NSICOMPONENTNAME
147      public:
148       ComponentName() = default;
150      private:
151       // A private destructor must be declared.
152       ~ComponentName() = default;
153     };
155     }  // namespace mozilla
157     #endif
159 Now write the definitions: :code:`path/to/ComponentName.cpp`
161 .. code:: c++
163     /* This Source Code Form is subject to the terms of the Mozilla Public
164      * License, v. 2.0. If a copy of the MPL was not distributed with this
165      * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
167     #include "ComponentName.h"
169     namespace mozilla {
171     // Use the macro to inject all of the definitions for nsISupports.
172     NS_IMPL_ISUPPORTS(ComponentName, nsIComponentName)
174     // This is the actual implementation of the `isAlive` attribute. Note that the
175     // method name is somewhat different than the attribute. We specified "read-only"
176     // in the attribute, so only a getter, not a setter was defined for us. Here
177     // the name was adjusted to be `GetIsAlive`.
178     //
179     // Another common detail of implementing an XPIDL interface is that the return values
180     // are passed as out parameters. The methods are treated as fallible, and the return
181     // value is an `nsresult`. See the XPIDL documentation for the full nitty gritty
182     // details.
183     //
184     // A common way to know the exact function signature for a method implementation is
185     // to copy and paste from existing examples, or inspecting the generated file
186     // directly: {obj-directory}/dist/include/nsIComponentName.h
187     NS_IMETHODIMP
188     ComponentName::GetIsAlive(bool* aIsAlive) {
189       *aIsAlive = true;
190       return NS_OK;
191     }
193     } // namespace: mozilla
195 Registering the component
196 -------------------------
198 At this point, the component should be correctly written, but it's not registered with the component system. In order to this, we'll need to create or modify the :code:`components.conf`.
200 .. code:: bash
202     touch path/to/components.conf
205 Now update the :code:`moz.build` to point to it.
207 .. code:: python
209     XPCOM_MANIFESTS += [
210         "components.conf",
211     ]
213 It is probably worth reading over :ref:`defining_xpcom_components`, but the following config will be sufficient to hook up our component to the :code:`Services` object.
214 Services should also be added to ``tools/lint/eslint/eslint-plugin-mozilla/lib/services.json``.
215 The easiest way to do that is to copy from ``<objdir>/xpcom/components/services.json``.
217 .. code:: python
219     Classes = [
220         {
221             # This CID is the ID for component entries, and needs a separate UUID from
222             # the .idl file. Replace the Xs with a uuid from:
223             # http://mozilla.pettay.fi/cgi-bin/mozuuid.pl
224             'cid': '{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}',
225             'interfaces': ['nsIComponentName'],
227             # A contract ID is a human-readable identifier for an _implementation_ of
228             # an XPCOM interface.
229             #
230             # "@mozilla.org/process/environment;1"
231             #  ^^^^^^^^^^^^ ^^^^^^^ ^^^^^^^^^^^ ^
232             #  |            |       |           |
233             #  |            |       |           The version number, usually just 1.
234             #  |            |       Component name
235             #  |            Module
236             #  Domain
237             #
238             # This design goes back to a time when XPCOM was intended to be a generalized
239             # solution for the Gecko Runtime Environment (GRE). At this point most (if
240             # not all) of mozilla-central has an @mozilla domain.
241             'contract_ids': ['@mozilla.org/component-name;1'],
243             # This is the name of the C++ type that implements the interface.
244             'type': 'mozilla::ComponentName',
246             # The header file to pull in for the implementation of the interface.
247             'headers': ['path/to/ComponentName.h'],
249             # In order to hook up this interface to the `Services` object, we can
250             # provide the "js_name" parameter. This is an ergonomic way to access
251             # the component.
252             'js_name': 'componentName',
253         },
254     ]
256 At this point the full :code:`moz.build` file should look like:
258 .. code:: python
260     # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
261     # vim: set filetype=python:
262     # This Source Code Form is subject to the terms of the Mozilla Public
263     # License, v. 2.0. If a copy of the MPL was not distributed with this
264     # file, You can obtain one at http://mozilla.org/MPL/2.0/.
266     XPIDL_SOURCES += [
267         "nsIComponentName.idl",
268     ]
270     XPCOM_MANIFESTS += [
271         "components.conf",
272     ]
274     EXPORTS.mozilla += [
275         "ComponentName.h",
276     ]
278     UNIFIED_SOURCES += [
279         "ComponentName.cpp",
280     ]
282 This completes the implementation of a basic XPCOM Interface using C++. The component should be available via the `Browser Console`_ or other chrome contexts.
284 .. code:: javascript
286     console.log(Services.componentName.isAlive);
287     > true