diff options
Diffstat (limited to 'components')
22 files changed, 494 insertions, 171 deletions
diff --git a/components/config/prefs.rs b/components/config/prefs.rs index 64dd9659e56..896a4fe0bc4 100644 --- a/components/config/prefs.rs +++ b/components/config/prefs.rs @@ -116,10 +116,6 @@ pub struct Preferences { // https://testutils.spec.whatwg.org#availability pub dom_testutils_enabled: bool, pub dom_trusted_types_enabled: bool, - /// Enable the [URLPattern] API. - /// - /// [URLPattern]: https://developer.mozilla.org/en-US/docs/Web/API/URLPattern - pub dom_urlpattern_enabled: bool, pub dom_xpath_enabled: bool, /// Enable WebGL2 APIs. pub dom_webgl2_enabled: bool, @@ -292,7 +288,6 @@ impl Preferences { dom_testperf_enabled: false, dom_testutils_enabled: false, dom_trusted_types_enabled: false, - dom_urlpattern_enabled: false, dom_webgl2_enabled: false, dom_webgpu_enabled: false, dom_webgpu_wgpu_backend: String::new(), diff --git a/components/layout/display_list/background.rs b/components/layout/display_list/background.rs index 563bce28450..f1099fdab2b 100644 --- a/components/layout/display_list/background.rs +++ b/components/layout/display_list/background.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use app_units::Au; -use euclid::{Point2D, Size2D, Vector2D}; +use euclid::{Size2D, Vector2D}; use style::computed_values::background_attachment::SingleComputedValue as BackgroundAttachment; use style::computed_values::background_clip::single_value::T as Clip; use style::computed_values::background_origin::single_value::T as Origin; @@ -15,7 +15,6 @@ use style::values::specified::background::{ }; use webrender_api::{self as wr, units}; use wr::ClipChainId; -use wr::units::LayoutSize; use crate::replaced::NaturalSizes; @@ -66,8 +65,7 @@ impl<'a> BackgroundPainter<'a> { if &BackgroundAttachment::Fixed == get_cyclic(&background.background_attachment.0, layer_index) { - let viewport_size = builder.compositor_info.viewport_size; - return units::LayoutRect::from_origin_and_size(Point2D::origin(), viewport_size); + return builder.compositor_info.viewport_size.into(); } match get_cyclic(&background.background_clip.0, layer_index) { @@ -132,6 +130,7 @@ impl<'a> BackgroundPainter<'a> { pub(super) fn positioning_area( &self, fragment_builder: &'a super::BuilderForBoxFragment, + builder: &mut super::DisplayListBuilder, layer_index: usize, ) -> units::LayoutRect { if let Some(positioning_area_override) = self.positioning_area_override { @@ -150,14 +149,7 @@ impl<'a> BackgroundPainter<'a> { Origin::PaddingBox => *fragment_builder.padding_rect(), Origin::BorderBox => fragment_builder.border_rect, }, - BackgroundAttachment::Fixed => { - // This isn't the viewport size because that rects larger than the viewport might be - // transformed down into areas smaller than the viewport. - units::LayoutRect::from_origin_and_size( - Point2D::origin(), - LayoutSize::new(f32::MAX, f32::MAX), - ) - }, + BackgroundAttachment::Fixed => builder.compositor_info.viewport_size.into(), } } } @@ -170,7 +162,7 @@ pub(super) fn layout_layer( natural_sizes: NaturalSizes, ) -> Option<BackgroundLayer> { let painting_area = painter.painting_area(fragment_builder, builder, layer_index); - let positioning_area = painter.positioning_area(fragment_builder, layer_index); + let positioning_area = painter.positioning_area(fragment_builder, builder, layer_index); let common = painter.common_properties(fragment_builder, builder, layer_index, painting_area); // https://drafts.csswg.org/css-backgrounds/#background-size diff --git a/components/layout/flow/root.rs b/components/layout/flow/root.rs index 8ad3671032e..fb9884a4f01 100644 --- a/components/layout/flow/root.rs +++ b/components/layout/flow/root.rs @@ -5,6 +5,8 @@ use app_units::Au; use atomic_refcell::AtomicRef; use compositing_traits::display_list::AxesScrollSensitivity; +use euclid::Rect; +use euclid::default::Size2D as UntypedSize2D; use malloc_size_of_derive::MallocSizeOf; use script::layout_dom::ServoLayoutNode; use script_layout_interface::wrapper_traits::{ @@ -27,7 +29,7 @@ use crate::flow::inline::InlineItem; use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox}; use crate::formatting_contexts::IndependentFormattingContext; use crate::fragment_tree::FragmentTree; -use crate::geom::{LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize}; +use crate::geom::{LogicalVec2, PhysicalRect, PhysicalSize}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; use crate::replaced::ReplacedContents; use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside}; @@ -348,7 +350,7 @@ impl BoxTree { pub fn layout( &self, layout_context: &LayoutContext, - viewport: euclid::Size2D<f32, CSSPixel>, + viewport: UntypedSize2D<Au>, ) -> FragmentTree { let style = layout_context .style_context @@ -358,13 +360,8 @@ impl BoxTree { // FIXME: use the document’s mode: // https://drafts.csswg.org/css-writing-modes/#principal-flow - let physical_containing_block = PhysicalRect::new( - PhysicalPoint::zero(), - PhysicalSize::new( - Au::from_f32_px(viewport.width), - Au::from_f32_px(viewport.height), - ), - ); + let physical_containing_block: Rect<Au, CSSPixel> = + PhysicalSize::from_untyped(viewport).into(); let initial_containing_block = DefiniteContainingBlock { size: LogicalVec2 { inline: physical_containing_block.size.width, diff --git a/components/layout/layout_impl.rs b/components/layout/layout_impl.rs index 8162ed1dd0b..3452c9a6e4c 100644 --- a/components/layout/layout_impl.rs +++ b/components/layout/layout_impl.rs @@ -16,7 +16,7 @@ use base::id::{PipelineId, WebViewId}; use compositing_traits::CrossProcessCompositorApi; use constellation_traits::ScrollState; use embedder_traits::{UntrustedNodeAddress, ViewportDetails}; -use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as UntypedSize2D}; +use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect}; use euclid::{Point2D, Scale, Size2D, Vector2D}; use fnv::FnvHashMap; use fonts::{FontContext, FontContextWebFontMethods}; @@ -150,10 +150,6 @@ pub struct LayoutThread { /// A counter for epoch messages epoch: Cell<Epoch>, - /// The size of the viewport. This may be different from the size of the screen due to viewport - /// constraints. - viewport_size: UntypedSize2D<Au>, - /// Scroll offsets of nodes that scroll. scroll_offsets: RefCell<HashMap<ExternalScrollId, Vector2D<f32, LayoutPixel>>>, @@ -527,10 +523,6 @@ impl LayoutThread { stacking_context_tree: Default::default(), // Epoch starts at 1 because of the initial display list for epoch 0 that we send to WR epoch: Cell::new(Epoch(1)), - viewport_size: Size2D::new( - Au::from_f32_px(config.viewport_details.size.width), - Au::from_f32_px(config.viewport_details.size.height), - ), compositor_api: config.compositor_api, scroll_offsets: Default::default(), stylist: Stylist::new(device, QuirksMode::NoQuirks), @@ -611,7 +603,8 @@ impl LayoutThread { ua_or_user: &ua_or_user_guard, }; - if self.update_device_if_necessary(&reflow_request, &guards) { + let viewport_changed = self.viewport_did_change(reflow_request.viewport_details); + if self.update_device_if_necessary(&reflow_request, viewport_changed, &guards) { if let Some(mut data) = root_element.mutate_data() { data.hint.insert(RestyleHint::recascade_subtree()); } @@ -653,14 +646,15 @@ impl LayoutThread { highlighted_dom_node: reflow_request.highlighted_dom_node, }; - let did_reflow = self.restyle_and_build_trees( + let damage = self.restyle_and_build_trees( &reflow_request, root_element, rayon_pool, &mut layout_context, + viewport_changed, ); - self.build_stacking_context_tree(&reflow_request, did_reflow); + self.build_stacking_context_tree(&reflow_request, damage); self.build_display_list(&reflow_request, &mut layout_context); self.first_reflow.set(false); @@ -683,12 +677,12 @@ impl LayoutThread { fn update_device_if_necessary( &mut self, reflow_request: &ReflowRequest, + viewport_changed: bool, guards: &StylesheetGuards, ) -> bool { let had_used_viewport_units = self.stylist.device().used_viewport_units(); - let viewport_size_changed = self.viewport_did_change(reflow_request.viewport_details); let theme_changed = self.theme_did_change(reflow_request.theme); - if !viewport_size_changed && !theme_changed { + if !viewport_changed && !theme_changed { return false; } self.update_device( @@ -696,7 +690,7 @@ impl LayoutThread { reflow_request.theme, guards, ); - (viewport_size_changed && had_used_viewport_units) || theme_changed + (viewport_changed && had_used_viewport_units) || theme_changed } fn prepare_stylist_for_reflow<'dom>( @@ -749,7 +743,8 @@ impl LayoutThread { root_element: ServoLayoutElement<'_>, rayon_pool: Option<&ThreadPool>, layout_context: &mut LayoutContext<'_>, - ) -> bool { + viewport_changed: bool, + ) -> RestyleDamage { let dirty_root = unsafe { ServoLayoutNode::new(&reflow_request.dirty_root.unwrap()) .as_element() @@ -765,7 +760,7 @@ impl LayoutThread { if !token.should_traverse() { layout_context.style_context.stylist.rule_tree().maybe_gc(); - return false; + return RestyleDamage::empty(); } let dirty_root: ServoLayoutNode = @@ -773,9 +768,9 @@ impl LayoutThread { let root_node = root_element.as_node(); let damage = compute_damage_and_repair_style(layout_context.shared_context(), root_node); - if damage.is_empty() || damage == RestyleDamage::REPAINT { + if !viewport_changed && !damage.contains(RestyleDamage::REBUILD_BOX) { layout_context.style_context.stylist.rule_tree().maybe_gc(); - return false; + return damage; } let mut box_tree = self.box_tree.borrow_mut(); @@ -794,10 +789,7 @@ impl LayoutThread { build_box_tree() }; - let viewport_size = Size2D::new( - self.viewport_size.width.to_f32_px(), - self.viewport_size.height.to_f32_px(), - ); + let viewport_size = self.stylist.device().au_viewport_size(); let run_layout = || { box_tree .as_ref() @@ -836,10 +828,10 @@ impl LayoutThread { // GC the rule tree if some heuristics are met. layout_context.style_context.stylist.rule_tree().maybe_gc(); - true + damage } - fn build_stacking_context_tree(&self, reflow_request: &ReflowRequest, did_reflow: bool) { + fn build_stacking_context_tree(&self, reflow_request: &ReflowRequest, damage: RestyleDamage) { if !reflow_request.reflow_goal.needs_display_list() && !reflow_request.reflow_goal.needs_display() { @@ -848,14 +840,17 @@ impl LayoutThread { let Some(fragment_tree) = &*self.fragment_tree.borrow() else { return; }; - if !did_reflow && self.stacking_context_tree.borrow().is_some() { + if !damage.contains(RestyleDamage::REBUILD_STACKING_CONTEXT) && + self.stacking_context_tree.borrow().is_some() + { return; } - let viewport_size = LayoutSize::from_untyped(Size2D::new( - self.viewport_size.width.to_f32_px(), - self.viewport_size.height.to_f32_px(), - )); + let viewport_size = self.stylist.device().au_viewport_size(); + let viewport_size = LayoutSize::new( + viewport_size.width.to_f32_px(), + viewport_size.height.to_f32_px(), + ); // Build the StackingContextTree. This turns the `FragmentTree` into a // tree of fragments in CSS painting order and also creates all @@ -949,9 +944,6 @@ impl LayoutThread { Au::from_f32_px(viewport_details.size.height), ); - // TODO: eliminate self.viewport_size in favour of using self.device.au_viewport_size() - self.viewport_size = new_viewport_size; - let device = self.stylist.device(); let size_did_change = device.au_viewport_size() != new_viewport_size; let pixel_ratio_did_change = device.device_pixel_ratio().get() != new_pixel_ratio; diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index faf25dc170d..22faa4b1191 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -131,7 +131,9 @@ pub(crate) fn compute_damage_and_repair_style_inner( } } - if propagated_damage == RestyleDamage::REPAINT && original_damage == RestyleDamage::REPAINT { + if !propagated_damage.contains(RestyleDamage::REBUILD_BOX) && + !original_damage.contains(RestyleDamage::REBUILD_BOX) + { node.repair_style(context); } diff --git a/components/script/dom/bindings/reflector.rs b/components/script/dom/bindings/reflector.rs index 0a5afbce487..c888686974e 100644 --- a/components/script/dom/bindings/reflector.rs +++ b/components/script/dom/bindings/reflector.rs @@ -11,7 +11,7 @@ use crate::DomTypes; use crate::dom::bindings::conversions::DerivedFrom; use crate::dom::bindings::root::DomRoot; use crate::dom::globalscope::GlobalScope; -use crate::realms::InRealm; +use crate::realms::{InRealm, enter_realm}; use crate::script_runtime::CanGc; /// Create the reflector for a new DOM object and yield ownership to the @@ -42,7 +42,16 @@ where } pub(crate) trait DomGlobal { + /// Returns the [relevant global] in whatever realm is currently active. + /// + /// [relevant global]: https://html.spec.whatwg.org/multipage/#concept-relevant-global fn global_(&self, realm: InRealm) -> DomRoot<GlobalScope>; + + /// Returns the [relevant global] in the same realm as the callee object. + /// If you know the callee's realm is already the current realm, it is + /// more efficient to call [DomGlobal::global_] instead. + /// + /// [relevant global]: https://html.spec.whatwg.org/multipage/#concept-relevant-global fn global(&self) -> DomRoot<GlobalScope>; } @@ -51,7 +60,8 @@ impl<T: DomGlobalGeneric<crate::DomTypeHolder>> DomGlobal for T { <Self as DomGlobalGeneric<crate::DomTypeHolder>>::global_(self, realm) } fn global(&self) -> DomRoot<GlobalScope> { - <Self as DomGlobalGeneric<crate::DomTypeHolder>>::global(self) + let realm = enter_realm(self); + <Self as DomGlobalGeneric<crate::DomTypeHolder>>::global_(self, InRealm::entered(&realm)) } } diff --git a/components/script/dom/bindings/structuredclone.rs b/components/script/dom/bindings/structuredclone.rs index c23156817cb..73974abe714 100644 --- a/components/script/dom/bindings/structuredclone.rs +++ b/components/script/dom/bindings/structuredclone.rs @@ -10,11 +10,13 @@ use std::os::raw; use std::ptr; use base::id::{ - BlobId, DomExceptionId, DomPointId, Index, MessagePortId, NamespaceIndex, PipelineNamespaceId, + BlobId, DomExceptionId, DomPointId, ImageBitmapId, Index, MessagePortId, NamespaceIndex, + PipelineNamespaceId, }; use constellation_traits::{ BlobImpl, DomException, DomPoint, MessagePortImpl, Serializable as SerializableInterface, - StructuredSerializedData, Transferrable as TransferrableInterface, TransformStreamData, + SerializableImageBitmap, StructuredSerializedData, Transferrable as TransferrableInterface, + TransformStreamData, }; use js::gc::RootedVec; use js::glue::{ @@ -42,6 +44,7 @@ use crate::dom::blob::Blob; use crate::dom::dompoint::DOMPoint; use crate::dom::dompointreadonly::DOMPointReadOnly; use crate::dom::globalscope::GlobalScope; +use crate::dom::imagebitmap::ImageBitmap; use crate::dom::messageport::MessagePort; use crate::dom::readablestream::ReadableStream; use crate::dom::types::{DOMException, TransformStream}; @@ -66,6 +69,7 @@ pub(super) enum StructuredCloneTags { DomException = 0xFFFF8007, WritableStream = 0xFFFF8008, TransformStream = 0xFFFF8009, + ImageBitmap = 0xFFFF800A, Max = 0xFFFFFFFF, } @@ -76,6 +80,7 @@ impl From<SerializableInterface> for StructuredCloneTags { SerializableInterface::DomPointReadOnly => StructuredCloneTags::DomPointReadOnly, SerializableInterface::DomPoint => StructuredCloneTags::DomPoint, SerializableInterface::DomException => StructuredCloneTags::DomException, + SerializableInterface::ImageBitmap => StructuredCloneTags::ImageBitmap, } } } @@ -83,6 +88,7 @@ impl From<SerializableInterface> for StructuredCloneTags { impl From<TransferrableInterface> for StructuredCloneTags { fn from(v: TransferrableInterface) -> Self { match v { + TransferrableInterface::ImageBitmap => StructuredCloneTags::ImageBitmap, TransferrableInterface::MessagePort => StructuredCloneTags::MessagePort, TransferrableInterface::ReadableStream => StructuredCloneTags::ReadableStream, TransferrableInterface::WritableStream => StructuredCloneTags::WritableStream, @@ -104,6 +110,7 @@ fn reader_for_type( SerializableInterface::DomPointReadOnly => read_object::<DOMPointReadOnly>, SerializableInterface::DomPoint => read_object::<DOMPoint>, SerializableInterface::DomException => read_object::<DOMException>, + SerializableInterface::ImageBitmap => read_object::<ImageBitmap>, } } @@ -237,6 +244,7 @@ fn serialize_for_type(val: SerializableInterface) -> SerializeOperation { SerializableInterface::DomPointReadOnly => try_serialize::<DOMPointReadOnly>, SerializableInterface::DomPoint => try_serialize::<DOMPoint>, SerializableInterface::DomException => try_serialize::<DOMException>, + SerializableInterface::ImageBitmap => try_serialize::<ImageBitmap>, } } @@ -264,6 +272,7 @@ fn receiver_for_type( ) -> fn(&GlobalScope, &mut StructuredDataReader<'_>, u64, RawMutableHandleObject) -> Result<(), ()> { match val { + TransferrableInterface::ImageBitmap => receive_object::<ImageBitmap>, TransferrableInterface::MessagePort => receive_object::<MessagePort>, TransferrableInterface::ReadableStream => receive_object::<ReadableStream>, TransferrableInterface::WritableStream => receive_object::<WritableStream>, @@ -390,6 +399,7 @@ type TransferOperation = unsafe fn( fn transfer_for_type(val: TransferrableInterface) -> TransferOperation { match val { + TransferrableInterface::ImageBitmap => try_transfer::<ImageBitmap>, TransferrableInterface::MessagePort => try_transfer::<MessagePort>, TransferrableInterface::ReadableStream => try_transfer::<ReadableStream>, TransferrableInterface::WritableStream => try_transfer::<WritableStream>, @@ -439,6 +449,7 @@ unsafe fn can_transfer_for_type( root_from_object::<T>(*obj, cx).map(|o| Transferable::can_transfer(&*o)) } match transferable { + TransferrableInterface::ImageBitmap => can_transfer::<ImageBitmap>(obj, cx), TransferrableInterface::MessagePort => can_transfer::<MessagePort>(obj, cx), TransferrableInterface::ReadableStream => can_transfer::<ReadableStream>(obj, cx), TransferrableInterface::WritableStream => can_transfer::<WritableStream>(obj, cx), @@ -527,6 +538,10 @@ pub(crate) struct StructuredDataReader<'a> { pub(crate) points: Option<HashMap<DomPointId, DomPoint>>, /// A map of serialized exceptions. pub(crate) exceptions: Option<HashMap<DomExceptionId, DomException>>, + // A map of serialized image bitmaps. + pub(crate) image_bitmaps: Option<HashMap<ImageBitmapId, SerializableImageBitmap>>, + /// A map of transferred image bitmaps. + pub(crate) transferred_image_bitmaps: Option<HashMap<ImageBitmapId, SerializableImageBitmap>>, } /// A data holder for transferred and serialized objects. @@ -545,6 +560,10 @@ pub(crate) struct StructuredDataWriter { pub(crate) exceptions: Option<HashMap<DomExceptionId, DomException>>, /// Serialized blobs. pub(crate) blobs: Option<HashMap<BlobId, BlobImpl>>, + /// Serialized image bitmaps. + pub(crate) image_bitmaps: Option<HashMap<ImageBitmapId, SerializableImageBitmap>>, + /// Transferred image bitmaps. + pub(crate) transferred_image_bitmaps: Option<HashMap<ImageBitmapId, SerializableImageBitmap>>, } /// Writes a structured clone. Returns a `DataClone` error if that fails. @@ -599,6 +618,8 @@ pub(crate) fn write( points: sc_writer.points.take(), exceptions: sc_writer.exceptions.take(), blobs: sc_writer.blobs.take(), + image_bitmaps: sc_writer.image_bitmaps.take(), + transferred_image_bitmaps: sc_writer.transferred_image_bitmaps.take(), }; Ok(data) @@ -623,6 +644,8 @@ pub(crate) fn read( points: data.points.take(), exceptions: data.exceptions.take(), errors: DOMErrorRecord { message: None }, + image_bitmaps: data.image_bitmaps.take(), + transferred_image_bitmaps: data.transferred_image_bitmaps.take(), }; let sc_reader_ptr = &mut sc_reader as *mut _; unsafe { diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 55db2e4d248..ec2ed70dd15 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -2992,8 +2992,7 @@ impl GlobalScope { if let Some(snapshot) = canvas.get_image_data() { let size = snapshot.size().cast(); - let image_bitmap = - ImageBitmap::new(self, size.width, size.height, can_gc).unwrap(); + let image_bitmap = ImageBitmap::new(self, size.width, size.height, can_gc); image_bitmap.set_bitmap_data(snapshot.to_vec()); image_bitmap.set_origin_clean(canvas.origin_is_clean()); p.resolve_native(&(image_bitmap), can_gc); @@ -3009,8 +3008,7 @@ impl GlobalScope { if let Some(snapshot) = canvas.get_image_data() { let size = snapshot.size().cast(); - let image_bitmap = - ImageBitmap::new(self, size.width, size.height, can_gc).unwrap(); + let image_bitmap = ImageBitmap::new(self, size.width, size.height, can_gc); image_bitmap.set_bitmap_data(snapshot.to_vec()); image_bitmap.set_origin_clean(canvas.origin_is_clean()); p.resolve_native(&(image_bitmap), can_gc); diff --git a/components/script/dom/htmlareaelement.rs b/components/script/dom/htmlareaelement.rs index a9d94cbd7b2..535d296a29f 100644 --- a/components/script/dom/htmlareaelement.rs +++ b/components/script/dom/htmlareaelement.rs @@ -46,6 +46,8 @@ pub enum Area { bottom_right: (f32, f32), }, Polygon { + /// Stored as a flat array of coordinates + /// e.g. [x1, y1, x2, y2, x3, y3] for a triangle points: Vec<f32>, }, } @@ -203,8 +205,28 @@ impl Area { p.y >= top_left.1 }, - //TODO polygon hit_test - _ => false, + Area::Polygon { ref points } => { + // Ray-casting algorithm to determine if point is inside polygon + // https://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm + let mut inside = false; + + debug_assert!(points.len() % 2 == 0); + let vertices = points.len() / 2; + + for i in 0..vertices { + let next_i = if i + 1 == vertices { 0 } else { i + 1 }; + + let xi = points[2 * i]; + let yi = points[2 * i + 1]; + let xj = points[2 * next_i]; + let yj = points[2 * next_i + 1]; + + if (yi > p.y) != (yj > p.y) && p.x < (xj - xi) * (p.y - yi) / (yj - yi) + xi { + inside = !inside; + } + } + inside + }, } } diff --git a/components/script/dom/imagebitmap.rs b/components/script/dom/imagebitmap.rs index ef6538e7451..97a1a54bba7 100644 --- a/components/script/dom/imagebitmap.rs +++ b/components/script/dom/imagebitmap.rs @@ -3,15 +3,20 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::Cell; +use std::collections::HashMap; use std::vec::Vec; +use base::id::{ImageBitmapId, ImageBitmapIndex}; +use constellation_traits::SerializableImageBitmap; use dom_struct::dom_struct; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::ImageBitmapMethods; -use crate::dom::bindings::error::Fallible; use crate::dom::bindings::reflector::{Reflector, reflect_dom_object}; use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::serializable::Serializable; +use crate::dom::bindings::structuredclone::StructuredData; +use crate::dom::bindings::transferable::Transferable; use crate::dom::globalscope::GlobalScope; use crate::script_runtime::CanGc; @@ -29,33 +34,36 @@ pub(crate) struct ImageBitmap { } impl ImageBitmap { - fn new_inherited(width_arg: u32, height_arg: u32) -> ImageBitmap { + fn new_inherited(width: u32, height: u32) -> ImageBitmap { ImageBitmap { reflector_: Reflector::new(), - width: width_arg, - height: height_arg, + width, + height, bitmap_data: DomRefCell::new(Some(vec![])), origin_clean: Cell::new(true), } } - #[allow(dead_code)] pub(crate) fn new( global: &GlobalScope, width: u32, height: u32, can_gc: CanGc, - ) -> Fallible<DomRoot<ImageBitmap>> { + ) -> DomRoot<ImageBitmap> { //assigning to a variable the return object of new_inherited let imagebitmap = Box::new(ImageBitmap::new_inherited(width, height)); - Ok(reflect_dom_object(imagebitmap, global, can_gc)) + reflect_dom_object(imagebitmap, global, can_gc) } pub(crate) fn set_bitmap_data(&self, data: Vec<u8>) { *self.bitmap_data.borrow_mut() = Some(data); } + pub(crate) fn origin_is_clean(&self) -> bool { + self.origin_clean.get() + } + pub(crate) fn set_origin_clean(&self, origin_is_clean: bool) { self.origin_clean.set(origin_is_clean); } @@ -67,6 +75,117 @@ impl ImageBitmap { } } +impl Serializable for ImageBitmap { + type Index = ImageBitmapIndex; + type Data = SerializableImageBitmap; + + /// <https://html.spec.whatwg.org/multipage/#the-imagebitmap-interface:serialization-steps> + fn serialize(&self) -> Result<(ImageBitmapId, Self::Data), ()> { + // Step 1. If value's origin-clean flag is not set, then throw a "DataCloneError" DOMException. + if !self.origin_is_clean() { + return Err(()); + } + + // If value has a [[Detached]] internal slot whose value is true, + // then throw a "DataCloneError" DOMException. + if self.is_detached() { + return Err(()); + } + + // Step 2. Set serialized.[[BitmapData]] to a copy of value's bitmap data. + let serialized = SerializableImageBitmap { + width: self.width, + height: self.height, + bitmap_data: self.bitmap_data.borrow().clone().unwrap(), + }; + + Ok((ImageBitmapId::new(), serialized)) + } + + /// <https://html.spec.whatwg.org/multipage/#the-imagebitmap-interface:deserialization-steps> + fn deserialize( + owner: &GlobalScope, + serialized: Self::Data, + can_gc: CanGc, + ) -> Result<DomRoot<Self>, ()> { + let image_bitmap = ImageBitmap::new(owner, serialized.width, serialized.height, can_gc); + + // Step 1. Set value's bitmap data to serialized.[[BitmapData]]. + image_bitmap.set_bitmap_data(serialized.bitmap_data); + + Ok(image_bitmap) + } + + fn serialized_storage<'a>( + reader: StructuredData<'a, '_>, + ) -> &'a mut Option<HashMap<ImageBitmapId, Self::Data>> { + match reader { + StructuredData::Reader(r) => &mut r.image_bitmaps, + StructuredData::Writer(w) => &mut w.image_bitmaps, + } + } +} + +impl Transferable for ImageBitmap { + type Index = ImageBitmapIndex; + type Data = SerializableImageBitmap; + + fn can_transfer(&self) -> bool { + if !self.origin_is_clean() || self.is_detached() { + return false; + } + true + } + + /// <https://html.spec.whatwg.org/multipage/#the-imagebitmap-interface:transfer-steps> + fn transfer(&self) -> Result<(ImageBitmapId, SerializableImageBitmap), ()> { + // Step 1. If value's origin-clean flag is not set, then throw a "DataCloneError" DOMException. + if !self.origin_is_clean() { + return Err(()); + } + + // If value has a [[Detached]] internal slot whose value is true, + // then throw a "DataCloneError" DOMException. + if self.is_detached() { + return Err(()); + } + + // Step 2. Set dataHolder.[[BitmapData]] to value's bitmap data. + // Step 3. Unset value's bitmap data. + let serialized = SerializableImageBitmap { + width: self.width, + height: self.height, + bitmap_data: self.bitmap_data.borrow_mut().take().unwrap(), + }; + + Ok((ImageBitmapId::new(), serialized)) + } + + /// <https://html.spec.whatwg.org/multipage/#the-imagebitmap-interface:transfer-receiving-steps> + fn transfer_receive( + owner: &GlobalScope, + _: ImageBitmapId, + serialized: SerializableImageBitmap, + ) -> Result<DomRoot<Self>, ()> { + let image_bitmap = + ImageBitmap::new(owner, serialized.width, serialized.height, CanGc::note()); + + // Step 1. Set value's bitmap data to serialized.[[BitmapData]]. + image_bitmap.set_bitmap_data(serialized.bitmap_data); + + Ok(image_bitmap) + } + + fn serialized_storage<'a>( + data: StructuredData<'a, '_>, + ) -> &'a mut Option<HashMap<ImageBitmapId, Self::Data>> { + match data { + StructuredData::Reader(r) => &mut r.transferred_image_bitmaps, + StructuredData::Writer(w) => &mut w.transferred_image_bitmaps, + } + } +} + impl ImageBitmapMethods<crate::DomTypeHolder> for ImageBitmap { /// <https://html.spec.whatwg.org/multipage/#dom-imagebitmap-height> fn Height(&self) -> u32 { diff --git a/components/script/dom/urlpattern.rs b/components/script/dom/urlpattern.rs index c811d3a9a70..63665f6df0b 100644 --- a/components/script/dom/urlpattern.rs +++ b/components/script/dom/urlpattern.rs @@ -4,6 +4,7 @@ use dom_struct::dom_struct; use js::rust::HandleObject; +use script_bindings::codegen::GenericBindings::URLPatternBinding::URLPatternResult; use script_bindings::codegen::GenericUnionTypes::USVStringOrURLPatternInit; use script_bindings::error::{Error, Fallible}; use script_bindings::reflector::Reflector; @@ -46,7 +47,7 @@ impl URLPattern { ) -> Fallible<DomRoot<URLPattern>> { // The section below converts from servos types to the types used in the urlpattern crate let base_url = base_url.map(|usv_string| usv_string.0); - let input = bindings_to_third_party::map_urlpattern_input(input, base_url.clone()); + let input = bindings_to_third_party::map_urlpattern_input(input); let options = urlpattern::UrlPatternOptions { ignore_case: options.ignoreCase, }; @@ -94,6 +95,50 @@ impl URLPatternMethods<crate::DomTypeHolder> for URLPattern { URLPattern::initialize(global, proto, input, None, options, can_gc) } + /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-test> + fn Test( + &self, + input: USVStringOrURLPatternInit, + base_url: Option<USVString>, + ) -> Fallible<bool> { + let input = bindings_to_third_party::map_urlpattern_input(input); + let inputs = urlpattern::quirks::process_match_input(input, base_url.as_deref()) + .map_err(|error| Error::Type(format!("{error}")))?; + let Some((match_input, _)) = inputs else { + return Ok(false); + }; + + self.associated_url_pattern + .test(match_input) + .map_err(|error| Error::Type(format!("{error}"))) + } + + /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-exec> + fn Exec( + &self, + input: USVStringOrURLPatternInit, + base_url: Option<USVString>, + ) -> Fallible<Option<URLPatternResult>> { + let input = bindings_to_third_party::map_urlpattern_input(input); + let inputs = urlpattern::quirks::process_match_input(input, base_url.as_deref()) + .map_err(|error| Error::Type(format!("{error}")))?; + let Some((match_input, inputs)) = inputs else { + return Ok(None); + }; + + let result = self + .associated_url_pattern + .exec(match_input) + .map_err(|error| Error::Type(format!("{error}")))?; + let Some(result) = result else { + return Ok(None); + }; + + Ok(Some(third_party_to_bindings::map_urlpattern_result( + result, inputs, + ))) + } + /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-protocol> fn Protocol(&self) -> USVString { // Step 1. Return this’s associated URL pattern’s protocol component’s pattern string. @@ -151,54 +196,115 @@ impl URLPatternMethods<crate::DomTypeHolder> for URLPattern { } mod bindings_to_third_party { + use script_bindings::codegen::GenericBindings::URLPatternBinding::URLPatternInit; + use crate::dom::urlpattern::USVStringOrURLPatternInit; + fn map_urlpatterninit(pattern_init: URLPatternInit) -> urlpattern::quirks::UrlPatternInit { + urlpattern::quirks::UrlPatternInit { + protocol: pattern_init.protocol.map(|protocol| protocol.0), + username: pattern_init.username.map(|username| username.0), + password: pattern_init.password.map(|password| password.0), + hostname: pattern_init.hostname.map(|hostname| hostname.0), + port: pattern_init.port.map(|hash| hash.0), + pathname: pattern_init + .pathname + .as_ref() + .map(|usv_string| usv_string.to_string()), + search: pattern_init.search.map(|search| search.0), + hash: pattern_init.hash.map(|hash| hash.0), + base_url: pattern_init.baseURL.map(|base_url| base_url.0), + } + } + pub(super) fn map_urlpattern_input( input: USVStringOrURLPatternInit, - base_url: Option<String>, ) -> urlpattern::quirks::StringOrInit { match input { USVStringOrURLPatternInit::USVString(usv_string) => { urlpattern::quirks::StringOrInit::String(usv_string.0) }, USVStringOrURLPatternInit::URLPatternInit(pattern_init) => { - let pattern_init = urlpattern::quirks::UrlPatternInit { - protocol: pattern_init - .protocol - .as_ref() - .map(|usv_string| usv_string.to_string()), - username: pattern_init - .username - .as_ref() - .map(|usv_string| usv_string.to_string()), - password: pattern_init - .password - .as_ref() - .map(|usv_string| usv_string.to_string()), - hostname: pattern_init - .hostname - .as_ref() - .map(|usv_string| usv_string.to_string()), - port: pattern_init - .port - .as_ref() - .map(|usv_string| usv_string.to_string()), - pathname: pattern_init - .pathname - .as_ref() - .map(|usv_string| usv_string.to_string()), - search: pattern_init - .search - .as_ref() - .map(|usv_string| usv_string.to_string()), - hash: pattern_init - .hash - .as_ref() - .map(|usv_string| usv_string.to_string()), - base_url, - }; - urlpattern::quirks::StringOrInit::Init(pattern_init) + urlpattern::quirks::StringOrInit::Init(map_urlpatterninit(pattern_init)) + }, + } + } +} + +mod third_party_to_bindings { + use script_bindings::codegen::GenericBindings::URLPatternBinding::{ + URLPatternComponentResult, URLPatternInit, URLPatternResult, + }; + use script_bindings::codegen::GenericUnionTypes::USVStringOrUndefined; + use script_bindings::record::Record; + use script_bindings::str::USVString; + + use crate::dom::bindings::codegen::UnionTypes::USVStringOrURLPatternInit; + + // FIXME: For some reason codegen puts a lot of options into these types that don't make sense + + fn map_component_result( + component_result: urlpattern::UrlPatternComponentResult, + ) -> URLPatternComponentResult { + let mut groups = Record::new(); + for (key, value) in component_result.groups.iter() { + let value = match value { + Some(value) => USVStringOrUndefined::USVString(USVString(value.to_owned())), + None => USVStringOrUndefined::Undefined(()), + }; + + groups.insert(USVString(key.to_owned()), value); + } + + URLPatternComponentResult { + input: Some(component_result.input.into()), + groups: Some(groups), + } + } + + fn map_urlpatterninit(pattern_init: urlpattern::quirks::UrlPatternInit) -> URLPatternInit { + URLPatternInit { + baseURL: pattern_init.base_url.map(USVString), + protocol: pattern_init.protocol.map(USVString), + username: pattern_init.username.map(USVString), + password: pattern_init.password.map(USVString), + hostname: pattern_init.hostname.map(USVString), + port: pattern_init.port.map(USVString), + pathname: pattern_init.pathname.map(USVString), + search: pattern_init.search.map(USVString), + hash: pattern_init.hash.map(USVString), + } + } + + pub(super) fn map_urlpattern_result( + result: urlpattern::UrlPatternResult, + (string_or_init, base_url): urlpattern::quirks::Inputs, + ) -> URLPatternResult { + let string_or_init = match string_or_init { + urlpattern::quirks::StringOrInit::String(string) => { + USVStringOrURLPatternInit::USVString(USVString(string)) + }, + urlpattern::quirks::StringOrInit::Init(pattern_init) => { + USVStringOrURLPatternInit::URLPatternInit(map_urlpatterninit(pattern_init)) }, + }; + + let mut inputs = vec![string_or_init]; + + if let Some(base_url) = base_url { + inputs.push(USVStringOrURLPatternInit::USVString(USVString(base_url))); + } + + URLPatternResult { + inputs: Some(inputs), + protocol: Some(map_component_result(result.protocol)), + username: Some(map_component_result(result.username)), + password: Some(map_component_result(result.password)), + hostname: Some(map_component_result(result.hostname)), + port: Some(map_component_result(result.port)), + pathname: Some(map_component_result(result.pathname)), + search: Some(map_component_result(result.search)), + hash: Some(map_component_result(result.hash)), } } } diff --git a/components/script_bindings/codegen/CodegenRust.py b/components/script_bindings/codegen/CodegenRust.py index 20d6d30681e..37d62ff2e1b 100644 --- a/components/script_bindings/codegen/CodegenRust.py +++ b/components/script_bindings/codegen/CodegenRust.py @@ -39,6 +39,7 @@ from WebIDL import ( from Configuration import ( Configuration, Descriptor, + DescriptorProvider, MakeNativeName, MemberIsLegacyUnforgeable, getModuleFromObject, @@ -617,16 +618,16 @@ class JSToNativeConversionInfo(): def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, - isDefinitelyObject=False, - isMember=False, + isDefinitelyObject: bool = False, + isMember: bool | str = False, isArgument=False, isAutoRooted=False, invalidEnumValueFatal=True, defaultValue=None, exceptionCode=None, - allowTreatNonObjectAsNull=False, + allowTreatNonObjectAsNull: bool = False, isCallbackReturnValue=False, - sourceDescription="value"): + sourceDescription="value") -> JSToNativeConversionInfo: """ Get a template for converting a JS value to a native object based on the given type and descriptor. If failureCode is given, then we're actually @@ -5071,7 +5072,7 @@ class CGConstant(CGThing): return f"pub const {name}: {const_type} = {value};\n" -def getUnionTypeTemplateVars(type, descriptorProvider): +def getUnionTypeTemplateVars(type, descriptorProvider: DescriptorProvider): if type.isGeckoInterface(): name = type.inner.identifier.name typeName = descriptorProvider.getDescriptor(name).returnType @@ -5108,6 +5109,12 @@ def getUnionTypeTemplateVars(type, descriptorProvider): elif type.isCallback(): name = type.name typeName = f"{name}<D>" + elif type.isUndefined(): + return { + "name": type.name, + "typeName": "()", + "jsConversion": CGGeneric("if value.is_undefined() { Ok(Some(())) } else { Ok(None) }") + } else: raise TypeError(f"Can't handle {type} in unions yet") @@ -5173,7 +5180,7 @@ impl{self.generic} Clone for {self.type}{self.genericSuffix} {{ def manualImpl(self, t, templateVars): if t == "Clone": return self.manualImplClone(templateVars) - raise f"Don't know how to impl {t} for union" + raise ValueError(f"Don't know how to impl {t} for union") def define(self): def getTypeWrapper(t): @@ -5330,31 +5337,44 @@ class CGUnionConversionStruct(CGThing): stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()] numericTypes = [t for t in memberTypes if t.isNumeric()] booleanTypes = [t for t in memberTypes if t.isBoolean()] - if stringTypes or numericTypes or booleanTypes: + numUndefinedVariants = [t.isUndefined() for t in memberTypes].count(True) + if stringTypes or numericTypes or booleanTypes or numUndefinedVariants != 0: assert len(stringTypes) <= 1 assert len(numericTypes) <= 1 assert len(booleanTypes) <= 1 + assert numUndefinedVariants <= 1 def getStringOrPrimitiveConversion(memberType): typename = get_name(memberType) return CGGeneric(get_match(typename)) + other = [] stringConversion = list(map(getStringOrPrimitiveConversion, stringTypes)) numericConversion = list(map(getStringOrPrimitiveConversion, numericTypes)) booleanConversion = list(map(getStringOrPrimitiveConversion, booleanTypes)) + undefinedConversion = CGGeneric("return Ok(ConversionResult::Success(Self::Undefined(())));") + if stringConversion: if booleanConversion: other.append(CGIfWrapper("value.get().is_boolean()", booleanConversion[0])) if numericConversion: other.append(CGIfWrapper("value.get().is_number()", numericConversion[0])) + if numUndefinedVariants != 0: + other.append(CGIfWrapper("value.get().is_undefined()", undefinedConversion)) other.append(stringConversion[0]) elif numericConversion: if booleanConversion: other.append(CGIfWrapper("value.get().is_boolean()", booleanConversion[0])) + if numUndefinedVariants != 0: + other.append(CGIfWrapper("value.get().is_undefined()", undefinedConversion)) other.append(numericConversion[0]) - else: - assert booleanConversion + elif booleanConversion: + if numUndefinedVariants != 0: + other.append(CGIfWrapper("value.get().is_undefined()", undefinedConversion)) other.append(booleanConversion[0]) + else: + assert numUndefinedVariants != 0 + other.append(undefinedConversion) conversions.append(CGList(other, "\n\n")) conversions.append(CGGeneric( f'Ok(ConversionResult::Failure("argument could not be converted to any of: {", ".join(names)}".into()))' @@ -5376,6 +5396,10 @@ class CGUnionConversionStruct(CGThing): post="\n}") def try_method(self, t): + if t.isUndefined(): + # undefined does not require a conversion method, so we don't generate one + return CGGeneric("") + templateVars = getUnionTypeTemplateVars(t, self.descriptorProvider) actualType = templateVars["typeName"] if type_needs_tracing(t): @@ -7151,7 +7175,7 @@ impl{self.generic} Clone for {self.makeClassName(self.dictionary)}{self.genericS def manualImpl(self, t): if t == "Clone": return self.manualImplClone() - raise f"Don't know how to impl {t} for dicts." + raise ValueError(f"Don't know how to impl {t} for dicts.") def struct(self): d = self.dictionary diff --git a/components/script_bindings/interface.rs b/components/script_bindings/interface.rs index 08ee0a4f420..e185cfe9cfd 100644 --- a/components/script_bindings/interface.rs +++ b/components/script_bindings/interface.rs @@ -16,11 +16,11 @@ use js::jsapi::{ GetFunctionRealm, GetNonCCWObjectGlobal, GetRealmGlobalOrNull, GetWellKnownSymbol, HandleObject as RawHandleObject, IsSharableCompartment, IsSystemCompartment, JS_AtomizeAndPinString, JS_GetFunctionObject, JS_GetProperty, JS_IterateCompartments, - JS_NewFunction, JS_NewGlobalObject, JS_NewObject, JS_NewPlainObject, JS_NewStringCopyN, - JS_SetReservedSlot, JS_WrapObject, JSAutoRealm, JSClass, JSClassOps, JSContext, - JSFUN_CONSTRUCTOR, JSFunctionSpec, JSObject, JSPROP_PERMANENT, JSPROP_READONLY, - JSPROP_RESOLVING, JSPropertySpec, JSString, JSTracer, ObjectOps, OnNewGlobalHookOption, - SymbolCode, TrueHandleValue, Value, jsid, + JS_NewFunction, JS_NewGlobalObject, JS_NewObject, JS_NewStringCopyN, JS_SetReservedSlot, + JS_WrapObject, JSAutoRealm, JSClass, JSClassOps, JSContext, JSFUN_CONSTRUCTOR, JSFunctionSpec, + JSObject, JSPROP_ENUMERATE, JSPROP_PERMANENT, JSPROP_READONLY, JSPROP_RESOLVING, + JSPropertySpec, JSString, JSTracer, ObjectOps, OnNewGlobalHookOption, SymbolCode, + TrueHandleValue, Value, jsid, }; use js::jsval::{JSVal, NullValue, PrivateValue}; use js::rust::wrappers::{ @@ -473,7 +473,11 @@ fn create_unscopable_object(cx: SafeJSContext, names: &[&CStr], mut rval: Mutabl assert!(!names.is_empty()); assert!(rval.is_null()); unsafe { - rval.set(JS_NewPlainObject(*cx)); + rval.set(JS_NewObjectWithGivenProto( + *cx, + ptr::null(), + HandleObject::null(), + )); assert!(!rval.is_null()); for &name in names { assert!(JS_DefineProperty( @@ -481,7 +485,7 @@ fn create_unscopable_object(cx: SafeJSContext, names: &[&CStr], mut rval: Mutabl rval.handle(), name.as_ptr(), HandleValue::from_raw(TrueHandleValue), - JSPROP_READONLY as u32, + JSPROP_ENUMERATE as u32, )); } } diff --git a/components/script_bindings/record.rs b/components/script_bindings/record.rs index 2668a84f42c..d469faefaf2 100644 --- a/components/script_bindings/record.rs +++ b/components/script_bindings/record.rs @@ -7,7 +7,7 @@ use std::cmp::Eq; use std::hash::Hash; use std::marker::Sized; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use indexmap::IndexMap; use js::conversions::{ConversionResult, FromJSValConvertible, ToJSValConvertible}; @@ -94,11 +94,17 @@ impl<K: RecordKey, V> Record<K, V> { impl<K: RecordKey, V> Deref for Record<K, V> { type Target = IndexMap<K, V>; - fn deref(&self) -> &IndexMap<K, V> { + fn deref(&self) -> &Self::Target { &self.map } } +impl<K: RecordKey, V> DerefMut for Record<K, V> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.map + } +} + impl<K, V, C> FromJSValConvertible for Record<K, V> where K: RecordKey, diff --git a/components/script_bindings/reflector.rs b/components/script_bindings/reflector.rs index 6b6ae03cb69..4b91b0536fc 100644 --- a/components/script_bindings/reflector.rs +++ b/components/script_bindings/reflector.rs @@ -8,7 +8,7 @@ use malloc_size_of_derive::MallocSizeOf; use crate::interfaces::GlobalScopeHelpers; use crate::iterable::{Iterable, IterableIterator}; -use crate::realms::{AlreadyInRealm, InRealm}; +use crate::realms::InRealm; use crate::root::{Dom, DomRoot, Root}; use crate::script_runtime::{CanGc, JSContext}; use crate::{DomTypes, JSTraceable}; @@ -108,17 +108,6 @@ pub trait DomGlobalGeneric<D: DomTypes>: DomObject { { D::GlobalScope::from_reflector(self, realm) } - - /// Returns the [`GlobalScope`] of the realm that the [`DomObject`] was created in. If this - /// object is a `Node`, this will be different from it's owning `Document` if adopted by. For - /// `Node`s it's almost always better to use `NodeTraits::owning_global`. - fn global(&self) -> DomRoot<D::GlobalScope> - where - Self: Sized, - { - let realm = AlreadyInRealm::assert_for_cx(D::GlobalScope::get_cx()); - D::GlobalScope::from_reflector(self, InRealm::already(&realm)) - } } impl<D: DomTypes, T: DomObject> DomGlobalGeneric<D> for T {} diff --git a/components/script_bindings/webidls/ImageBitmap.webidl b/components/script_bindings/webidls/ImageBitmap.webidl index aaa35c67995..b9b811eb9bd 100644 --- a/components/script_bindings/webidls/ImageBitmap.webidl +++ b/components/script_bindings/webidls/ImageBitmap.webidl @@ -9,8 +9,7 @@ * You are granted a license to use, reproduce and create derivative works of this document. */ -//[Exposed=(Window,Worker), Serializable, Transferable] -[Exposed=(Window,Worker), Pref="dom_imagebitmap_enabled"] +[Exposed=(Window,Worker), Serializable, Transferable, Pref="dom_imagebitmap_enabled"] interface ImageBitmap { readonly attribute unsigned long width; readonly attribute unsigned long height; diff --git a/components/script_bindings/webidls/TestBinding.webidl b/components/script_bindings/webidls/TestBinding.webidl index f2c45313228..33ba2f41fbb 100644 --- a/components/script_bindings/webidls/TestBinding.webidl +++ b/components/script_bindings/webidls/TestBinding.webidl @@ -627,3 +627,6 @@ dictionary NotUsedAnyWhereElse {}; dictionary RecordFieldWithUnionInside { record<USVString, (USVString or NotUsedAnyWhereElse)> recordWithUnionField; }; + +// https://github.com/servo/servo/issues/28679 +typedef (USVString or undefined) UnionWithUndefinedVariant; diff --git a/components/script_bindings/webidls/URLPattern.webidl b/components/script_bindings/webidls/URLPattern.webidl index f61b65702bc..a29cb650c23 100644 --- a/components/script_bindings/webidls/URLPattern.webidl +++ b/components/script_bindings/webidls/URLPattern.webidl @@ -6,14 +6,14 @@ typedef (USVString or URLPatternInit) URLPatternInput; -[Exposed=(Window,Worker), Pref="dom_urlpattern_enabled"] +[Exposed=(Window,Worker)] interface URLPattern { [Throws] constructor(URLPatternInput input, USVString baseURL, optional URLPatternOptions options = {}); [Throws] constructor(optional URLPatternInput input = {}, optional URLPatternOptions options = {}); - // [Throws] boolean test(optional URLPatternInput input = {}, optional USVString baseURL); + [Throws] boolean test(optional URLPatternInput input = {}, optional USVString baseURL); - // [Throws] URLPatternResult? exec(optional URLPatternInput input = {}, optional USVString baseURL); + [Throws] URLPatternResult? exec(optional URLPatternInput input = {}, optional USVString baseURL); readonly attribute USVString protocol; readonly attribute USVString username; @@ -43,20 +43,20 @@ dictionary URLPatternOptions { boolean ignoreCase = false; }; -// dictionary URLPatternResult { -// sequence<URLPatternInput> inputs; - -// URLPatternComponentResult protocol; -// URLPatternComponentResult username; -// URLPatternComponentResult password; -// URLPatternComponentResult hostname; -// URLPatternComponentResult port; -// URLPatternComponentResult pathname; -// URLPatternComponentResult search; -// URLPatternComponentResult hash; -// }; - -// dictionary URLPatternComponentResult { -// USVString input; -// record<USVString, (USVString or undefined)> groups; -// }; +dictionary URLPatternResult { + sequence<URLPatternInput> inputs; + + URLPatternComponentResult protocol; + URLPatternComponentResult username; + URLPatternComponentResult password; + URLPatternComponentResult hostname; + URLPatternComponentResult port; + URLPatternComponentResult pathname; + URLPatternComponentResult search; + URLPatternComponentResult hash; +}; + +dictionary URLPatternComponentResult { + USVString input; + record<USVString, (USVString or undefined)> groups; +}; diff --git a/components/shared/base/id.rs b/components/shared/base/id.rs index b6ad1b3de9b..a091311d237 100644 --- a/components/shared/base/id.rs +++ b/components/shared/base/id.rs @@ -371,6 +371,8 @@ namespace_id! {DomExceptionId, DomExceptionIndex, "DomException"} namespace_id! {HistoryStateId, HistoryStateIndex, "HistoryState"} +namespace_id! {ImageBitmapId, ImageBitmapIndex, "ImageBitmap"} + // We provide ids just for unit testing. pub const TEST_NAMESPACE: PipelineNamespaceId = PipelineNamespaceId(1234); #[allow(unsafe_code)] diff --git a/components/shared/constellation/structured_data/mod.rs b/components/shared/constellation/structured_data/mod.rs index 81e3849e476..3ea0d78eaf3 100644 --- a/components/shared/constellation/structured_data/mod.rs +++ b/components/shared/constellation/structured_data/mod.rs @@ -10,7 +10,7 @@ mod transferable; use std::collections::HashMap; -use base::id::{BlobId, DomExceptionId, DomPointId, MessagePortId}; +use base::id::{BlobId, DomExceptionId, DomPointId, ImageBitmapId, MessagePortId}; use log::warn; use malloc_size_of_derive::MallocSizeOf; use serde::{Deserialize, Serialize}; @@ -34,6 +34,10 @@ pub struct StructuredSerializedData { pub ports: Option<HashMap<MessagePortId, MessagePortImpl>>, /// Transform streams transferred objects. pub transform_streams: Option<HashMap<MessagePortId, TransformStreamData>>, + /// Serialized image bitmap objects. + pub image_bitmaps: Option<HashMap<ImageBitmapId, SerializableImageBitmap>>, + /// Transferred image bitmap objects. + pub transferred_image_bitmaps: Option<HashMap<ImageBitmapId, SerializableImageBitmap>>, } impl StructuredSerializedData { @@ -42,6 +46,7 @@ impl StructuredSerializedData { field.as_ref().is_some_and(|h| h.is_empty()) } match val { + Transferrable::ImageBitmap => is_field_empty(&self.transferred_image_bitmaps), Transferrable::MessagePort => is_field_empty(&self.ports), Transferrable::ReadableStream => is_field_empty(&self.ports), Transferrable::WritableStream => is_field_empty(&self.ports), diff --git a/components/shared/constellation/structured_data/serializable.rs b/components/shared/constellation/structured_data/serializable.rs index 22370087665..cbb932c52ec 100644 --- a/components/shared/constellation/structured_data/serializable.rs +++ b/components/shared/constellation/structured_data/serializable.rs @@ -11,7 +11,7 @@ use std::cell::RefCell; use std::collections::HashMap; use std::path::PathBuf; -use base::id::{BlobId, DomExceptionId, DomPointId}; +use base::id::{BlobId, DomExceptionId, DomPointId, ImageBitmapId}; use malloc_size_of_derive::MallocSizeOf; use net_traits::filemanager_thread::RelativePos; use serde::{Deserialize, Serialize}; @@ -47,6 +47,8 @@ pub enum Serializable { DomPointReadOnly, /// The `DOMException` interface. DomException, + /// The `ImageBitmap` interface. + ImageBitmap, } impl Serializable { @@ -62,6 +64,9 @@ impl Serializable { Serializable::DomException => { StructuredSerializedData::clone_all_of_type::<DomException> }, + Serializable::ImageBitmap => { + StructuredSerializedData::clone_all_of_type::<SerializableImageBitmap> + }, } } } @@ -312,3 +317,31 @@ impl BroadcastClone for DomException { Some(self.clone()) } } + +#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] +/// A serializable version of the ImageBitmap interface. +pub struct SerializableImageBitmap { + pub width: u32, + pub height: u32, + pub bitmap_data: Vec<u8>, +} + +impl BroadcastClone for SerializableImageBitmap { + type Id = ImageBitmapId; + + fn source( + data: &StructuredSerializedData, + ) -> &Option<std::collections::HashMap<Self::Id, Self>> { + &data.image_bitmaps + } + + fn destination( + data: &mut StructuredSerializedData, + ) -> &mut Option<std::collections::HashMap<Self::Id, Self>> { + &mut data.image_bitmaps + } + + fn clone_for_broadcast(&self) -> Option<Self> { + Some(self.clone()) + } +} diff --git a/components/shared/constellation/structured_data/transferable.rs b/components/shared/constellation/structured_data/transferable.rs index 3210a41a538..bce10420182 100644 --- a/components/shared/constellation/structured_data/transferable.rs +++ b/components/shared/constellation/structured_data/transferable.rs @@ -24,6 +24,8 @@ pub struct TransformStreamData { /// All the DOM interfaces that can be transferred. #[derive(Clone, Copy, Debug, EnumIter)] pub enum Transferrable { + /// The `ImageBitmap` interface. + ImageBitmap, /// The `MessagePort` interface. MessagePort, /// The `ReadableStream` interface. |