From 01a52233d314935ee6f95dffd058bc1e2b791df5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 12 Apr 2019 11:08:05 -0700 Subject: [PATCH] Support [NoInterfaceObject] in `web-sys` This commit enables `[NoInterfaceObject]` annotated interfaces in `web-sys`. The `NoInterfaceObject` attribute means that there's not actually a JS class for the object, but all of its properties and such can still be accessed structually and invoked. This should help provide more bindings for some more common types on the web! Note that this builds on recent features to ensure that `dyn_into` and friends always fail for `NoInterfaceObject` objects because they don't actually have a class. Closes #893 Closes #1257 Closes #1315 --- crates/web-sys/Cargo.toml | 78 +++++++++++++++++++++++++ crates/webidl-tests/main.rs | 1 + crates/webidl-tests/no_interface.js | 8 +++ crates/webidl-tests/no_interface.rs | 11 ++++ crates/webidl-tests/no_interface.webidl | 9 +++ crates/webidl/src/first_pass.rs | 25 +++----- crates/webidl/src/lib.rs | 6 +- 7 files changed, 121 insertions(+), 17 deletions(-) create mode 100644 crates/webidl-tests/no_interface.js create mode 100644 crates/webidl-tests/no_interface.rs create mode 100644 crates/webidl-tests/no_interface.webidl diff --git a/crates/web-sys/Cargo.toml b/crates/web-sys/Cargo.toml index 557f5381..67851e13 100644 --- a/crates/web-sys/Cargo.toml +++ b/crates/web-sys/Cargo.toml @@ -56,6 +56,7 @@ Algorithm = [] AlignSetting = [] AnalyserNode = [] AnalyserOptions = [] +AngleInstancedArrays = [] Animation = [] AnimationEffect = [] AnimationEvent = [] @@ -63,6 +64,7 @@ AnimationEventInit = [] AnimationPlayState = [] AnimationPlaybackEvent = [] AnimationPlaybackEventInit = [] +AnimationPropertyDetails = [] AnimationPropertyValueDetails = [] AnimationTimeline = [] AssignedNodesOptions = [] @@ -159,6 +161,7 @@ ChromeFilePropertyBag = [] ChromeWorker = [] Client = [] ClientQueryOptions = [] +ClientRectsAndTexts = [] ClientType = [] Clients = [] ClipboardEvent = [] @@ -195,6 +198,7 @@ ContextAttributes2d = [] ConvertCoordinateOptions = [] ConvolverNode = [] ConvolverOptions = [] +Coordinates = [] Credential = [] CredentialCreationOptions = [] CredentialRequestOptions = [] @@ -240,6 +244,7 @@ DecoderDoctorNotificationType = [] DedicatedWorkerGlobalScope = [] DelayNode = [] DelayOptions = [] +DeviceAcceleration = [] DeviceAccelerationInit = [] DeviceLightEvent = [] DeviceLightEventInit = [] @@ -249,6 +254,7 @@ DeviceOrientationEvent = [] DeviceOrientationEventInit = [] DeviceProximityEvent = [] DeviceProximityEventInit = [] +DeviceRotationRate = [] DeviceRotationRateInit = [] DhKeyDeriveParams = [] DirectionSetting = [] @@ -311,11 +317,22 @@ EventModifierInit = [] EventSource = [] EventSourceInit = [] EventTarget = [] +Exception = [] +ExtBlendMinmax = [] +ExtColorBufferFloat = [] +ExtColorBufferHalfFloat = [] +ExtDisjointTimerQuery = [] +ExtFragDepth = [] +ExtSRgb = [] +ExtShaderTextureLod = [] +ExtTextureFilterAnisotropic = [] ExtendableEvent = [] ExtendableEventInit = [] ExtendableMessageEvent = [] ExtendableMessageEventInit = [] +External = [] FakePluginMimeEntry = [] +FakePluginTagInit = [] FetchEvent = [] FetchEventInit = [] FetchObserver = [] @@ -345,6 +362,7 @@ FontFace = [] FontFaceDescriptors = [] FontFaceLoadStatus = [] FontFaceSet = [] +FontFaceSetIterator = [] FontFaceSetIteratorResult = [] FontFaceSetLoadEvent = [] FontFaceSetLoadEventInit = [] @@ -368,8 +386,10 @@ GamepadHapticActuatorType = [] GamepadMappingType = [] GamepadPose = [] GamepadServiceTest = [] +Geolocation = [] GetNotificationOptions = [] GetRootNodeOptions = [] +GetUserMediaRequest = [] GridDeclaration = [] GridTrackState = [] GroupedHistoryEventInit = [] @@ -416,6 +436,7 @@ HtmlHeadElement = [] HtmlHeadingElement = [] HtmlHrElement = [] HtmlHtmlElement = [] +HtmlHyperlinkElementUtils = [] HtmlIFrameElement = [] HtmlImageElement = [] HtmlInputElement = [] @@ -491,10 +512,12 @@ IdbVersionChangeEventInit = [] IdleDeadline = [] IdleRequestOptions = [] IirFilterNode = [] +IirFilterOptions = [] ImageBitmap = [] ImageBitmapFormat = [] ImageBitmapRenderingContext = [] ImageCapture = [] +ImageCaptureError = [] ImageCaptureErrorEvent = [] ImageCaptureErrorEventInit = [] ImageData = [] @@ -505,12 +528,14 @@ IntersectionObserver = [] IntersectionObserverEntry = [] IntersectionObserverEntryInit = [] IntersectionObserverInit = [] +IntlUtils = [] IterableKeyAndValueResult = [] IterableKeyOrValueResult = [] IterationCompositeOperation = [] JsonWebKey = [] KeyAlgorithm = [] KeyEvent = [] +KeyIdsInitData = [] KeyboardEvent = [] KeyboardEventInit = [] KeyframeEffect = [] @@ -519,6 +544,7 @@ L10nElement = [] L10nValue = [] LifecycleCallbacks = [] LineAlignSetting = [] +ListBoxObject = [] LocalMediaStream = [] LocaleInfo = [] Location = [] @@ -569,6 +595,7 @@ MediaStreamAudioDestinationNode = [] MediaStreamAudioSourceNode = [] MediaStreamAudioSourceOptions = [] MediaStreamConstraints = [] +MediaStreamError = [] MediaStreamEvent = [] MediaStreamEventInit = [] MediaStreamTrack = [] @@ -602,6 +629,7 @@ MimeTypeArray = [] MouseEvent = [] MouseEventInit = [] MouseScrollEvent = [] +MozDebug = [] MutationEvent = [] MutationObserver = [] MutationObserverInit = [] @@ -612,6 +640,7 @@ NativeOsFileReadOptions = [] NativeOsFileWriteAtomicOptions = [] NavigationType = [] Navigator = [] +NavigatorAutomationInformation = [] NetworkCommandOptions = [] NetworkInformation = [] NetworkResultOptions = [] @@ -627,6 +656,13 @@ NotificationEventInit = [] NotificationOptions = [] NotificationPermission = [] ObserverCallback = [] +OesElementIndexUint = [] +OesStandardDerivatives = [] +OesTextureFloat = [] +OesTextureFloatLinear = [] +OesTextureHalfFloat = [] +OesTextureHalfFloatLinear = [] +OesVertexArrayObject = [] OfflineAudioCompletionEvent = [] OfflineAudioCompletionEventInit = [] OfflineAudioContext = [] @@ -672,6 +708,7 @@ PerformanceNavigation = [] PerformanceNavigationTiming = [] PerformanceObserver = [] PerformanceObserverEntryList = [] +PerformanceObserverInit = [] PerformanceResourceTiming = [] PerformanceServerTiming = [] PerformanceTiming = [] @@ -693,7 +730,9 @@ PopStateEvent = [] PopStateEventInit = [] PopupBlockedEvent = [] PopupBlockedEventInit = [] +Position = [] PositionAlignSetting = [] +PositionError = [] PositionOptions = [] Presentation = [] PresentationAvailability = [] @@ -716,9 +755,11 @@ ProfileTimelineStackFrame = [] ProfileTimelineWorkerOperationType = [] ProgressEvent = [] ProgressEventInit = [] +PromiseNativeHandler = [] PromiseRejectionEvent = [] PromiseRejectionEventInit = [] PublicKeyCredential = [] +PublicKeyCredentialCreationOptions = [] PublicKeyCredentialDescriptor = [] PublicKeyCredentialEntity = [] PublicKeyCredentialParameters = [] @@ -792,6 +833,7 @@ RtcIdentityAssertionResult = [] RtcIdentityProvider = [] RtcIdentityProviderDetails = [] RtcIdentityProviderOptions = [] +RtcIdentityProviderRegistrar = [] RtcIdentityValidationResult = [] RtcInboundRtpStreamStats = [] RtcLifecycleEvent = [] @@ -844,6 +886,7 @@ ScreenOrientation = [] ScriptProcessorNode = [] ScrollAreaEvent = [] ScrollBehavior = [] +ScrollBoxObject = [] ScrollIntoViewOptions = [] ScrollLogicalPosition = [] ScrollOptions = [] @@ -952,7 +995,27 @@ SvgMetadataElement = [] SvgNumber = [] SvgNumberList = [] SvgPathElement = [] +SvgPathSeg = [] +SvgPathSegArcAbs = [] +SvgPathSegArcRel = [] +SvgPathSegClosePath = [] +SvgPathSegCurvetoCubicAbs = [] +SvgPathSegCurvetoCubicRel = [] +SvgPathSegCurvetoCubicSmoothAbs = [] +SvgPathSegCurvetoCubicSmoothRel = [] +SvgPathSegCurvetoQuadraticAbs = [] +SvgPathSegCurvetoQuadraticRel = [] +SvgPathSegCurvetoQuadraticSmoothAbs = [] +SvgPathSegCurvetoQuadraticSmoothRel = [] +SvgPathSegLinetoAbs = [] +SvgPathSegLinetoHorizontalAbs = [] +SvgPathSegLinetoHorizontalRel = [] +SvgPathSegLinetoRel = [] +SvgPathSegLinetoVerticalAbs = [] +SvgPathSegLinetoVerticalRel = [] SvgPathSegList = [] +SvgPathSegMovetoAbs = [] +SvgPathSegMovetoRel = [] SvgPatternElement = [] SvgPoint = [] SvgPointList = [] @@ -1044,7 +1107,9 @@ TrackEventInit = [] TransitionEvent = [] TransitionEventInit = [] Transport = [] +TreeBoxObject = [] TreeCellInfo = [] +TreeView = [] TreeWalker = [] U2f = [] U2fClientData = [] @@ -1183,6 +1248,19 @@ WebKitCssMatrix = [] WebSocket = [] WebSocketDict = [] WebSocketElement = [] +WebglColorBufferFloat = [] +WebglCompressedTextureAstc = [] +WebglCompressedTextureAtc = [] +WebglCompressedTextureEtc = [] +WebglCompressedTextureEtc1 = [] +WebglCompressedTexturePvrtc = [] +WebglCompressedTextureS3tc = [] +WebglCompressedTextureS3tcSrgb = [] +WebglDebugRendererInfo = [] +WebglDebugShaders = [] +WebglDepthTexture = [] +WebglDrawBuffers = [] +WebglLoseContext = [] WebrtcGlobalStatisticsReport = [] WheelEvent = [] WheelEventInit = [] diff --git a/crates/webidl-tests/main.rs b/crates/webidl-tests/main.rs index 0dffef7f..48546c3e 100644 --- a/crates/webidl-tests/main.rs +++ b/crates/webidl-tests/main.rs @@ -12,3 +12,4 @@ pub mod global; pub mod namespace; pub mod simple; pub mod throws; +pub mod no_interface; diff --git a/crates/webidl-tests/no_interface.js b/crates/webidl-tests/no_interface.js new file mode 100644 index 00000000..d5c65c96 --- /dev/null +++ b/crates/webidl-tests/no_interface.js @@ -0,0 +1,8 @@ +global.GetNoInterfaceObject = class { + static get() { + return { + number: 3, + foo: () => {}, + } + } +}; diff --git a/crates/webidl-tests/no_interface.rs b/crates/webidl-tests/no_interface.rs new file mode 100644 index 00000000..c872fb86 --- /dev/null +++ b/crates/webidl-tests/no_interface.rs @@ -0,0 +1,11 @@ +use js_sys::Object; +use wasm_bindgen_test::*; + +include!(concat!(env!("OUT_DIR"), "/no_interface.rs")); + +#[wasm_bindgen_test] +fn smoke() { + let obj = GetNoInterfaceObject::get(); + assert_eq!(obj.number(), 3.0); + obj.foo(); +} diff --git a/crates/webidl-tests/no_interface.webidl b/crates/webidl-tests/no_interface.webidl new file mode 100644 index 00000000..7a7d4ddb --- /dev/null +++ b/crates/webidl-tests/no_interface.webidl @@ -0,0 +1,9 @@ +[NoInterfaceObject] +interface NoInterfaceObject { + readonly attribute double number; + void foo(); +}; + +interface GetNoInterfaceObject { + static NoInterfaceObject get(); +}; diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index 2413d92d..cc8755bd 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -45,6 +45,7 @@ pub(crate) struct FirstPassRecord<'src> { pub(crate) struct InterfaceData<'src> { /// Whether only partial interfaces were encountered pub(crate) partial: bool, + pub(crate) has_interface: bool, pub(crate) deprecated: Option, pub(crate) attributes: Vec<&'src AttributeInterfaceMember<'src>>, pub(crate) consts: Vec<&'src ConstMember<'src>>, @@ -303,22 +304,14 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> { return Ok(()); } - if util::is_no_interface_object(&self.attributes) { - log::info!( - "Skipping because of `NoInterfaceObject` attribute: {:?}", - self.identifier.0 - ); - return Ok(()); - } - - { - let interface_data = record.interfaces.entry(self.identifier.0).or_default(); - 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()); - } + let interface_data = record.interfaces.entry(self.identifier.0).or_default(); + 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()); + interface_data.has_interface = + !util::is_no_interface_object(&self.attributes); if let Some(attrs) = &self.attributes { for attr in attrs.body.list.iter() { process_interface_attribute(record, self.identifier.0, attr); diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 08a85397..7ab1e0bb 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -514,7 +514,11 @@ impl<'src> FirstPassRecord<'src> { attrs, doc_comment: None, instanceof_shim: format!("__widl_instanceof_{}", name), - is_type_of: None, + is_type_of: if data.has_interface { + None + } else { + Some(syn::parse_quote!{ |_| false }) + }, extends: Vec::new(), vendor_prefixes: Vec::new(), };