Blink developers (non-bindings development): for general IDL use, see Web IDL interfaces; for configuring bindings, see Blink IDL Extended Attributes; for IDL dictionaries use, see IDL dictionaries in Blink.
OverviewWeb IDL is a language that defines how Blink interfaces are bound to V8. You need to write IDL files (e.g. xml_http_request.idl, element.idl, etc) to expose Blink interfaces to those external languages. When Blink is built, the IDL files are parsed, and the code to bind Blink implementations to V8 interfaces automatically generated. This document describes practical information about how the IDL bindings work and how you can write IDL files in Blink. The syntax of IDL files is fairly well documented in the Web IDL spec, but it is too formal to read :-) and there are several differences between the Web IDL spec and the Blink IDL due to implementation issues. For design docs on bindings generation, see IDL build and IDL compiler. For Blink developers, the main details of IDLs are the extended attributes, which control implementation-specific behavior: see Blink IDL Extended Attributes for extensive details. Our goal is to converge Blink's IDL and Web IDL. The grammar is almost identical; see below. Basics of IDLHere is an example of IDL files: [CustomToV8] interface Node { const unsigned short ELEMENT_NODE = 1; attribute Node parentNode; [TreatReturnedNullStringAs=Null] attribute DOMString nodeName; [Custom] Node appendChild(Node newChild); void addEventListener(DOMString type, EventListener listener, optional boolean useCapture); }; Let us introduce some terms:
The key points are as follows:
The valid extended attributes depend on where they attach: interfaces and methods have different extended attributes. A simple IDL file template looks like: interface INTERFACE_NAME { const unsigned long value = 12345; attribute Node node; void func(long argument, ...); }; With extended attributes, this looks like: [ EXTATTR, EXTATTR, ..., ] interface INTERFACE_NAME { const unsigned long value = 12345; [EXTATTR, EXTATTR, ...] attribute Node node; [EXTATTR, EXTATTR, ...] void func([EXTATTR, EXTATTR, ...] optional [EXTATTR] long argument, ...); }; SyntaxBlink IDL is a dialect of Web IDL. The lexical syntax is identical, but the phrase syntax is slightly different. Implementation-wise, the lexer and parser are written in PLY (Python lex-yacc), an implementation of lex and yacc for Python. A standard-compliant lexer is used (Chromium tools/idl_parser/idl_lexer.py). The parser (Blink bindings/scripts/blink_idl_parser.py) derives from a standard-compliant parser (Chromium tools/idl_parser/idl_parser.py). Blink deviations from the Web IDL standard can be seen as the BNF production rules in the derived parser. Style Style guidelines are to generally follow Blink style for C++, with a few points highlighted, addenda, and exceptions. These are not enforced by a pre-submit test, but do assist legibility:
[ A, B /* No trailing commas on the last extended attribute */ ] interface Foo { ... };
interface Bar { ... };
getter DOMString (unsigned long index); // Not: DOMString(unsigned long index)
// Indexed property operations getter DOMString (unsigned long index); setter DOMString (unsigned long index, DOMString value); deleter boolean (unsigned long index); // Named property operations getter DOMString (DOMString name); setter DOMString (DOMString name, DOMString value); deleter boolean (DOMString name); SemanticsWeb IDL exposes an interface to JavaScript, which is implemented in C++. Thus its semantics bridge these two languages, though it is not identical to either. Web IDL's semantics are much closer to C++ than to JavaScript – in practice, it is a relatively thin abstraction layer over C++. Thus C++ implementations are quite close to the IDL spec, though the resulting interface is somewhat unnatural from a JavaScript perspective: it behaves differently from normal JavaScript libraries.
TypesSee: Web IDL types.
Primitive types in Web IDL are very close to fundamental types in C++ (booleans, characters, integers, and floats), though note that there is no
int type in Web IDL (specs usually use long instead).undefined and nullJavaScript has two special values,
undefined and null , which are often confusing and do not fit easily into C++. Indeed, precise behavior of undefined in Web IDL has varied over time and is under discussion (see W3C Bug 23532 - Dealing with undefined).Behavior on
undefined and null MUST be tested in web tests, as these can be passed and are easy to get wrong. If these tests are omitted, there may be crashes (which will be caught by ClusterFuzz) or behavioral bugs (which will show up as web pages or JavaScript libraries breaking).For the purposes of Blink, behavior can be summarized as follows:
Function resolutionWeb IDL has required arguments and optional arguments. JavaScript does not: omitted arguments have Thus if you have the following Web IDL function declaration: interface A { void foo(long x); }; ...the JavaScript However, in JavaScript the corresponding function can be called without arguments: function foo(x) { return x } foo() // undefined Note that To get similar behavior in Web IDL, the argument can be explicitly specified as For example, given an optional argument such as: interface A { void foo(optional long x); };
This results in a = new A(); a.foo() being legal, and calling the underlying Blink C++ function implementing For overloaded operations, the situation is more complicated, and not currently implemented in Blink (Bug 293561). See the overload resolution algorithm in the spec for details. Pragmatically, passing Passing interface A { void foo(optional long x); void foo(Node x); }; This results in a = new A(); a.foo(undefined) resolving to the first Note that Blink code implementing a function can also check arguments, and similarly, JavaScript functions can check arguments, and access the number of arguments via Warning: File organizationThe Web IDL spec treats the Web API as a single API, spread across various IDL fragments. In practice these fragments are
.idl files, stored in the codebase alongside their implementation, with basename equal to the interface name. Thus for example the fragment defining the Node interface is written in node.idl , which is stored in the third_party/blink/renderer/core/dom directory, and is accompanied by node.h and node.cc in the same directory. In some cases the implementation has a different name, in which case there must be an [ImplementedAs=...] extended attribute in the IDL file, and the .h/.cc files have basename equal to the value of the [ImplementedAs=...] .For simplicity, each IDL file contains a single interface or dictionary, and contains all information needed for that definition, except for dependencies (below), notably any enumerations, implements statements, typedefs, and callback functions.
DependenciesIn principle (as a matter of the Web IDL spec) any IDL file can depend on any other IDL file, and thus changing one file can require rebuilding all the dependents. In practice there are 4 kinds of dependencies (since other required definitions, like enumerations and typedefs, are contained in the IDL file for the interface):
In practice, what happens is that, when compiling a given interfaces, its partial interfaces and the other interfaces it implements are merged into a single data structure, and that is compiled. There is a small amount of data recording where exactly a member came from (so the correct C++ class can be called), and a few other extended attributes for switching the partial/implemented interface on or off, but otherwise it is as if all members were specified in a single
interface statement. This is a deep dependency relationship: any change in the partial/implemented interface changes the bindings for the overall (merged) interface, since all the data is in fact used.Bindings for interfaces in general do not depend on their ancestors, beyond the name of their immediate parent. This is because the bindings just generate a class, which refers to the parent class, but otherwise is subject to information hiding. However, in a few cases bindings depend on whether the interface inherits from some other interface (notably EventHandler or Node), and in a few cases bindings depend on the extended attributes of ancestors (these extended attributes are "inherited"; the list is compute_dependencies.INHERITED_EXTENDED_ATTRIBUTES, and consists of extended attributes that affect memory management). There is thus a shallow dependency on ancestors, specifically only on the ancestor chain and on inherited extended attributes, not on the other contents of ancestors. On the other hand, the dependencies on used interfaces – so-called cross dependencies – are generally shallow dependency relationships: the using interface does not need to know much about the used interface (currently just the name of the implementing class, and whether the interface is a callback interface or not). Thus almost all changes in the used interface do not change the bindings for the using interface: the public information used by other bindings is minimal. There is one exception, namely the IDL extended attribute validatorTo avoid bugs caused by typos in extended attributes in IDL files, the extended attribute validator was introduced to the Blink build flow to check if all the extended attributes used in IDL files are implemented in the code generator. If you use an extended attribute not implemented in code generators, the extended attribute validator fails, and the Blink build fails. A list of IDL attributes implemented in code generators is described in IDLExtendedAttributes.txt. If you want to add a new IDL attribute, you need to
Note that the validator checks for known extended attributes and their arguments (if any), but does not enforce correct use of the the attributes. A warning will not be issued if, for example,
[Clamp] is specified on an interface.TestsReference tests (run-bindings-tests)third_party/blink/tools/run_bindings_tests.py tests the code generator, including its treatment of extended attributes. Specifically, run_bindings_tests.py compiles the IDL files in bindings/tests/idls, and then compares the results against reference files in bindings/tests/results. For example, run_bindings_tests.py reads test_object.idl, and then compares the generated results against v8_test_object.h and v8_test_object.cc, reporting any differences. If you change the behavior of the code generator or add a new extended attribute, please add suitable test cases, preferably reusing existing IDL files (this is to minimize size of diffs when making changes to overall generated bindings). You can reset the run-bindings-tests results using the --reset-results option: third_party/blink/tools/run_bindings_tests.py --reset-results run_bindings_tests.py is run in a presubmit script for any changes to Source/bindings: this requires you to update test results when you change the behavior of the code generator, and thus if test results get out of date, the presubmit test will fail: you won't be able to upload your patch via git-cl, and the CQ will refuse to process the patch. The objective of run-bindings-tests is to show you and reviewers how the code generation is changed by your patch. If you change the behavior of code generators, you need to update the results of run-bindings-tests. Despite these checks, sometimes the test results can get out of date; this is primarily due to dcommitting or changes in real IDL files (not in Source/bindings) that are used in test cases. If the results are out of date prior to your CL, please rebaseline them separately, before committing your CL, as otherwise it will be difficult to distinguish which changes are due to your CL and which are due to rebaselining due to older CLs. Note that using real interfaces in test IDL files means changes to real IDL files can break run-bindings-tests (e.g., Blink r174804/CL 292503006: Oilpan: add [WillBeGarbageCollected] for Node., since Node is inherited by test files). This is ok (we're not going to run run_bindings_tests.py on every IDL edit, and it's easy to fix), but something to be aware of. It is also possible for run_bindings_tests.py to break for other reasons, since it use the developer's local tree: it thus may pass locally but fail remotely, or conversely. For example, renaming Python files can result in outdated bytecode (.pyc files) being used locally and succeeding, even if run_bindings_tests.py is incompatible with current Python source (.py), as discussed and fixed in CL 301743008.
Behavior testsTo test behavior, use web tests, most simply actual interfaces that use the behavior you're implementing. If adding new behavior, it's preferable to make code generator changes and the first actual use case in the same CL, so that it is properly tested, and the changes appear in the context of an actual change. If this makes the CL too large, these can be split into a CG-only CL and an actual use CL, committed in sequence, but unused features should not be added to the CG. For general behavior, like type conversions, there are some internal tests, like bindings/webidl-type-mapping.html, which uses testing/type_conversions.idl. There are also some other IDL files in testing, like testing/internals.idl.
Where is the bindings code generated?By reading this document you can learn how extended attributes work. However, the best practice to understand extended attributes is to try to use some and watch what kind of bindings code is generated. If you change an IDL file and rebuild (e.g., with ninja or Make), the bindings for that IDL file (and possibly others, if there are dependents) will be rebuilt. If the bindings have changed (in ninja), or even if they haven't (in other build systems), it will also recompile the bindings code. Regenerating bindings for a single IDL file is very fast, but regenerating all of them takes several minutes of CPU time. In case of xxx.idl in the Release build, the bindings code is generated in the following files ("Release" becomes "Debug" in the Debug build). out/Release/gen/third_party/blink/renderer/bindings/{core,modules}/v8_xxx.{h,cc} Limitations and improvementsA few parts of the Web IDL spec are not implemented; features are implemented on an as-needed basis. See component:Blink>Bindings for open bugs; please feel free to file bugs or contact bindings developers (members of blink-reviews-bindings, or bindings/OWNERS) if you have any questions, problems, or requests. Bindings generation can be controlled in many ways, generally by adding an extended attribute to specify the behavior, sometimes by special-casing a specific type, interface, or member. If the existing extended attributes are not sufficient (or buggy), please file a bug and contact bindings developers! Some commonly encountered limitations and suitable workarounds are listed below. Generally limitations can be worked around by using custom bindings, but these should be avoided if possible. If you need to work around a limitation, please put a TODO with the bug number (as demonstrated below) in the IDL so that we can remove the hack when the feature is implemented.Syntax error causes infinite loopSome syntax errors cause the IDL parser to enter an infinite loop (Bug 363830). Until this is fixed, if the compiler hangs, please terminate the compiler and check your syntax.
Type checkingThe bindings do not do full type checking (Bug 321518). They do some type checking, but not all. Notably nullability is not strictly enforced. See Bindings development Mailing ListIf working on bindings, you likely wish to join the blink-reviews-bindings mailing list.See also
Derived from: http://trac.webkit.org/wiki/WebKitIDL Licensed under BSD:
Copyright (C) 2009 Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |