Auto merge of #25280 - servo:2020-hit-test, r=SimonSapin

Handle cursor and hit testing in 2020
This commit is contained in:
bors-servo 2019-12-13 13:38:08 -05:00 committed by GitHub
commit 0103ab7698
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 305 additions and 144 deletions

1
Cargo.lock generated
View file

@ -2721,6 +2721,7 @@ dependencies = [
"app_units",
"atomic_refcell",
"cssparser",
"embedder_traits",
"euclid",
"gfx",
"gfx_traits",

View file

@ -16,6 +16,7 @@ doctest = false
app_units = "0.7"
atomic_refcell = "0.1"
cssparser = "0.27"
embedder_traits = {path = "../embedder_traits"}
euclid = "0.20"
gfx = {path = "../gfx"}
gfx_traits = {path = "../gfx_traits"}

View file

@ -4,9 +4,11 @@
use crate::fragments::{BoxFragment, Fragment};
use crate::geom::physical::{Rect, Vec2};
use embedder_traits::Cursor;
use euclid::{Point2D, SideOffsets2D};
use gfx::text::glyph::GlyphStore;
use std::sync::Arc;
use style::properties::ComputedValues;
use style::values::computed::{BorderStyle, Length};
use webrender_api::{self as wr, units, CommonItemProperties, PrimitiveFlags};
@ -58,11 +60,12 @@ impl Fragment {
.translate(&containing_block.top_left);
let mut baseline_origin = rect.top_left.clone();
baseline_origin.y += t.ascent;
let cursor = cursor(&t.parent_style, Cursor::Text);
let common = CommonItemProperties {
clip_rect: rect.clone().into(),
clip_id: wr::ClipId::root(builder.pipeline_id),
spatial_id: wr::SpatialId::root_scroll_node(builder.pipeline_id),
hit_info: None,
hit_info: cursor.map(|cursor| (t.tag.0 as u64, cursor as u16)),
// TODO(gw): Make use of the WR backface visibility functionality.
flags: PrimitiveFlags::default(),
};
@ -119,11 +122,12 @@ impl BoxFragment {
.to_physical(self.style.writing_mode, containing_block)
.translate(&containing_block.top_left)
.into();
let cursor = cursor(&self.style, Cursor::Default);
let common = CommonItemProperties {
clip_rect: border_rect,
clip_id: wr::ClipId::root(builder.pipeline_id),
spatial_id: wr::SpatialId::root_scroll_node(builder.pipeline_id),
hit_info: None,
hit_info: cursor.map(|cursor| (self.tag.0 as u64, cursor as u16)),
// TODO(gw): Make use of the WR backface visibility functionality.
flags: PrimitiveFlags::default(),
};
@ -147,7 +151,7 @@ impl BoxFragment {
let background_color = self
.style
.resolve_color(self.style.clone_background_color());
if background_color.alpha > 0 {
if background_color.alpha > 0 || common.hit_info.is_some() {
builder.wr.push_rect(common, rgba(background_color))
}
}
@ -228,3 +232,51 @@ fn glyphs(glyph_runs: &[Arc<GlyphStore>], mut origin: Vec2<Length>) -> Vec<wr::G
}
glyphs
}
fn cursor(values: &ComputedValues, default: Cursor) -> Option<Cursor> {
use style::computed_values::pointer_events::T as PointerEvents;
use style::values::specified::ui::CursorKind;
let inherited_ui = values.get_inherited_ui();
if inherited_ui.pointer_events == PointerEvents::None {
return None;
}
Some(match inherited_ui.cursor.keyword {
CursorKind::Auto => default,
CursorKind::None => Cursor::None,
CursorKind::Default => Cursor::Default,
CursorKind::Pointer => Cursor::Pointer,
CursorKind::ContextMenu => Cursor::ContextMenu,
CursorKind::Help => Cursor::Help,
CursorKind::Progress => Cursor::Progress,
CursorKind::Wait => Cursor::Wait,
CursorKind::Cell => Cursor::Cell,
CursorKind::Crosshair => Cursor::Crosshair,
CursorKind::Text => Cursor::Text,
CursorKind::VerticalText => Cursor::VerticalText,
CursorKind::Alias => Cursor::Alias,
CursorKind::Copy => Cursor::Copy,
CursorKind::Move => Cursor::Move,
CursorKind::NoDrop => Cursor::NoDrop,
CursorKind::NotAllowed => Cursor::NotAllowed,
CursorKind::Grab => Cursor::Grab,
CursorKind::Grabbing => Cursor::Grabbing,
CursorKind::EResize => Cursor::EResize,
CursorKind::NResize => Cursor::NResize,
CursorKind::NeResize => Cursor::NeResize,
CursorKind::NwResize => Cursor::NwResize,
CursorKind::SResize => Cursor::SResize,
CursorKind::SeResize => Cursor::SeResize,
CursorKind::SwResize => Cursor::SwResize,
CursorKind::WResize => Cursor::WResize,
CursorKind::EwResize => Cursor::EwResize,
CursorKind::NsResize => Cursor::NsResize,
CursorKind::NeswResize => Cursor::NeswResize,
CursorKind::NwseResize => Cursor::NwseResize,
CursorKind::ColResize => Cursor::ColResize,
CursorKind::RowResize => Cursor::RowResize,
CursorKind::AllScroll => Cursor::AllScroll,
CursorKind::ZoomIn => Cursor::ZoomIn,
CursorKind::ZoomOut => Cursor::ZoomOut,
})
}

View file

@ -14,7 +14,7 @@ use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
use servo_arc::Arc as ServoArc;
use std::marker::PhantomData as marker;
use std::sync::Arc;
use style::dom::TNode;
use style::dom::{OpaqueNode, TNode};
use style::properties::ComputedValues;
use style::selector_parser::PseudoElement;
@ -24,9 +24,9 @@ pub enum WhichPseudoElement {
After,
}
pub(super) enum Contents<Node> {
pub(super) enum Contents {
/// Refers to a DOM subtree, plus `::before` and `::after` pseudo-elements.
OfElement(Node),
OfElement,
/// Example: an `<img src=…>` element.
/// <https://drafts.csswg.org/css2/conform.html#replaced-element>
@ -37,8 +37,8 @@ pub(super) enum Contents<Node> {
OfPseudoElement(Vec<PseudoElementContentItem>),
}
pub(super) enum NonReplacedContents<Node> {
OfElement(Node),
pub(super) enum NonReplacedContents {
OfElement,
OfPseudoElement(Vec<PseudoElementContentItem>),
}
@ -51,14 +51,15 @@ pub(super) trait TraversalHandler<'dom, Node>
where
Node: 'dom,
{
fn handle_text(&mut self, text: String, parent_style: &ServoArc<ComputedValues>);
fn handle_text(&mut self, node: Node, text: String, parent_style: &ServoArc<ComputedValues>);
/// Or pseudo-element
fn handle_element(
&mut self,
node: Node,
style: &ServoArc<ComputedValues>,
display: DisplayGeneratingBox,
contents: Contents<Node>,
contents: Contents,
box_slot: BoxSlot<'dom>,
);
}
@ -75,7 +76,7 @@ fn traverse_children_of<'dom, Node>(
let mut next = parent_element.first_child();
while let Some(child) = next {
if let Some(contents) = child.as_text() {
handler.handle_text(contents, &child.style(context));
handler.handle_text(child, contents, &child.style(context));
} else if child.is_element() {
traverse_element(child, context, handler);
}
@ -108,9 +109,10 @@ fn traverse_element<'dom, Node>(
},
Display::GeneratingBox(display) => {
handler.handle_element(
element,
&style,
display,
replaced.map_or(Contents::OfElement(element), Contents::Replaced),
replaced.map_or(Contents::OfElement, Contents::Replaced),
element.element_box_slot(),
);
},
@ -131,19 +133,20 @@ fn traverse_pseudo_element<'dom, Node>(
Display::Contents => {
element.unset_pseudo_element_box(which);
let items = generate_pseudo_element_content(&style, element, context);
traverse_pseudo_element_contents(&style, context, handler, items);
traverse_pseudo_element_contents(element, &style, context, handler, items);
},
Display::GeneratingBox(display) => {
let items = generate_pseudo_element_content(&style, element, context);
let contents = Contents::OfPseudoElement(items);
let box_slot = element.pseudo_element_box_slot(which);
handler.handle_element(&style, display, contents, box_slot);
handler.handle_element(element, &style, display, contents, box_slot);
},
}
}
}
fn traverse_pseudo_element_contents<'dom, Node>(
node: Node,
pseudo_element_style: &ServoArc<ComputedValues>,
context: &LayoutContext,
handler: &mut impl TraversalHandler<'dom, Node>,
@ -154,7 +157,9 @@ fn traverse_pseudo_element_contents<'dom, Node>(
let mut anonymous_style = None;
for item in items {
match item {
PseudoElementContentItem::Text(text) => handler.handle_text(text, pseudo_element_style),
PseudoElementContentItem::Text(text) => {
handler.handle_text(node, text, pseudo_element_style)
},
PseudoElementContentItem::Replaced(contents) => {
let item_style = anonymous_style.get_or_insert_with(|| {
context
@ -176,6 +181,7 @@ fn traverse_pseudo_element_contents<'dom, Node>(
Display::GeneratingBox(display_inline)
);
handler.handle_element(
node,
item_style,
display_inline,
Contents::Replaced(contents),
@ -187,51 +193,51 @@ fn traverse_pseudo_element_contents<'dom, Node>(
}
}
impl<Node> Contents<Node> {
impl Contents {
/// Returns true iff the `try_from` impl below would return `Err(_)`
pub fn is_replaced(&self) -> bool {
match self {
Contents::OfElement(_) | Contents::OfPseudoElement(_) => false,
Contents::OfElement | Contents::OfPseudoElement(_) => false,
Contents::Replaced(_) => true,
}
}
}
impl<Node> std::convert::TryFrom<Contents<Node>> for NonReplacedContents<Node> {
impl std::convert::TryFrom<Contents> for NonReplacedContents {
type Error = ReplacedContent;
fn try_from(contents: Contents<Node>) -> Result<Self, Self::Error> {
fn try_from(contents: Contents) -> Result<Self, Self::Error> {
match contents {
Contents::OfElement(node) => Ok(NonReplacedContents::OfElement(node)),
Contents::OfElement => Ok(NonReplacedContents::OfElement),
Contents::OfPseudoElement(items) => Ok(NonReplacedContents::OfPseudoElement(items)),
Contents::Replaced(replaced) => Err(replaced),
}
}
}
impl<Node> std::convert::From<NonReplacedContents<Node>> for Contents<Node> {
fn from(contents: NonReplacedContents<Node>) -> Self {
impl From<NonReplacedContents> for Contents {
fn from(contents: NonReplacedContents) -> Self {
match contents {
NonReplacedContents::OfElement(node) => Contents::OfElement(node),
NonReplacedContents::OfElement => Contents::OfElement,
NonReplacedContents::OfPseudoElement(items) => Contents::OfPseudoElement(items),
}
}
}
impl<'dom, Node> NonReplacedContents<Node>
where
Node: NodeExt<'dom>,
{
pub(crate) fn traverse(
impl NonReplacedContents {
pub(crate) fn traverse<'dom, Node>(
self,
inherited_style: &ServoArc<ComputedValues>,
context: &LayoutContext,
node: Node,
inherited_style: &ServoArc<ComputedValues>,
handler: &mut impl TraversalHandler<'dom, Node>,
) {
) where
Node: NodeExt<'dom>,
{
match self {
NonReplacedContents::OfElement(node) => traverse_children_of(node, context, handler),
NonReplacedContents::OfElement => traverse_children_of(node, context, handler),
NonReplacedContents::OfPseudoElement(items) => {
traverse_pseudo_element_contents(inherited_style, context, handler, items)
traverse_pseudo_element_contents(node, inherited_style, context, handler, items)
},
}
}
@ -307,6 +313,7 @@ pub(crate) trait NodeExt<'dom>: 'dom + Copy + LayoutNode + Send + Sync {
fn parent_node(self) -> Option<Self>;
fn style(self, context: &LayoutContext) -> ServoArc<ComputedValues>;
fn as_opaque(self) -> OpaqueNode;
fn layout_data_mut(&self) -> AtomicRefMut<LayoutDataForElement>;
fn element_box_slot(&self) -> BoxSlot<'dom>;
fn pseudo_element_box_slot(&self, which: WhichPseudoElement) -> BoxSlot<'dom>;
@ -366,6 +373,10 @@ where
self.to_threadsafe().style(context.shared_context())
}
fn as_opaque(self) -> OpaqueNode {
self.opaque()
}
fn layout_data_mut(&self) -> AtomicRefMut<LayoutDataForElement> {
self.get_raw_data()
.map(|d| d.layout_data.borrow_mut())

View file

@ -22,12 +22,13 @@ use style::selector_parser::PseudoElement;
impl BlockFormattingContext {
pub fn construct<'dom>(
context: &LayoutContext,
node: impl NodeExt<'dom>,
style: &Arc<ComputedValues>,
contents: NonReplacedContents<impl NodeExt<'dom>>,
contents: NonReplacedContents,
content_sizes: ContentSizesRequest,
) -> (Self, BoxContentSizes) {
let (contents, contains_floats, inline_content_sizes) =
BlockContainer::construct(context, style, contents, content_sizes);
BlockContainer::construct(context, node, style, contents, content_sizes);
// FIXME: add contribution to `inline_content_sizes` of floats in this formatting context
// https://dbaron.org/css/intrinsic/#intrinsic
let bfc = Self {
@ -38,25 +39,26 @@ impl BlockFormattingContext {
}
}
enum IntermediateBlockLevelBox<Node> {
SameFormattingContextBlock {
style: Arc<ComputedValues>,
contents: IntermediateBlockContainer<Node>,
},
struct BlockLevelJob<'dom, Node> {
node: Node,
box_slot: BoxSlot<'dom>,
style: Arc<ComputedValues>,
kind: BlockLevelCreator,
}
enum BlockLevelCreator {
SameFormattingContextBlock(IntermediateBlockContainer),
Independent {
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents<Node>,
contents: Contents,
},
OutOfFlowAbsolutelyPositionedBox {
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents<Node>,
contents: Contents,
},
OutOfFlowFloatBox {
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents<Node>,
contents: Contents,
},
}
@ -67,9 +69,9 @@ enum IntermediateBlockLevelBox<Node> {
/// of a given element.
///
/// Deferring allows using rayons `into_par_iter`.
enum IntermediateBlockContainer<Node> {
enum IntermediateBlockContainer {
InlineFormattingContext(InlineFormattingContext),
Deferred { contents: NonReplacedContents<Node> },
Deferred(NonReplacedContents),
}
/// A builder for a block container.
@ -79,11 +81,13 @@ enum IntermediateBlockContainer<Node> {
struct BlockContainerBuilder<'dom, 'style, Node> {
context: &'style LayoutContext<'style>,
root: Node,
block_container_style: &'style Arc<ComputedValues>,
/// The list of block-level boxes of the final block container.
/// The list of block-level boxes to be built for the final block container.
///
/// Contains all the complete block level boxes we found traversing the tree
/// Contains all the block-level jobs we found traversing the tree
/// so far, if this is empty at the end of the traversal and the ongoing
/// inline formatting context is not empty, the block container establishes
/// an inline formatting context (see end of `build`).
@ -94,7 +98,7 @@ struct BlockContainerBuilder<'dom, 'style, Node> {
/// doesn't have a next sibling, we either reached the end of the container
/// root or there are ongoing inline-level boxes
/// (see `handle_block_level_element`).
block_level_boxes: Vec<(IntermediateBlockLevelBox<Node>, BoxSlot<'dom>)>,
block_level_boxes: Vec<BlockLevelJob<'dom, Node>>,
/// The ongoing inline formatting context of the builder.
///
@ -131,12 +135,14 @@ struct BlockContainerBuilder<'dom, 'style, Node> {
impl BlockContainer {
pub fn construct<'dom>(
context: &LayoutContext,
root: impl NodeExt<'dom>,
block_container_style: &Arc<ComputedValues>,
contents: NonReplacedContents<impl NodeExt<'dom>>,
contents: NonReplacedContents,
content_sizes: ContentSizesRequest,
) -> (BlockContainer, ContainsFloats, BoxContentSizes) {
let mut builder = BlockContainerBuilder {
context,
root,
block_container_style,
block_level_boxes: Vec::new(),
ongoing_inline_formatting_context: InlineFormattingContext::default(),
@ -145,7 +151,7 @@ impl BlockContainer {
contains_floats: ContainsFloats::No,
};
contents.traverse(block_container_style, context, &mut builder);
contents.traverse(context, root, block_container_style, &mut builder);
debug_assert!(builder.ongoing_inline_boxes_stack.is_empty());
@ -176,17 +182,14 @@ impl BlockContainer {
contains_floats: builder.contains_floats,
outer_content_sizes_of_children: ContentSizes::zero(),
};
let mapfold =
|acc: &mut Accumulator,
(intermediate, box_slot): (IntermediateBlockLevelBox<_>, BoxSlot<'_>)| {
let (block_level_box, box_contains_floats) = intermediate.finish(
context,
content_sizes.if_requests_inline(|| &mut acc.outer_content_sizes_of_children),
);
acc.contains_floats |= box_contains_floats;
box_slot.set(LayoutBox::BlockLevel(block_level_box.clone()));
block_level_box
};
let mapfold = |acc: &mut Accumulator, creator: BlockLevelJob<'dom, _>| {
let (block_level_box, box_contains_floats) = creator.finish(
context,
content_sizes.if_requests_inline(|| &mut acc.outer_content_sizes_of_children),
);
acc.contains_floats |= box_contains_floats;
block_level_box
};
let block_level_boxes = if context.use_rayon {
builder
.block_level_boxes
@ -231,15 +234,16 @@ where
{
fn handle_element(
&mut self,
node: Node,
style: &Arc<ComputedValues>,
display: DisplayGeneratingBox,
contents: Contents<Node>,
contents: Contents,
box_slot: BoxSlot<'dom>,
) {
match display {
DisplayGeneratingBox::OutsideInside { outside, inside } => match outside {
DisplayOutside::Inline => box_slot.set(LayoutBox::InlineLevel(
self.handle_inline_level_element(style, inside, contents),
self.handle_inline_level_element(node, style, inside, contents),
)),
DisplayOutside::Block => {
let box_style = style.get_box();
@ -247,22 +251,29 @@ where
// https://drafts.csswg.org/css2/visuren.html#dis-pos-flo
if box_style.position.is_absolutely_positioned() {
self.handle_absolutely_positioned_element(
node,
style.clone(),
inside,
contents,
box_slot,
)
} else if box_style.float.is_floating() {
self.handle_float_element(style.clone(), inside, contents, box_slot)
self.handle_float_element(node, style.clone(), inside, contents, box_slot)
} else {
self.handle_block_level_element(style.clone(), inside, contents, box_slot)
self.handle_block_level_element(
node,
style.clone(),
inside,
contents,
box_slot,
)
}
},
},
}
}
fn handle_text(&mut self, input: String, parent_style: &Arc<ComputedValues>) {
fn handle_text(&mut self, node: Node, input: String, parent_style: &Arc<ComputedValues>) {
let (leading_whitespace, mut input) = self.handle_leading_whitespace(&input);
if leading_whitespace || !input.is_empty() {
// This text node should be pushed either to the next ongoing
@ -319,6 +330,7 @@ where
if let Some(text) = new_text_run_contents {
let parent_style = parent_style.clone();
inlines.push(Arc::new(InlineLevelBox::TextRun(TextRun {
tag: node.as_opaque(),
parent_style,
text,
})))
@ -368,15 +380,17 @@ where
fn handle_inline_level_element(
&mut self,
node: Node,
style: &Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents<Node>,
contents: Contents,
) -> Arc<InlineLevelBox> {
let box_ = if display_inside == DisplayInside::Flow && !contents.is_replaced() {
// We found un inline box.
// Whatever happened before, all we need to do before recurring
// is to remember this ongoing inline level box.
self.ongoing_inline_boxes_stack.push(InlineBox {
tag: node.as_opaque(),
style: style.clone(),
first_fragment: true,
last_fragment: false,
@ -384,9 +398,12 @@ where
});
// `unwrap` doesnt panic here because `is_replaced` returned `false`.
NonReplacedContents::try_from(contents)
.unwrap()
.traverse(&style, self.context, self);
NonReplacedContents::try_from(contents).unwrap().traverse(
self.context,
node,
&style,
self,
);
let mut inline_box = self
.ongoing_inline_boxes_stack
@ -398,6 +415,7 @@ where
Arc::new(InlineLevelBox::Atomic(
IndependentFormattingContext::construct(
self.context,
node,
style.clone(),
display_inside,
contents,
@ -411,9 +429,10 @@ where
fn handle_block_level_element(
&mut self,
node: Node,
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents<Node>,
contents: Contents,
box_slot: BoxSlot<'dom>,
) {
// We just found a block level element, all ongoing inline level boxes
@ -427,6 +446,7 @@ where
.rev()
.map(|ongoing| {
let fragmented = InlineBox {
tag: ongoing.tag,
style: ongoing.style.clone(),
first_fragment: ongoing.first_fragment,
// The fragmented boxes before the block level element
@ -459,47 +479,60 @@ where
// context needs to be ended.
self.end_ongoing_inline_formatting_context();
let intermediate_box = match contents.try_into() {
let kind = match contents.try_into() {
Ok(contents) => match display_inside {
DisplayInside::Flow => IntermediateBlockLevelBox::SameFormattingContextBlock {
style,
contents: IntermediateBlockContainer::Deferred { contents },
},
_ => IntermediateBlockLevelBox::Independent {
style,
DisplayInside::Flow => BlockLevelCreator::SameFormattingContextBlock(
IntermediateBlockContainer::Deferred(contents),
),
_ => BlockLevelCreator::Independent {
display_inside,
contents: contents.into(),
},
},
Err(contents) => {
let contents = Contents::Replaced(contents);
IntermediateBlockLevelBox::Independent {
style,
BlockLevelCreator::Independent {
display_inside,
contents,
}
},
};
self.block_level_boxes.push((intermediate_box, box_slot))
self.block_level_boxes.push(BlockLevelJob {
node,
box_slot,
style,
kind,
});
}
fn handle_absolutely_positioned_element(
&mut self,
node: Node,
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents<Node>,
contents: Contents,
box_slot: BoxSlot<'dom>,
) {
if !self.has_ongoing_inline_formatting_context() {
let box_ = IntermediateBlockLevelBox::OutOfFlowAbsolutelyPositionedBox {
style,
let kind = BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
contents,
display_inside,
};
self.block_level_boxes.push((box_, box_slot));
self.block_level_boxes.push(BlockLevelJob {
node,
box_slot,
style,
kind,
});
} else {
let box_ = Arc::new(InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(
AbsolutelyPositionedBox::construct(self.context, style, display_inside, contents),
AbsolutelyPositionedBox::construct(
self.context,
node,
style,
display_inside,
contents,
),
));
self.current_inline_level_boxes().push(box_.clone());
box_slot.set(LayoutBox::InlineLevel(box_))
@ -508,23 +541,29 @@ where
fn handle_float_element(
&mut self,
node: Node,
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents<Node>,
contents: Contents,
box_slot: BoxSlot<'dom>,
) {
self.contains_floats = ContainsFloats::Yes;
if !self.has_ongoing_inline_formatting_context() {
let box_ = IntermediateBlockLevelBox::OutOfFlowFloatBox {
style,
let kind = BlockLevelCreator::OutOfFlowFloatBox {
contents,
display_inside,
};
self.block_level_boxes.push((box_, box_slot));
self.block_level_boxes.push(BlockLevelJob {
node,
box_slot,
style,
kind,
});
} else {
let box_ = Arc::new(InlineLevelBox::OutOfFlowFloatBox(FloatBox::construct(
self.context,
node,
style,
display_inside,
contents,
@ -557,13 +596,18 @@ where
)
});
let box_ = IntermediateBlockLevelBox::SameFormattingContextBlock {
style: anonymous_style.clone(),
contents: IntermediateBlockContainer::InlineFormattingContext(std::mem::take(
let kind = BlockLevelCreator::SameFormattingContextBlock(
IntermediateBlockContainer::InlineFormattingContext(std::mem::take(
&mut self.ongoing_inline_formatting_context,
)),
};
self.block_level_boxes.push((box_, BoxSlot::dummy()))
);
self.block_level_boxes.push(BlockLevelJob {
node: self.root,
// FIXME(nox): We should be storing this somewhere.
box_slot: BoxSlot::dummy(),
style: anonymous_style.clone(),
kind,
});
}
fn current_inline_level_boxes(&mut self) -> &mut Vec<Arc<InlineLevelBox>> {
@ -582,7 +626,7 @@ where
}
}
impl<'dom, Node> IntermediateBlockLevelBox<Node>
impl<'dom, Node> BlockLevelJob<'dom, Node>
where
Node: NodeExt<'dom>,
{
@ -591,10 +635,13 @@ where
context: &LayoutContext,
max_assign_in_flow_outer_content_sizes_to: Option<&mut ContentSizes>,
) -> (Arc<BlockLevelBox>, ContainsFloats) {
match self {
IntermediateBlockLevelBox::SameFormattingContextBlock { style, contents } => {
let node = self.node;
let style = self.style;
let (block_level_box, contains_floats) = match self.kind {
BlockLevelCreator::SameFormattingContextBlock(contents) => {
let (contents, contains_floats, box_content_sizes) = contents.finish(
context,
node,
&style,
ContentSizesRequest::inline_if(
max_assign_in_flow_outer_content_sizes_to.is_some() &&
@ -604,12 +651,14 @@ where
if let Some(to) = max_assign_in_flow_outer_content_sizes_to {
to.max_assign(&box_content_sizes.outer_inline(&style))
}
let block_level_box =
Arc::new(BlockLevelBox::SameFormattingContextBlock { contents, style });
let block_level_box = Arc::new(BlockLevelBox::SameFormattingContextBlock {
tag: node.as_opaque(),
contents,
style,
});
(block_level_box, contains_floats)
},
IntermediateBlockLevelBox::Independent {
style,
BlockLevelCreator::Independent {
display_inside,
contents,
} => {
@ -619,6 +668,7 @@ where
);
let contents = IndependentFormattingContext::construct(
context,
node,
style,
display_inside,
contents,
@ -632,43 +682,48 @@ where
ContainsFloats::No,
)
},
IntermediateBlockLevelBox::OutOfFlowAbsolutelyPositionedBox {
style,
BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
display_inside,
contents,
} => {
let block_level_box = Arc::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
AbsolutelyPositionedBox::construct(context, style, display_inside, contents),
AbsolutelyPositionedBox::construct(
context,
node,
style,
display_inside,
contents,
),
));
(block_level_box, ContainsFloats::No)
},
IntermediateBlockLevelBox::OutOfFlowFloatBox {
style,
BlockLevelCreator::OutOfFlowFloatBox {
display_inside,
contents,
} => {
let block_level_box = Arc::new(BlockLevelBox::OutOfFlowFloatBox(
FloatBox::construct(context, style, display_inside, contents),
FloatBox::construct(context, node, style, display_inside, contents),
));
(block_level_box, ContainsFloats::Yes)
},
}
};
self.box_slot
.set(LayoutBox::BlockLevel(block_level_box.clone()));
(block_level_box, contains_floats)
}
}
impl<'dom, Node> IntermediateBlockContainer<Node>
where
Node: NodeExt<'dom>,
{
fn finish(
impl IntermediateBlockContainer {
fn finish<'dom>(
self,
context: &LayoutContext,
node: impl NodeExt<'dom>,
style: &Arc<ComputedValues>,
content_sizes: ContentSizesRequest,
) -> (BlockContainer, ContainsFloats, BoxContentSizes) {
match self {
IntermediateBlockContainer::Deferred { contents } => {
BlockContainer::construct(context, style, contents, content_sizes)
IntermediateBlockContainer::Deferred(contents) => {
BlockContainer::construct(context, node, style, contents, content_sizes)
},
IntermediateBlockContainer::InlineFormattingContext(ifc) => {
let content_sizes = content_sizes.compute(|| ifc.inline_content_sizes(context));

View file

@ -29,14 +29,16 @@ impl FloatContext {
impl FloatBox {
pub fn construct<'dom>(
context: &LayoutContext,
node: impl NodeExt<'dom>,
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents<impl NodeExt<'dom>>,
contents: Contents,
) -> Self {
let content_sizes = ContentSizesRequest::inline_if(!style.inline_size_is_length());
Self {
contents: IndependentFormattingContext::construct(
context,
node,
style,
display_inside,
contents,

View file

@ -16,6 +16,7 @@ use crate::ContainingBlock;
use app_units::Au;
use gfx::text::text_run::GlyphRun;
use servo_arc::Arc;
use style::dom::OpaqueNode;
use style::properties::ComputedValues;
use style::values::computed::{Length, LengthPercentage, Percentage};
use style::values::specified::text::TextAlignKeyword;
@ -38,6 +39,7 @@ pub(crate) enum InlineLevelBox {
#[derive(Debug)]
pub(crate) struct InlineBox {
pub tag: OpaqueNode,
pub style: Arc<ComputedValues>,
pub first_fragment: bool,
pub last_fragment: bool,
@ -47,6 +49,7 @@ pub(crate) struct InlineBox {
/// https://www.w3.org/TR/css-display-3/#css-text-run
#[derive(Debug)]
pub(crate) struct TextRun {
pub tag: OpaqueNode,
pub parent_style: Arc<ComputedValues>,
pub text: String,
}
@ -59,6 +62,7 @@ struct InlineNestingLevelState<'box_tree> {
}
struct PartialInlineBoxFragment<'box_tree> {
tag: OpaqueNode,
style: Arc<ComputedValues>,
start_corner: Vec2<Length>,
padding: Sides<Length>,
@ -371,6 +375,7 @@ impl InlineBox {
start_corner += &relative_adjustement(&style, ifc.containing_block)
}
PartialInlineBoxFragment {
tag: self.tag,
style,
start_corner,
padding,
@ -398,6 +403,7 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
at_line_break: bool,
) {
let mut fragment = BoxFragment {
tag: self.tag,
style: self.style.clone(),
children: std::mem::take(&mut nesting_level.fragments_so_far),
content_rect: Rect {
@ -465,6 +471,7 @@ fn layout_atomic<'box_tree>(
let fragments = replaced.make_fragments(&atomic.style, size.clone());
let content_rect = Rect { start_corner, size };
BoxFragment {
tag: atomic.tag,
style: atomic.style.clone(),
children: fragments,
content_rect,
@ -539,6 +546,7 @@ fn layout_atomic<'box_tree>(
},
};
BoxFragment {
tag: atomic.tag,
style: atomic.style.clone(),
children: independent_layout.fragments,
content_rect,
@ -685,6 +693,7 @@ impl TextRun {
ifc.current_nesting_level
.fragments_so_far
.push(Fragment::Text(TextFragment {
tag: self.tag,
parent_style: self.parent_style.clone(),
rect,
ascent: font_ascent.into(),

View file

@ -18,6 +18,7 @@ use crate::ContainingBlock;
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use rayon_croissant::ParallelIteratorExt;
use servo_arc::Arc;
use style::dom::OpaqueNode;
use style::properties::ComputedValues;
use style::values::computed::{Length, LengthOrAuto};
use style::Zero;
@ -44,6 +45,7 @@ pub(crate) enum BlockContainer {
#[derive(Debug)]
pub(crate) enum BlockLevelBox {
SameFormattingContextBlock {
tag: OpaqueNode,
style: Arc<ComputedValues>,
contents: BlockContainer,
},
@ -268,24 +270,27 @@ impl BlockLevelBox {
float_context: Option<&mut FloatContext>,
) -> Fragment {
match self {
BlockLevelBox::SameFormattingContextBlock { style, contents } => {
Fragment::Box(positioning_context.for_maybe_position_relative(
layout_context,
containing_block,
style,
|positioning_context| {
layout_in_flow_non_replaced_block_level(
layout_context,
positioning_context,
containing_block,
style,
NonReplacedContents::SameFormattingContextBlock(contents),
tree_rank,
float_context,
)
},
))
},
BlockLevelBox::SameFormattingContextBlock {
tag,
style,
contents,
} => Fragment::Box(positioning_context.for_maybe_position_relative(
layout_context,
containing_block,
style,
|positioning_context| {
layout_in_flow_non_replaced_block_level(
layout_context,
positioning_context,
containing_block,
*tag,
style,
NonReplacedContents::SameFormattingContextBlock(contents),
tree_rank,
float_context,
)
},
)),
BlockLevelBox::Independent(contents) => {
Fragment::Box(positioning_context.for_maybe_position_relative(
layout_context,
@ -294,6 +299,7 @@ impl BlockLevelBox {
|positioning_context| match contents.as_replaced() {
Ok(replaced) => layout_in_flow_replaced_block_level(
containing_block,
contents.tag,
&contents.style,
replaced,
),
@ -301,6 +307,7 @@ impl BlockLevelBox {
layout_context,
positioning_context,
containing_block,
contents.tag,
&contents.style,
NonReplacedContents::EstablishesAnIndependentFormattingContext(
non_replaced,
@ -339,6 +346,7 @@ fn layout_in_flow_non_replaced_block_level<'a>(
layout_context: &LayoutContext,
positioning_context: &mut PositioningContext<'a>,
containing_block: &ContainingBlock,
tag: OpaqueNode,
style: &Arc<ComputedValues>,
block_level_kind: NonReplacedContents<'a>,
tree_rank: usize,
@ -488,6 +496,7 @@ fn layout_in_flow_non_replaced_block_level<'a>(
},
};
BoxFragment {
tag,
style: style.clone(),
children: fragments,
content_rect,
@ -503,6 +512,7 @@ fn layout_in_flow_non_replaced_block_level<'a>(
/// https://drafts.csswg.org/css2/visudet.html#inline-replaced-height
fn layout_in_flow_replaced_block_level<'a>(
containing_block: &ContainingBlock,
tag: OpaqueNode,
style: &Arc<ComputedValues>,
replaced: &ReplacedContent,
) -> BoxFragment {
@ -536,6 +546,7 @@ fn layout_in_flow_replaced_block_level<'a>(
size,
};
BoxFragment {
tag,
style: style.clone(),
children: fragments,
content_rect,

View file

@ -61,19 +61,25 @@ fn construct_for_root_element<'dom>(
Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { inside, .. }) => inside,
};
let contents = replaced.map_or(Contents::OfElement(root_element), Contents::Replaced);
let contents = replaced.map_or(Contents::OfElement, Contents::Replaced);
if box_style.position.is_absolutely_positioned() {
(
ContainsFloats::No,
vec![Arc::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
AbsolutelyPositionedBox::construct(context, style, display_inside, contents),
AbsolutelyPositionedBox::construct(
context,
root_element,
style,
display_inside,
contents,
),
))],
)
} else if box_style.float.is_floating() {
(
ContainsFloats::Yes,
vec![Arc::new(BlockLevelBox::OutOfFlowFloatBox(
FloatBox::construct(context, style, display_inside, contents),
FloatBox::construct(context, root_element, style, display_inside, contents),
))],
)
} else {
@ -82,6 +88,7 @@ fn construct_for_root_element<'dom>(
vec![Arc::new(BlockLevelBox::Independent(
IndependentFormattingContext::construct(
context,
root_element,
style,
display_inside,
contents,

View file

@ -13,12 +13,14 @@ use crate::style_ext::DisplayInside;
use crate::ContainingBlock;
use servo_arc::Arc;
use std::convert::TryInto;
use style::dom::OpaqueNode;
use style::properties::ComputedValues;
use style::values::computed::Length;
/// https://drafts.csswg.org/css-display/#independent-formatting-context
#[derive(Debug)]
pub(crate) struct IndependentFormattingContext {
pub tag: OpaqueNode,
pub style: Arc<ComputedValues>,
/// If it was requested during construction
@ -54,9 +56,10 @@ enum NonReplacedIFCKind<'a> {
impl IndependentFormattingContext {
pub fn construct<'dom>(
context: &LayoutContext,
node: impl NodeExt<'dom>,
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents<impl NodeExt<'dom>>,
contents: Contents,
content_sizes: ContentSizesRequest,
) -> Self {
match contents.try_into() {
@ -64,11 +67,13 @@ impl IndependentFormattingContext {
DisplayInside::Flow | DisplayInside::FlowRoot => {
let (bfc, content_sizes) = BlockFormattingContext::construct(
context,
node,
&style,
non_replaced,
content_sizes,
);
Self {
tag: node.as_opaque(),
style,
content_sizes,
contents: IndependentFormattingContextContents::Flow(bfc),
@ -78,6 +83,7 @@ impl IndependentFormattingContext {
Err(replaced) => {
let content_sizes = content_sizes.compute(|| replaced.inline_content_sizes(&style));
Self {
tag: node.as_opaque(),
style,
content_sizes,
contents: IndependentFormattingContextContents::Replaced(replaced),

View file

@ -6,6 +6,7 @@ use crate::geom::flow_relative::{Rect, Sides, Vec2};
use gfx::text::glyph::GlyphStore;
use servo_arc::Arc as ServoArc;
use std::sync::Arc;
use style::dom::OpaqueNode;
use style::logical_geometry::WritingMode;
use style::properties::ComputedValues;
use style::values::computed::Length;
@ -20,6 +21,7 @@ pub(crate) enum Fragment {
}
pub(crate) struct BoxFragment {
pub tag: OpaqueNode,
pub style: ServoArc<ComputedValues>,
pub children: Vec<Fragment>,
@ -55,6 +57,7 @@ pub(crate) struct AnonymousFragment {
}
pub(crate) struct TextFragment {
pub tag: OpaqueNode,
pub parent_style: ServoArc<ComputedValues>,
pub rect: Rect<Length>,
pub ascent: Length,

View file

@ -60,9 +60,10 @@ pub(crate) enum AbsoluteBoxOffsets {
impl AbsolutelyPositionedBox {
pub fn construct<'dom>(
context: &LayoutContext,
node: impl NodeExt<'dom>,
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents<impl NodeExt<'dom>>,
contents: Contents,
) -> Self {
// "Shrink-to-fit" in https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
let content_sizes = ContentSizesRequest::inline_if(
@ -76,6 +77,7 @@ impl AbsolutelyPositionedBox {
Self {
contents: IndependentFormattingContext::construct(
context,
node,
style,
display_inside,
contents,
@ -469,6 +471,7 @@ impl<'box_tree> HoistedAbsolutelyPositionedBox<'box_tree> {
};
BoxFragment {
tag: self.absolutely_positioned_box.contents.tag,
style: style.clone(),
children: fragments,
content_rect,

View file

@ -10,7 +10,7 @@ ${helpers.predefined_type(
"cursor",
"Cursor",
"computed::Cursor::auto()",
engines="gecko servo-2013",
engines="gecko servo-2013 servo-2020",
initial_specified_value="specified::Cursor::auto()",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-ui/#cursor",
@ -22,7 +22,7 @@ ${helpers.predefined_type(
${helpers.single_keyword(
"pointer-events",
"auto none",
engines="gecko servo-2013",
engines="gecko servo-2013 servo-2020",
animation_value_type="discrete",
extra_gecko_values="visiblepainted visiblefill visiblestroke visible painted fill stroke all",
spec="https://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty",