From bb7271aa4381120bc45cbe74500e9f72f7162da3 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 28 Sep 2018 14:16:17 -0700 Subject: [PATCH] Fix our WebIDL for Safari This commit employs the strategy described in #908 to apply a non-breaking change to fix WebIDL to be compatible with all browsers, including Safari. The problem here is that `BaseAudioContext` and `AudioScheduledSourceNode` are not types in Safari, but they are types in Firefox/Chrome. The fix here was to move the contents of these two interfaces into mixins, and then include the mixins in all classes which inherit from these two classes. That should have the same effect as defining the methods inherently on the original interface. Additionally a special `[RustDeprecated]` attribute to WebIDL was added to signify interfaces this has happened to. Currently it's directly tailored towards this case of "this intermediate class doesn't exist in all browsers", but we may want to refine and extend the deprecation message over time. Although it's possible we could do this as a breaking change to `web-sys` I'm hoping that we can do this as a non-breaking change for now and then eventually on the next breaking release batch all these changes together, deleting the intermediate classes. This is also hopefully a good trial run for how stable web-sys can be when it's actually stable! cc #897 cc #908 --- crates/web-sys/src/lib.rs | 1 + .../enabled/AudioBufferSourceNode.webidl | 5 ++ .../webidls/enabled/AudioContext.webidl | 2 + .../enabled/AudioScheduledSourceNode.webidl | 6 ++ .../webidls/enabled/BaseAudioContext.webidl | 6 ++ .../webidls/enabled/ConstantSourceNode.webidl | 2 + .../enabled/OfflineAudioContext.webidl | 2 + .../webidls/enabled/OscillatorNode.webidl | 2 + crates/webidl/src/first_pass.rs | 3 + crates/webidl/src/lib.rs | 26 ++++++--- crates/webidl/src/util.rs | 25 ++++++++- examples/webaudio/Cargo.toml | 2 - examples/webaudio/src/lib.rs | 56 +++++++------------ 13 files changed, 90 insertions(+), 48 deletions(-) diff --git a/crates/web-sys/src/lib.rs b/crates/web-sys/src/lib.rs index 3f01918110e..c09c36e3978 100755 --- a/crates/web-sys/src/lib.rs +++ b/crates/web-sys/src/lib.rs @@ -12,6 +12,7 @@ //! require. #![doc(html_root_url = "https://docs.rs/web-sys/0.2")] +#![allow(deprecated)] extern crate js_sys; extern crate wasm_bindgen; diff --git a/crates/web-sys/webidls/enabled/AudioBufferSourceNode.webidl b/crates/web-sys/webidls/enabled/AudioBufferSourceNode.webidl index 729550c5d73..cc98e587011 100644 --- a/crates/web-sys/webidls/enabled/AudioBufferSourceNode.webidl +++ b/crates/web-sys/webidls/enabled/AudioBufferSourceNode.webidl @@ -32,7 +32,12 @@ interface AudioBufferSourceNode : AudioScheduledSourceNode { attribute double loopStart; attribute double loopEnd; + attribute EventHandler onended; + [Throws] void start(optional double when = 0, optional double grainOffset = 0, optional double grainDuration); + + [Throws] + void stop (optional double when = 0); }; diff --git a/crates/web-sys/webidls/enabled/AudioContext.webidl b/crates/web-sys/webidls/enabled/AudioContext.webidl index d115b968b62..ca5187ee8eb 100644 --- a/crates/web-sys/webidls/enabled/AudioContext.webidl +++ b/crates/web-sys/webidls/enabled/AudioContext.webidl @@ -37,3 +37,5 @@ interface AudioContext : BaseAudioContext { [NewObject, Throws] MediaStreamAudioDestinationNode createMediaStreamDestination(); }; + +AudioContext includes rustBaseAudioContext; diff --git a/crates/web-sys/webidls/enabled/AudioScheduledSourceNode.webidl b/crates/web-sys/webidls/enabled/AudioScheduledSourceNode.webidl index 2acf6373b32..39f4d2c87f2 100644 --- a/crates/web-sys/webidls/enabled/AudioScheduledSourceNode.webidl +++ b/crates/web-sys/webidls/enabled/AudioScheduledSourceNode.webidl @@ -10,7 +10,13 @@ * liability, trademark and document use rules apply. */ +[RustDeprecated="doesn't exist in Safari, use parent class methods instead"] interface AudioScheduledSourceNode : AudioNode { +}; + +AudioScheduledSourceNode includes rustAudioScheduledSourceNode; + +interface mixin rustAudioScheduledSourceNode { attribute EventHandler onended; [Throws] void start (optional double when = 0); diff --git a/crates/web-sys/webidls/enabled/BaseAudioContext.webidl b/crates/web-sys/webidls/enabled/BaseAudioContext.webidl index e8796bb9f3a..1634cdd33fd 100644 --- a/crates/web-sys/webidls/enabled/BaseAudioContext.webidl +++ b/crates/web-sys/webidls/enabled/BaseAudioContext.webidl @@ -19,7 +19,13 @@ enum AudioContextState { "closed" }; +[RustDeprecated="doesn't exist in Safari, use `AudioContext` instead now"] interface BaseAudioContext : EventTarget { +}; + +BaseAudioContext includes rustBaseAudioContext; + +interface mixin rustBaseAudioContext { readonly attribute AudioDestinationNode destination; readonly attribute float sampleRate; readonly attribute double currentTime; diff --git a/crates/web-sys/webidls/enabled/ConstantSourceNode.webidl b/crates/web-sys/webidls/enabled/ConstantSourceNode.webidl index cd005897aac..238e4c05d49 100644 --- a/crates/web-sys/webidls/enabled/ConstantSourceNode.webidl +++ b/crates/web-sys/webidls/enabled/ConstantSourceNode.webidl @@ -19,3 +19,5 @@ dictionary ConstantSourceOptions { interface ConstantSourceNode : AudioScheduledSourceNode { readonly attribute AudioParam offset; }; + +ConstantSourceNode includes rustAudioScheduledSourceNode; diff --git a/crates/web-sys/webidls/enabled/OfflineAudioContext.webidl b/crates/web-sys/webidls/enabled/OfflineAudioContext.webidl index a2498d6e56a..d4aae25e669 100644 --- a/crates/web-sys/webidls/enabled/OfflineAudioContext.webidl +++ b/crates/web-sys/webidls/enabled/OfflineAudioContext.webidl @@ -29,3 +29,5 @@ interface OfflineAudioContext : BaseAudioContext { readonly attribute unsigned long length; attribute EventHandler oncomplete; }; + +OfflineAudioContext includes rustBaseAudioContext; diff --git a/crates/web-sys/webidls/enabled/OscillatorNode.webidl b/crates/web-sys/webidls/enabled/OscillatorNode.webidl index 65029ccb28b..2322c4ae8fa 100644 --- a/crates/web-sys/webidls/enabled/OscillatorNode.webidl +++ b/crates/web-sys/webidls/enabled/OscillatorNode.webidl @@ -37,3 +37,5 @@ interface OscillatorNode : AudioScheduledSourceNode { void setPeriodicWave(PeriodicWave periodicWave); }; + +OscillatorNode includes rustAudioScheduledSourceNode; diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index 37ce61c28fb..0fc18c3a007 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -44,6 +44,7 @@ pub(crate) struct FirstPassRecord<'src> { pub(crate) struct InterfaceData<'src> { /// Whether only partial interfaces were encountered pub(crate) partial: bool, + pub(crate) deprecated: Option, pub(crate) attributes: Vec<&'src AttributeInterfaceMember<'src>>, pub(crate) consts: Vec<&'src ConstMember<'src>>, pub(crate) operations: BTreeMap, OperationData<'src>>, @@ -311,6 +312,8 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> { interface_data.partial = false; interface_data.superclass = self.inheritance.map(|s| s.identifier.0); interface_data.definition_attributes = self.attributes.as_ref(); + interface_data.deprecated = util::get_rust_deprecated(&self.attributes) + .map(|s| s.to_string()); } if let Some(attrs) = &self.attributes { for attr in attrs.body.list.iter() { diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index fc6bab60ccb..f4f64835cbf 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -488,18 +488,15 @@ impl<'src> FirstPassRecord<'src> { data: &InterfaceData<'src>, ) { let mut doc_comment = Some(format!("The `{}` object\n\n{}", name, mdn_doc(name, None),)); - let derive = syn::Attribute { - pound_token: Default::default(), - style: syn::AttrStyle::Outer, - bracket_token: Default::default(), - path: Ident::new("derive", Span::call_site()).into(), - tts: quote!((Debug, Clone)), - }; + + let mut attrs = Vec::new(); + attrs.push(parse_quote!( #[derive(Debug, Clone)] )); + self.add_deprecated(data, &mut attrs); let mut import_type = backend::ast::ImportType { vis: public(), rust_name: rust_ident(camel_case_ident(name).as_str()), js_name: name.to_string(), - attrs: vec![derive], + attrs, doc_comment: None, instanceof_shim: format!("__widl_instanceof_{}", name), extends: Vec::new(), @@ -539,6 +536,7 @@ impl<'src> FirstPassRecord<'src> { self.member_attribute( program, name, + data, member.modifier, member.readonly.is_some(), &member.type_, @@ -559,6 +557,7 @@ impl<'src> FirstPassRecord<'src> { self.member_attribute( program, name, + data, if let Some(s) = member.stringifier { Some(weedle::interface::StringifierOrInheritOrStatic::Stringifier(s)) } else { @@ -578,6 +577,7 @@ impl<'src> FirstPassRecord<'src> { &self, program: &mut backend::ast::Program, self_name: &'src str, + data: &InterfaceData<'src>, modifier: Option, readonly: bool, type_: &'src weedle::types::AttributedType<'src>, @@ -620,6 +620,7 @@ impl<'src> FirstPassRecord<'src> { let mut doc = import_function.doc_comment.take(); self.append_required_features_doc(&import_function, &mut doc, &[]); import_function.doc_comment = doc; + self.add_deprecated(data, &mut import_function.function.rust_attrs); program.imports.push(wrap_import_function(import_function)); } } @@ -677,10 +678,19 @@ impl<'src> FirstPassRecord<'src> { let mut doc = doc.clone(); self.append_required_features_doc(&method, &mut doc, &[]); method.doc_comment = doc; + self.add_deprecated(data, &mut method.function.rust_attrs); program.imports.push(wrap_import_function(method)); } } + fn add_deprecated(&self, data: &InterfaceData<'src>, dst: &mut Vec) { + let msg = match &data.deprecated { + Some(s) => s, + None => return, + }; + dst.push(parse_quote!( #[deprecated(note = #msg)] )); + } + fn append_required_features_doc( &self, item: impl ImportedTypeReferences, diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index a67ef2560dd..1fa8ee83887 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -7,7 +7,7 @@ use heck::{CamelCase, ShoutySnakeCase, SnakeCase}; use proc_macro2::{Ident, Span}; use syn; use weedle; -use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList}; +use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList, IdentifierOrString}; use weedle::literal::{ConstValue, FloatLit, IntegerLit}; use first_pass::{FirstPassRecord, OperationData, OperationId, Signature}; @@ -666,6 +666,29 @@ pub fn is_no_interface_object(ext_attrs: &Option) -> bool has_named_attribute(ext_attrs.as_ref(), "NoInterfaceObject") } +pub fn get_rust_deprecated<'a>(ext_attrs: &Option>) + -> Option<&'a str> +{ + ext_attrs.as_ref()? + .body + .list + .iter() + .filter_map(|attr| { + match attr { + ExtendedAttribute::Ident(id) => Some(id), + _ => None, + } + }) + .filter_map(|ident| { + match ident.rhs { + IdentifierOrString::String(s) => Some(s), + IdentifierOrString::Identifier(_) => None, + } + }) + .next() + .map(|s| s.0) +} + /// Whether a webidl object is marked as structural. pub fn is_structural( item_attrs: Option<&ExtendedAttributeList>, diff --git a/examples/webaudio/Cargo.toml b/examples/webaudio/Cargo.toml index 1db5cc9fc5d..d4790b2860c 100644 --- a/examples/webaudio/Cargo.toml +++ b/examples/webaudio/Cargo.toml @@ -16,8 +16,6 @@ features = [ 'AudioDestinationNode', 'AudioNode', 'AudioParam', - 'AudioScheduledSourceNode', - 'BaseAudioContext', 'GainNode', 'OscillatorNode', 'OscillatorType', diff --git a/examples/webaudio/src/lib.rs b/examples/webaudio/src/lib.rs index 9cbc3836d0b..e518ce7e256 100644 --- a/examples/webaudio/src/lib.rs +++ b/examples/webaudio/src/lib.rs @@ -2,9 +2,7 @@ extern crate wasm_bindgen; extern crate web_sys; use wasm_bindgen::prelude::*; -use web_sys::{ - AudioContext, AudioNode, AudioScheduledSourceNode, BaseAudioContext, OscillatorType, -}; +use web_sys::{AudioContext, AudioNode, OscillatorType}; /// Converts a midi note to frequency /// @@ -45,22 +43,14 @@ impl Drop for FmOsc { #[wasm_bindgen] impl FmOsc { #[wasm_bindgen(constructor)] - pub fn new() -> FmOsc { - let ctx = web_sys::AudioContext::new().unwrap(); - let primary; - let fm_osc; - let gain; - let fm_gain; + pub fn new() -> Result { + let ctx = web_sys::AudioContext::new()?; - { - let base: &BaseAudioContext = ctx.as_ref(); - - // Create our web audio objects. - primary = base.create_oscillator().unwrap(); - fm_osc = base.create_oscillator().unwrap(); - gain = base.create_gain().unwrap(); - fm_gain = base.create_gain().unwrap(); - } + // Create our web audio objects. + let primary = ctx.create_oscillator()?; + let fm_osc = ctx.create_oscillator()?; + let gain = ctx.create_gain()?; + let fm_gain = ctx.create_gain()?; // Some initial settings: primary.set_type(OscillatorType::Sine); @@ -76,42 +66,34 @@ impl FmOsc { let gain_node: &AudioNode = gain.as_ref(); let fm_osc_node: &AudioNode = fm_osc.as_ref(); let fm_gain_node: &AudioNode = fm_gain.as_ref(); - let base: &BaseAudioContext = ctx.as_ref(); - let destination = base.destination(); + let destination = ctx.destination(); let destination_node: &AudioNode = destination.as_ref(); // Connect the nodes up! // The primary oscillator is routed through the gain node, so that // it can control the overall output volume. - primary_node.connect_with_audio_node(gain.as_ref()).unwrap(); + primary_node.connect_with_audio_node(gain.as_ref())?; // Then connect the gain node to the AudioContext destination (aka // your speakers). - gain_node.connect_with_audio_node(destination_node).unwrap(); + gain_node.connect_with_audio_node(destination_node)?; // The FM oscillator is connected to its own gain node, so it can // control the amount of modulation. - fm_osc_node - .connect_with_audio_node(fm_gain.as_ref()) - .unwrap(); + fm_osc_node.connect_with_audio_node(fm_gain.as_ref())?; + // Connect the FM oscillator to the frequency parameter of the main // oscillator, so that the FM node can modulate its frequency. - fm_gain_node - .connect_with_audio_param(&primary.frequency()) - .unwrap(); + fm_gain_node.connect_with_audio_param(&primary.frequency())?; } // Start the oscillators! - AsRef::::as_ref(&primary) - .start() - .unwrap(); - AsRef::::as_ref(&fm_osc) - .start() - .unwrap(); - - FmOsc { + primary.start()?; + fm_osc.start()?; + + Ok(FmOsc { ctx, primary, gain, @@ -119,7 +101,7 @@ impl FmOsc { fm_osc, fm_freq_ratio: 0.0, fm_gain_ratio: 0.0, - } + }) } /// Sets the gain for this oscillator, between 0.0 and 1.0.