aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorMartin Robinson <mrobinson@igalia.com>2024-04-15 22:20:55 +0200
committerGitHub <noreply@github.com>2024-04-15 20:20:55 +0000
commitf3790415974bd1318ff2c2a66ab9b169c5b4c2fe (patch)
tree41da5778fce265c7896b2fd177bff0e69c40921b /components
parenta77c15ee1613d1682affea55a3e821e42ccf71f0 (diff)
downloadservo-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.toml2
-rw-r--r--components/layout_2020/dom.rs29
-rw-r--r--components/layout_2020/dom_traversal.rs2
-rw-r--r--components/layout_2020/flow/root.rs6
-rw-r--r--components/layout_2020/replaced.rs39
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()
+}