-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Adding a New WebIDL Interface
Note: For a real-world example, see the changeset which added the FragmentDirective interface.
To add a new WebIDL interface and its implementation to WebKit, follow these steps:
- Create all necessary files — e.g,
Foo.idl,Foo.h, andFoo.cpp— in the appropriate directories. - Find an existing
.idlfile that’s for a WebIDL interface similar to the one you’re adding.
For the purposes of these steps, let’s call that fileBar.idlhere. - In your
Source/WebCoredirectory, run a (rip)grep (or similar) search for\W(JS)?Bar\.(idl|cpp|h)|macro\(Bar\), to find config files with lines containing any ofBar.idl,Bar.h,Bar.cpp,JSBar.h,JSBar.cpp, ormacro(Bar). - For each matching
Bar.idl,Bar.h, orBar.cppline you find, add a correspondingFoo.idl,Foo.h, orFoo.cppline (for the files you created in step #1) - For each matching
JSBar.horJSBar.cppline you find, add a correspondingJSFoo.horJSFoo.cppline (for JS “derived sources” bindings files generated by the build). - For any
macro(Bar)line you find, add a correspondingmacro(Foo)line. - Run the build.
- If the build fails, repeat the above steps as needed.
For step #4, the files in the Source/WebCore directory that you’ll likely need to add lines to are the following:
-
CMakeLists.txtneeds a line forFoo.idl -
DerivedSources-input.xcfilelistneeds a line forFoo.idl -
DerivedSources-output.xcfilelistneeds lines forJSFoo.handJSFoo.cpp -
DerivedSources.makeneeds a line forFoo.idl -
Headers.cmakeneeds a line forFoo.h -
Sources.txtneeds lines forFoo.cppandJSFoo.cpp -
WebCore.xcodeproj/project.pbxprojneeds lines forFoo.idl,Foo.h, andFoo.cpp
You may also need to make the following changes:
-
bindings/js/WebCoreBuiltinNames.hmay need amacro(Foo) \line
(This is necessary when you’re using theEnabledBySettingannotation in any WebIDL files you’re adding) -
WebCore.xcodeproj/project.pbxprojmay need lines for theJSFoo.handJSFoo.cppderived sources
[TODO: Figure when it’s actually necessary to add anyJS*lines to this file]
Also for step #4: See https://docs.webkit.org/Deep%20Dive/Build/AddingNewFile.html for details on using Xcode to add new files to the project — which will modify the WebCore.xcodeproj/project.pbxproj file. But, for any JSFoo.h and JSFoo.cpp files, you might also need to make manual modifications to the WebCore.xcodeproj/project.pbxproj file — to assign each file a unique hash (fileref/UUID), and to adjust the paths for those files.
See also: https://docs.webkit.org/Deep%20Dive/Architecture/AddingNewJSApi.html
Note: The examples in this section are based on the actual WebIDL for the document.fragmentDirective property and the FragmentDirective interface and its corresponding implementation in WebKit.
A minimal C++ implementation in WebKit for a WebIDL interface will look something like this:
class FragmentDirective : public RefCounted<FragmentDirective> {
public:
static Ref<FragmentDirective> create() { return adoptRef(*new FragmentDirective()); }
virtual ~FragmentDirective() = default;
private:
FragmentDirective() = default;
};Notes:
- The corresponding C++ class for the interface needs to inherit from
public RefCounted<FragmentDirective>. - The class must explicitly declare a
virtualpublic destructor:virtual ~FragmentDirective()— unless the class is markedfinal, per Safer CPP Guidelines. - The class must explicitly declare a
privateconstructor, per Safer CPP Guidelines — and note: in C++, if you didn’t explicitly declare it private, the compiler will generate an implicit constructor, and it will be public.
A minimal C++ implementation for a WebIDL readonly attribute added to an existing interface will look something like this:
Ref<FragmentDirective> fragmentDirective() { return m_fragmentDirective; }…which corresponds to the following WebIDL:
partial interface Document {
[SameObject] readonly attribute FragmentDirective fragmentDirective;
};Note: While the WebIDL defines a (readonly) fragmentDirective attribute, the corresponding C++ code must define a fragmentDirective() function that’s a getter for a m_fragmentDirective data member; unless such a corresponding function is declared, a build with the derived sources produced by the bindings generator would fail during linking.
Sometimes you may find there’s a name conflict between the name of a WebIDL construct you’re adding, and the name of an existing C++ symbol in the WebKit code. In such cases, you’ll need to use the ImplementedAs WebIDL annotation, and specify an (arbitrary) alternate name for the corresponding C++ symbol in your implementation — like this:
WebIDL:
[SameObject, ImplementedAs=fragmentDirectiveForBindings]
readonly attribute FragmentDirective fragmentDirective;C++
Ref<FragmentDirective> fragmentDirectiveForBindings() { return m_fragmentDirectiveForBindings; }That is, for the WebIDL fragmentDirective attribute, we’ve given the corresponding C++ function the name fragmentDirectiveForBindings() — and added the ImplementedAs annotation to the WebIDL — so the bindings generator know where to find our function. (It would otherwise look for a function named fragmentDirective()).
Using the suffix forBindings is a common naming convention in the existing code — but using that exact suffix isn’t a requirement; instead the only requirement is that the C++ symbol name matches the value you specify for the ImplementedAs annotation in the WebIDL source.
If you’ve made a mistake during any of the steps outlined above, you might encounter some of these error messages:
| Build error | Cause |
|---|---|
| no member named 'deref' | Your C++ class should inherit from public RefCounted<FragmentDirective>. |
| type 'Foo' does not provide a call operator | A part of your C++ implementation should be implemented as a function (that is, callable). |
| Foo.h: No such file or directory | The Foo.h header file should have its target membership set to Private in Xcode (that is, in the WebCore.xcodeproj/project.pbxproj config file). |
| Undefined symbols for architecture | A config file is missing a necessary Foo.h or JSFoo.h line. |