diff options
author | Martin Robinson <mrobinson@igalia.com> | 2024-04-15 22:20:55 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-15 20:20:55 +0000 |
commit | f3790415974bd1318ff2c2a66ab9b169c5b4c2fe (patch) | |
tree | 41da5778fce265c7896b2fd177bff0e69c40921b /components | |
parent | a77c15ee1613d1682affea55a3e821e42ccf71f0 (diff) | |
download | servo-f3790415974bd1318ff2c2a66ab9b169c5b4c2fe.tar.gz servo-f3790415974bd1318ff2c2a66ab9b169c5b4c2fe.zip |
layout: Add support for `<object>` with image data URLs (#32069)
This is enough support for `<object>` to get Acid2 working.
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Diffstat (limited to 'components')
-rw-r--r-- | components/layout_2020/Cargo.toml | 2 | ||||
-rw-r--r-- | components/layout_2020/dom.rs | 29 | ||||
-rw-r--r-- | components/layout_2020/dom_traversal.rs | 2 | ||||
-rw-r--r-- | components/layout_2020/flow/root.rs | 6 | ||||
-rw-r--r-- | components/layout_2020/replaced.rs | 39 |
5 files changed, 71 insertions, 7 deletions
diff --git a/components/layout_2020/Cargo.toml b/components/layout_2020/Cargo.toml index 9820827150a..5ed04e7b908 100644 --- a/components/layout_2020/Cargo.toml +++ b/components/layout_2020/Cargo.toml @@ -44,6 +44,8 @@ style = { workspace = true } style_traits = { workspace = true } unicode-script = { workspace = true } unicode-segmentation = { workspace = true } +url = { workspace = true } +data-url = { workspace = true } webrender_api = { workspace = true } xi-unicode = { workspace = true } diff --git a/components/layout_2020/dom.rs b/components/layout_2020/dom.rs index b065757fefd..5d75317f75d 100644 --- a/components/layout_2020/dom.rs +++ b/components/layout_2020/dom.rs @@ -6,10 +6,15 @@ use std::marker::PhantomData; use std::sync::{Arc, Mutex}; use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; +use html5ever::{local_name, namespace_url, ns}; use msg::constellation_msg::{BrowsingContextId, PipelineId}; use net_traits::image::base::Image as NetImage; -use script_layout_interface::wrapper_traits::{LayoutDataTrait, LayoutNode, ThreadSafeLayoutNode}; -use script_layout_interface::HTMLCanvasDataSource; +use script_layout_interface::wrapper_traits::{ + LayoutDataTrait, LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode, +}; +use script_layout_interface::{ + HTMLCanvasDataSource, LayoutElementType, LayoutNodeType as ScriptLayoutNodeType, +}; use servo_arc::Arc as ServoArc; use style::properties::ComputedValues; @@ -95,6 +100,7 @@ pub(crate) trait NodeExt<'dom>: 'dom + LayoutNode<'dom> { fn as_canvas(self) -> Option<(CanvasInfo, PhysicalSize<f64>)>; fn as_iframe(self) -> Option<(PipelineId, BrowsingContextId)>; fn as_video(self) -> Option<(webrender_api::ImageKey, PhysicalSize<f64>)>; + fn as_typeless_object_with_data_attribute(self) -> Option<String>; fn style(self, context: &LayoutContext) -> ServoArc<ComputedValues>; fn layout_data_mut(self) -> AtomicRefMut<'dom, InnerDOMLayoutData>; @@ -163,6 +169,25 @@ where } } + fn as_typeless_object_with_data_attribute(self) -> Option<String> { + if self.type_id() != ScriptLayoutNodeType::Element(LayoutElementType::HTMLObjectElement) { + return None; + } + let Some(element) = self.to_threadsafe().as_element() else { + return None; + }; + + // TODO: This is the what the legacy layout system does, but really if Servo + // supports any `<object>` that's an image, it should support those with URLs + // and `type` attributes with image mime types. + if element.get_attr(&ns!(), &local_name!("type")).is_some() { + return None; + } + element + .get_attr(&ns!(), &local_name!("data")) + .map(|string| string.to_owned()) + } + fn style(self, context: &LayoutContext) -> ServoArc<ComputedValues> { self.to_threadsafe().style(context.shared_context()) } diff --git a/components/layout_2020/dom_traversal.rs b/components/layout_2020/dom_traversal.rs index b00108a2b9a..9fed32cbc6d 100644 --- a/components/layout_2020/dom_traversal.rs +++ b/components/layout_2020/dom_traversal.rs @@ -175,7 +175,7 @@ fn traverse_element<'dom, Node>( ) where Node: NodeExt<'dom>, { - let replaced = ReplacedContent::for_element(element); + let replaced = ReplacedContent::for_element(element, context); let style = element.style(context); match Display::from(style.get_box().display) { Display::None => element.unset_all_boxes(), diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index eecb6d4c2f7..b3b436ce23d 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -204,7 +204,7 @@ impl BoxTree { loop { if let Some((primary_style, display_inside, update_point)) = update_point(dirty_node) { - let contents = ReplacedContent::for_element(dirty_node) + let contents = ReplacedContent::for_element(dirty_node, context) .map_or(Contents::OfElement, Contents::Replaced); let info = NodeAndStyleInfo::new(dirty_node, Arc::clone(&primary_style)); let out_of_flow_absolutely_positioned_box = ArcRefCell::new( @@ -262,8 +262,8 @@ fn construct_for_root_element<'dom>( Display::GeneratingBox(display_generating_box) => display_generating_box.display_inside(), }; - let contents = - ReplacedContent::for_element(root_element).map_or(Contents::OfElement, Contents::Replaced); + let contents = ReplacedContent::for_element(root_element, context) + .map_or(Contents::OfElement, Contents::Replaced); let root_box = if box_style.position.is_absolutely_positioned() { BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(ArcRefCell::new( AbsolutelyPositionedBox::construct(context, &info, display_inside, contents), diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index 2c5276b20b2..7f3f7743c1c 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -7,6 +7,7 @@ use std::sync::{Arc, Mutex}; use app_units::Au; use canvas_traits::canvas::{CanvasId, CanvasMsg, FromLayoutMsg}; +use data_url::DataUrl; use ipc_channel::ipc::{self, IpcSender}; use msg::constellation_msg::{BrowsingContextId, PipelineId}; use net_traits::image::base::Image; @@ -19,6 +20,7 @@ use style::values::computed::image::Image as ComputedImage; use style::values::computed::{Length, LengthOrAuto}; use style::values::CSSFloat; use style::Zero; +use url::Url; use webrender_api::ImageKey; use crate::context::LayoutContext; @@ -131,7 +133,17 @@ pub(crate) enum ReplacedContentKind { } impl ReplacedContent { - pub fn for_element<'dom>(element: impl NodeExt<'dom>) -> Option<Self> { + pub fn for_element<'dom>(element: impl NodeExt<'dom>, context: &LayoutContext) -> Option<Self> { + if let Some(ref data_attribute_string) = element.as_typeless_object_with_data_attribute() { + if let Some(url) = try_to_parse_image_data_url(data_attribute_string) { + return Self::from_image_url( + element, + context, + &ComputedUrl::Valid(ServoArc::new(url)), + ); + } + } + let (kind, intrinsic_size_in_dots) = { if let Some((image, intrinsic_size_in_dots)) = element.as_image() { ( @@ -558,3 +570,28 @@ impl ReplacedContent { } } } + +fn try_to_parse_image_data_url(string: &str) -> Option<Url> { + if !string.starts_with("data:") { + return None; + } + let Some(data_url) = DataUrl::process(string).ok() else { + return None; + }; + + let mime_type = data_url.mime_type(); + if mime_type.type_ != "image" { + return None; + } + + // TODO: Find a better way to test for supported image formats. Currently this type of check is + // repeated several places in Servo, but should be centralized somehow. + if !matches!( + mime_type.subtype.as_str(), + "png" | "jpeg" | "gif" | "webp" | "bmp" | "ico" + ) { + return None; + } + + Url::parse(string).ok() +} |