Implement WebIDL callback interfaces

This commit implements callback interfaces for WebIDL, the final WebIDL
construct that we were unconditionally ignoring! Callback interfaces are
implemented as dictionaries of callbacks. Single-operation callback interfaces
are also expanded when flattening to accept a `Function` as well, in accordance
with the WebIDL spec.

New features have been added to `web-sys` for all the new callback interface
types. Additionally the `EventTarget.webidl` was tweaked to not have
`EventListener?` as this is required for all functional usage and there's no
need to keep that sort of web browser compat here.

Closes #258
This commit is contained in:
Alex Crichton
2018-09-10 11:16:55 -07:00
parent 2cf82bc0b3
commit 8181f7fa95
9 changed files with 165 additions and 11 deletions

View File

@ -42,9 +42,10 @@ use backend::util::{ident_ty, rust_ident, raw_ident, wrap_import_function};
use proc_macro2::{Ident, Span};
use weedle::attribute::{ExtendedAttributeList};
use weedle::dictionary::DictionaryMember;
use weedle::interface::InterfaceMember;
use first_pass::{FirstPass, FirstPassRecord, OperationId, InterfaceData};
use first_pass::OperationData;
use first_pass::{OperationData, CallbackInterfaceData};
use util::{public, webidl_const_v_to_backend_const_v, TypePosition, camel_case_ident, shouty_snake_case_ident, snake_case_ident, mdn_doc};
use idl_type::ToIdlType;
@ -108,6 +109,11 @@ fn parse(webidl_source: &str, allowed_types: Option<&[&str]>)
first_pass_record.append_interface(&mut program, name, d);
}
}
for (name, d) in first_pass_record.callback_interfaces.iter() {
if filter(name) {
first_pass_record.append_callback_interface(&mut program, d);
}
}
// Prune out `extends` annotations that aren't defined as these shouldn't
// prevent the type from being usable entirely. They're just there for
@ -644,4 +650,38 @@ impl<'src> FirstPassRecord<'src> {
list,
));
}
fn append_callback_interface(
&self,
program: &mut backend::ast::Program,
item: &CallbackInterfaceData<'src>,
) {
let mut fields = Vec::new();
for member in item.definition.members.body.iter() {
match member {
InterfaceMember::Operation(op) => {
let identifier = match op.identifier {
Some(i) => i.0,
None => continue,
};
let pos = TypePosition::Argument;
fields.push(ast::DictionaryField {
required: false,
name: rust_ident(&snake_case_ident(identifier)),
ty: idl_type::IdlType::Callback.to_syn_type(pos)
.unwrap(),
});
}
_ => {
warn!("skipping callback interface member on {}",
item.definition.identifier.0);
}
}
}
program.dictionaries.push(ast::Dictionary {
name: rust_ident(&camel_case_ident(item.definition.identifier.0)),
fields,
});
}
}