How to work on a Web API with the assistance of Copilot
- Read-up on the relevant spec.
- Add
.webidl
file(s) in this folder for each interface that you want to implement. If one already exists, you want to add the missing parts to it. - For each interface, this will generate a trait named
{interface_name}Methods
, accessible viause crate::dom::bindings::codegen::Bindings::{interface_name}Binding
. - Use this trait by:
- Adding a matching struct, using
#[dom_struct]
- Adding methods with
todo!
bodies.
- Adding a matching struct, using
- At this point, the struct can have only one member:
reflector_: Reflector,
- The struct should be documented with a link to its interface in the spec,
- The trait methods should each be documented with a link to their definition in the interface.
- Example result.
- Back in the spec,
- Read-up on DOM interfaces.
- Using what you know, for each internal slot of the interface:
- Add an appropriate member to the struct(s) added at 4.
- If this requires defining other structs or enums, these should derive
JSTraceable
andMallocSizeOf
(example). - Any
JSTraceable
structs added above that contains members that must be rooted because they are either JS values or Dom Objects should be marked with#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
. Example, where the lint is needed due to the presence of aJSVal
. - If such a struct is assigned to a variable,
impl js::gc::Rootable
for the and userooted!
to root the variabe(example). - All of this can be changed later, so simply use your best judgement at this stage.
- Add methods for construction(not to be confused with a
Constructor
that is part of the Web API). - Example result.
- For each methods of the bindings trait referred to at 3 in Part 1:
- In general, follow the structure of the spec: if a method calls into another named algorithm, implement that named algorithm as a separate private method of your struct, that the trait methods calls into. If you later realize this private method can be used from other structs, make it
pub(crate)
. - For each algorithm step in the spec:
- Copy the prose from the spec, but ommit the step number(these change too often).
- Paste the prose into the code.
- Here is where Copilot comes in: review the suggestion, and accept it if correct.
- At irregular but frequent interval:
./mach check
your code, and otherwise review it for correctness. - If Copilot suggest a non-existent method call, this may in fact be a good name for a named algorithm to be implemented: review the suggestion and implement the method if appropriate(perhaps just with a
todo!
for now). - Be wary of multi-line suggestions: these can be correct, and even include subsequent steps from the specification, all correctly documented. But it can also include non-sense. One line at a time is more reliable. When defining a new method, Copilot can suggest a useful signature, and sometimes even the entire body, but it is best to accept everyhting but then delete the body and re-do it one line at the time.
- Copilot is good for boilerplate that often comes-up, such as the need to react to the fulfillment or rejection of a promise. This will require multiple lines of code and the definition of additional structs, which is again best done by acception suggestions one line at a time(or sometimes one method or struct signature at a time).
- In order to protect against copyright infringement, please set the suggestions matching public code setting to Blocked.
- Example result
- In general, follow the structure of the spec: if a method calls into another named algorithm, implement that named algorithm as a separate private method of your struct, that the trait methods calls into. If you later realize this private method can be used from other structs, make it
- Note: there are certain things that are often needed to perform operation as part of an algorithm:
SafeJSContext
: can be obtained usingGlobalScope::get_cx()
.GlobalScope
: can be obtained usingself.global()
on adom_struct
, orGlobalScope::from_safe_context
InRealm
: can be obtained as an argument to the generated trait method, using this configuration fileCanGc
: same as above- It is best to access them as early as possible, say at the top of the trait method implementation, and to pass them down(as ref for
GlobalScope
) as arguments, in the order described above(with any other needed argument coming in between&GlobalScope
andInRealm
).
- This should give you a complete first draft.
- Now comes the time to identify which WPT test to run against your first draft. You can find them here.
- This may require turning them on, using this config.
- Test may fail because:
- There is a bug in the code. These should be fixed.
- The test uses other APIs that aren't supported yet(usually
ERROR
).
- Bugs should be fixed. Copilot is of little help here.
- Expected failures can be marked as such, using the process described here.
- This part is done when there are no unexpected test results left.
- On occasion, on the advice of a reviewer, you may file an issue and describe a failure that you cannot fix, mark the test as a failure, and leave it to a follow-up.
- While you may ask for a review at any time if stuck, now is the time to take a last look at your code and decide if you want to refactor anything.
- Copilot is also useful at this stage:
- You can use the chat to ask it to perform a refactoring.
- The resulting edits should be reviewed and accepted one at a time.
- If you are satisfied, now is the time to ask for a final review.
- Congratulations, the new Web API you implemented should soon merge.