mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Add support for <iframe> elements for Layout 2020
This change adds support for the <iframe> element to Layout 2020. In addition, certain aspects of the implementation are made the same between both layout systems.
This commit is contained in:
parent
e09acf88f4
commit
9e0b41ebc4
38 changed files with 281 additions and 164 deletions
|
@ -10,8 +10,10 @@ use crate::replaced::IntrinsicSizes;
|
|||
use crate::style_ext::ComputedValuesExt;
|
||||
use embedder_traits::Cursor;
|
||||
use euclid::{Point2D, SideOffsets2D, Size2D};
|
||||
use fnv::FnvHashMap;
|
||||
use gfx::text::glyph::GlyphStore;
|
||||
use mitochondria::OnceCell;
|
||||
use msg::constellation_msg::BrowsingContextId;
|
||||
use net_traits::image_cache::UsePlaceholder;
|
||||
use script_traits::compositor::CompositorDisplayListInfo;
|
||||
use std::sync::Arc;
|
||||
|
@ -22,6 +24,7 @@ use style::properties::ComputedValues;
|
|||
use style::values::computed::{BorderStyle, Length, LengthPercentage};
|
||||
use style::values::specified::text::TextDecorationLine;
|
||||
use style::values::specified::ui::CursorKind;
|
||||
use style_traits::CSSPixel;
|
||||
use webrender_api::{self as wr, units};
|
||||
|
||||
mod background;
|
||||
|
@ -48,6 +51,7 @@ pub struct DisplayListBuilder<'a> {
|
|||
pub context: &'a LayoutContext<'a>,
|
||||
pub wr: wr::DisplayListBuilder,
|
||||
pub compositor_info: CompositorDisplayListInfo,
|
||||
pub iframe_sizes: FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>,
|
||||
|
||||
/// Contentful paint, for the purpose of
|
||||
/// https://w3c.github.io/paint-timing/#first-contentful-paint
|
||||
|
@ -69,6 +73,7 @@ impl<'a> DisplayListBuilder<'a> {
|
|||
context,
|
||||
wr: wr::DisplayListBuilder::new(pipeline_id, fragment_tree.scrollable_overflow()),
|
||||
compositor_info: CompositorDisplayListInfo::default(),
|
||||
iframe_sizes: FnvHashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,6 +147,34 @@ impl Fragment {
|
|||
Visibility::Hidden => (),
|
||||
Visibility::Collapse => (),
|
||||
},
|
||||
Fragment::IFrame(iframe) => match iframe.style.get_inherited_box().visibility {
|
||||
Visibility::Visible => {
|
||||
builder.is_contentful = true;
|
||||
let rect = iframe
|
||||
.rect
|
||||
.to_physical(iframe.style.writing_mode, containing_block)
|
||||
.translate(containing_block.origin.to_vector());
|
||||
|
||||
builder.iframe_sizes.insert(
|
||||
iframe.browsing_context_id,
|
||||
Size2D::new(rect.size.width.px(), rect.size.height.px()),
|
||||
);
|
||||
|
||||
let common = builder.common_properties(rect.to_webrender(), &iframe.style);
|
||||
builder.wr.push_iframe(
|
||||
rect.to_webrender(),
|
||||
common.clip_rect,
|
||||
&wr::SpaceAndClipInfo {
|
||||
spatial_id: common.spatial_id,
|
||||
clip_id: common.clip_id,
|
||||
},
|
||||
iframe.pipeline_id.to_webrender(),
|
||||
true,
|
||||
);
|
||||
},
|
||||
Visibility::Hidden => (),
|
||||
Visibility::Collapse => (),
|
||||
},
|
||||
Fragment::Text(t) => match t.parent_style.get_inherited_box().visibility {
|
||||
Visibility::Visible => {
|
||||
self.build_display_list_for_text_fragment(t, builder, containing_block)
|
||||
|
|
|
@ -482,7 +482,7 @@ impl Fragment {
|
|||
stacking_context,
|
||||
);
|
||||
},
|
||||
Fragment::Text(_) | Fragment::Image(_) => {
|
||||
Fragment::Text(_) | Fragment::Image(_) | Fragment::IFrame(_) => {
|
||||
stacking_context.fragments.push(StackingContextFragment {
|
||||
section: StackingContextSection::Content,
|
||||
space_and_clip: builder.current_space_and_clip,
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside, DisplayOuts
|
|||
use crate::wrapper::GetStyleAndLayoutData;
|
||||
use atomic_refcell::AtomicRefMut;
|
||||
use html5ever::LocalName;
|
||||
use msg::constellation_msg::{BrowsingContextId, PipelineId};
|
||||
use net_traits::image::base::Image as NetImage;
|
||||
use script_layout_interface::wrapper_traits::{
|
||||
LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
|
||||
|
@ -396,6 +397,7 @@ pub(crate) trait NodeExt<'dom>: 'dom + Copy + LayoutNode<'dom> + Send + Sync {
|
|||
/// adjusted for `image_density`.
|
||||
fn as_image(self) -> Option<(Option<Arc<NetImage>>, PhysicalSize<f64>)>;
|
||||
fn as_canvas(self) -> Option<(CanvasInfo, PhysicalSize<f64>)>;
|
||||
fn as_iframe(self) -> Option<(PipelineId, BrowsingContextId)>;
|
||||
fn first_child(self) -> Option<Self>;
|
||||
fn next_sibling(self) -> Option<Self>;
|
||||
fn parent_node(self) -> Option<Self>;
|
||||
|
@ -462,6 +464,16 @@ where
|
|||
))
|
||||
}
|
||||
|
||||
fn as_iframe(self) -> Option<(PipelineId, BrowsingContextId)> {
|
||||
let node = self.to_threadsafe();
|
||||
match (node.iframe_pipeline_id(), node.iframe_browsing_context_id()) {
|
||||
(Some(pipeline_id), Some(browsing_context_id)) => {
|
||||
Some((pipeline_id, browsing_context_id))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn first_child(self) -> Option<Self> {
|
||||
TNode::first_child(&self)
|
||||
}
|
||||
|
|
|
@ -475,6 +475,7 @@ impl FragmentTree {
|
|||
.to_physical(fragment.parent_style.writing_mode, &containing_block),
|
||||
Fragment::AbsoluteOrFixedPositioned(_) |
|
||||
Fragment::Image(_) |
|
||||
Fragment::IFrame(_) |
|
||||
Fragment::Anonymous(_) => return None,
|
||||
};
|
||||
|
||||
|
@ -506,6 +507,7 @@ impl FragmentTree {
|
|||
Fragment::Box(_) |
|
||||
Fragment::Text(_) |
|
||||
Fragment::Image(_) |
|
||||
Fragment::IFrame(_) |
|
||||
Fragment::Anonymous(_) => return None,
|
||||
};
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ use gfx::font::FontMetrics as GfxFontMetrics;
|
|||
use gfx::text::glyph::GlyphStore;
|
||||
use gfx_traits::print_tree::PrintTree;
|
||||
use gfx_traits::{combine_id_with_fragment_type, FragmentType};
|
||||
use msg::constellation_msg::{BrowsingContextId, PipelineId};
|
||||
#[cfg(not(debug_assertions))]
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use servo_arc::Arc as ServoArc;
|
||||
|
@ -70,6 +71,7 @@ pub(crate) enum Fragment {
|
|||
AbsoluteOrFixedPositioned(AbsoluteOrFixedPositionedFragment),
|
||||
Text(TextFragment),
|
||||
Image(ImageFragment),
|
||||
IFrame(IFrameFragment),
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
@ -174,6 +176,16 @@ pub(crate) struct ImageFragment {
|
|||
pub image_key: ImageKey,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct IFrameFragment {
|
||||
pub debug_id: DebugId,
|
||||
pub pipeline_id: PipelineId,
|
||||
pub browsing_context_id: BrowsingContextId,
|
||||
pub rect: Rect<Length>,
|
||||
#[serde(skip_serializing)]
|
||||
pub style: ServoArc<ComputedValues>,
|
||||
}
|
||||
|
||||
impl Fragment {
|
||||
pub fn offset_inline(&mut self, offset: &Length) {
|
||||
let position = match self {
|
||||
|
@ -182,6 +194,7 @@ impl Fragment {
|
|||
Fragment::Anonymous(f) => &mut f.rect.start_corner,
|
||||
Fragment::Text(f) => &mut f.rect.start_corner,
|
||||
Fragment::Image(f) => &mut f.rect.start_corner,
|
||||
Fragment::IFrame(f) => &mut f.rect.start_corner,
|
||||
};
|
||||
|
||||
position.inline += *offset;
|
||||
|
@ -193,7 +206,8 @@ impl Fragment {
|
|||
Fragment::Text(fragment) => Some(fragment.tag),
|
||||
Fragment::AbsoluteOrFixedPositioned(_) |
|
||||
Fragment::Anonymous(_) |
|
||||
Fragment::Image(_) => None,
|
||||
Fragment::Image(_) |
|
||||
Fragment::IFrame(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,6 +218,7 @@ impl Fragment {
|
|||
Fragment::Anonymous(fragment) => fragment.print(tree),
|
||||
Fragment::Text(fragment) => fragment.print(tree),
|
||||
Fragment::Image(fragment) => fragment.print(tree),
|
||||
Fragment::IFrame(fragment) => fragment.print(tree),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,6 +236,9 @@ impl Fragment {
|
|||
Fragment::Image(fragment) => fragment
|
||||
.rect
|
||||
.to_physical(fragment.style.writing_mode, &containing_block),
|
||||
Fragment::IFrame(fragment) => fragment
|
||||
.rect
|
||||
.to_physical(fragment.style.writing_mode, &containing_block),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -462,6 +480,16 @@ impl ImageFragment {
|
|||
}
|
||||
}
|
||||
|
||||
impl IFrameFragment {
|
||||
pub fn print(&self, tree: &mut PrintTree) {
|
||||
tree.add_item(format!(
|
||||
"IFrame\
|
||||
\npipeline={:?} rect={:?}",
|
||||
self.pipeline_id, self.rect
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
impl CollapsedBlockMargins {
|
||||
pub fn from_margin(margin: &Sides<Length>) -> Self {
|
||||
Self {
|
||||
|
|
|
@ -10,7 +10,6 @@ use app_units::Au;
|
|||
use euclid::default::{Point2D, Rect};
|
||||
use euclid::Size2D;
|
||||
use euclid::Vector2D;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use script_layout_interface::rpc::TextIndexResponse;
|
||||
use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC};
|
||||
|
@ -19,7 +18,6 @@ use script_layout_interface::rpc::{OffsetParentResponse, ResolvedStyleResponse};
|
|||
use script_layout_interface::wrapper_traits::{
|
||||
LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
|
||||
};
|
||||
use script_traits::LayoutMsg as ConstellationMsg;
|
||||
use script_traits::UntrustedNodeAddress;
|
||||
use servo_arc::Arc as ServoArc;
|
||||
use std::collections::HashMap;
|
||||
|
@ -43,9 +41,6 @@ use webrender_api::ExternalScrollId;
|
|||
///
|
||||
/// This needs to be protected by a mutex so we can do fast RPCs.
|
||||
pub struct LayoutThreadData {
|
||||
/// The channel on which messages can be sent to the constellation.
|
||||
pub constellation_chan: IpcSender<ConstellationMsg>,
|
||||
|
||||
/// The root stacking context.
|
||||
pub display_list: Option<webrender_api::DisplayListBuilder>,
|
||||
|
||||
|
@ -427,6 +422,7 @@ fn process_offset_parent_query_inner(
|
|||
.to_physical(fragment.parent_style.writing_mode, &containing_block),
|
||||
Fragment::AbsoluteOrFixedPositioned(_) |
|
||||
Fragment::Image(_) |
|
||||
Fragment::IFrame(_) |
|
||||
Fragment::Anonymous(_) => unreachable!(),
|
||||
};
|
||||
let border_box = fragment_relative_rect.translate(containing_block.origin.to_vector());
|
||||
|
@ -503,6 +499,7 @@ fn process_offset_parent_query_inner(
|
|||
Fragment::AbsoluteOrFixedPositioned(_) |
|
||||
Fragment::Text(_) |
|
||||
Fragment::Image(_) |
|
||||
Fragment::IFrame(_) |
|
||||
Fragment::Anonymous(_) => None,
|
||||
};
|
||||
|
||||
|
@ -552,6 +549,7 @@ fn process_offset_parent_query_inner(
|
|||
Fragment::Box(_) |
|
||||
Fragment::Text(_) |
|
||||
Fragment::Image(_) |
|
||||
Fragment::IFrame(_) |
|
||||
Fragment::Anonymous(_) => None,
|
||||
}
|
||||
})
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use crate::context::LayoutContext;
|
||||
use crate::dom_traversal::NodeExt;
|
||||
use crate::fragments::{DebugId, Fragment, ImageFragment};
|
||||
use crate::fragments::{DebugId, Fragment, IFrameFragment, ImageFragment};
|
||||
use crate::geom::flow_relative::{Rect, Vec2};
|
||||
use crate::geom::PhysicalSize;
|
||||
use crate::sizing::ContentSizes;
|
||||
|
@ -12,6 +12,7 @@ use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
|
|||
use crate::ContainingBlock;
|
||||
use canvas_traits::canvas::{CanvasId, CanvasMsg, FromLayoutMsg};
|
||||
use ipc_channel::ipc::{self, IpcSender};
|
||||
use msg::constellation_msg::{BrowsingContextId, PipelineId};
|
||||
use net_traits::image::base::Image;
|
||||
use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
|
||||
use servo_arc::Arc as ServoArc;
|
||||
|
@ -41,6 +42,9 @@ pub(crate) struct ReplacedContent {
|
|||
///
|
||||
/// * For SVG, see https://svgwg.org/svg2-draft/coords.html#SizingSVGInCSS
|
||||
/// and again https://github.com/w3c/csswg-drafts/issues/4572.
|
||||
///
|
||||
/// * IFrames do not have intrinsic width and height or intrinsic ratio according
|
||||
/// to https://drafts.csswg.org/css-images/#intrinsic-dimensions.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct IntrinsicSizes {
|
||||
pub width: Option<Length>,
|
||||
|
@ -75,9 +79,16 @@ pub(crate) struct CanvasInfo {
|
|||
pub canvas_id: CanvasId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct IFrameInfo {
|
||||
pub pipeline_id: PipelineId,
|
||||
pub browsing_context_id: BrowsingContextId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) enum ReplacedContentKind {
|
||||
Image(Option<Arc<Image>>),
|
||||
IFrame(IFrameInfo),
|
||||
Canvas(CanvasInfo),
|
||||
}
|
||||
|
||||
|
@ -85,35 +96,51 @@ impl ReplacedContent {
|
|||
pub fn for_element<'dom>(element: impl NodeExt<'dom>) -> Option<Self> {
|
||||
let (kind, intrinsic_size_in_dots) = {
|
||||
if let Some((image, intrinsic_size_in_dots)) = element.as_image() {
|
||||
(ReplacedContentKind::Image(image), intrinsic_size_in_dots)
|
||||
(
|
||||
ReplacedContentKind::Image(image),
|
||||
Some(intrinsic_size_in_dots),
|
||||
)
|
||||
} else if let Some((canvas_info, intrinsic_size_in_dots)) = element.as_canvas() {
|
||||
(
|
||||
ReplacedContentKind::Canvas(canvas_info),
|
||||
intrinsic_size_in_dots,
|
||||
Some(intrinsic_size_in_dots),
|
||||
)
|
||||
} else if let Some((pipeline_id, browsing_context_id)) = element.as_iframe() {
|
||||
(
|
||||
ReplacedContentKind::IFrame(IFrameInfo {
|
||||
pipeline_id,
|
||||
browsing_context_id,
|
||||
}),
|
||||
None,
|
||||
)
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: should 'image-resolution' (when implemented) be used *instead* of
|
||||
// `script::dom::htmlimageelement::ImageRequest::current_pixel_density`?
|
||||
|
||||
// https://drafts.csswg.org/css-images-4/#the-image-resolution
|
||||
let dppx = 1.0;
|
||||
|
||||
let width = (intrinsic_size_in_dots.width as CSSFloat) / dppx;
|
||||
let height = (intrinsic_size_in_dots.height as CSSFloat) / dppx;
|
||||
|
||||
return Some(Self {
|
||||
kind,
|
||||
intrinsic: IntrinsicSizes {
|
||||
width: Some(Length::new(width)),
|
||||
height: Some(Length::new(height)),
|
||||
// FIXME https://github.com/w3c/csswg-drafts/issues/4572
|
||||
ratio: Some(width / height),
|
||||
let intrinsic = intrinsic_size_in_dots.map_or_else(
|
||||
|| IntrinsicSizes {
|
||||
width: None,
|
||||
height: None,
|
||||
ratio: None,
|
||||
},
|
||||
});
|
||||
|intrinsic_size_in_dots| {
|
||||
// FIXME: should 'image-resolution' (when implemented) be used *instead* of
|
||||
// `script::dom::htmlimageelement::ImageRequest::current_pixel_density`?
|
||||
// https://drafts.csswg.org/css-images-4/#the-image-resolution
|
||||
let dppx = 1.0;
|
||||
let width = (intrinsic_size_in_dots.width as CSSFloat) / dppx;
|
||||
let height = (intrinsic_size_in_dots.height as CSSFloat) / dppx;
|
||||
IntrinsicSizes {
|
||||
width: Some(Length::new(width)),
|
||||
height: Some(Length::new(height)),
|
||||
// FIXME https://github.com/w3c/csswg-drafts/issues/4572
|
||||
ratio: Some(width / height),
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return Some(Self { kind, intrinsic });
|
||||
}
|
||||
|
||||
pub fn from_image_url<'dom>(
|
||||
|
@ -203,6 +230,18 @@ impl ReplacedContent {
|
|||
})
|
||||
.into_iter()
|
||||
.collect(),
|
||||
ReplacedContentKind::IFrame(iframe) => {
|
||||
vec![Fragment::IFrame(IFrameFragment {
|
||||
debug_id: DebugId::new(),
|
||||
style: style.clone(),
|
||||
pipeline_id: iframe.pipeline_id,
|
||||
browsing_context_id: iframe.browsing_context_id,
|
||||
rect: Rect {
|
||||
start_corner: Vec2::zero(),
|
||||
size,
|
||||
},
|
||||
})]
|
||||
},
|
||||
ReplacedContentKind::Canvas(canvas_info) => {
|
||||
if self.intrinsic.width == Some(Length::zero()) ||
|
||||
self.intrinsic.height == Some(Length::zero())
|
||||
|
@ -264,6 +303,7 @@ impl ReplacedContent {
|
|||
|
||||
let default_object_size = || {
|
||||
// FIXME:
|
||||
// https://drafts.csswg.org/css-images/#default-object-size
|
||||
// “If 300px is too wide to fit the device, UAs should use the width of
|
||||
// the largest rectangle that has a 2:1 ratio and fits the device instead.”
|
||||
// “height of the largest rectangle that has a 2:1 ratio, has a height not greater
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue