Implement ::selection pseudo-element

This commit is contained in:
Emilio Cobos Álvarez 2016-03-28 23:08:32 +02:00
parent 4aa1da3211
commit 92f39ea5cf
10 changed files with 67 additions and 21 deletions

View file

@ -360,6 +360,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
let fragment = Fragment::from_opaque_node_and_style(child_node.opaque(), let fragment = Fragment::from_opaque_node_and_style(child_node.opaque(),
PseudoElementType::Normal, PseudoElementType::Normal,
style, style,
child_node.selected_style().clone(),
child_node.restyle_damage(), child_node.restyle_damage(),
SpecificFragmentInfo::TableRow); SpecificFragmentInfo::TableRow);
let mut new_child: FlowRef = Arc::new(TableRowFlow::from_fragment(fragment)); let mut new_child: FlowRef = Arc::new(TableRowFlow::from_fragment(fragment));
@ -373,6 +374,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
let fragment = Fragment::from_opaque_node_and_style(child_node.opaque(), let fragment = Fragment::from_opaque_node_and_style(child_node.opaque(),
PseudoElementType::Normal, PseudoElementType::Normal,
style, style,
child_node.selected_style().clone(),
child_node.restyle_damage(), child_node.restyle_damage(),
SpecificFragmentInfo::Table); SpecificFragmentInfo::Table);
let mut new_child: FlowRef = Arc::new(TableFlow::from_fragment(fragment)); let mut new_child: FlowRef = Arc::new(TableFlow::from_fragment(fragment));
@ -387,6 +389,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
Fragment::from_opaque_node_and_style(child_node.opaque(), Fragment::from_opaque_node_and_style(child_node.opaque(),
PseudoElementType::Normal, PseudoElementType::Normal,
style, style,
child_node.selected_style().clone(),
child_node.restyle_damage(), child_node.restyle_damage(),
SpecificFragmentInfo::TableWrapper); SpecificFragmentInfo::TableWrapper);
let mut new_child: FlowRef = Arc::new(TableWrapperFlow::from_fragment(fragment, None)); let mut new_child: FlowRef = Arc::new(TableWrapperFlow::from_fragment(fragment, None));
@ -583,6 +586,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
properties::modify_style_for_text(&mut whitespace_style); properties::modify_style_for_text(&mut whitespace_style);
let fragment = Fragment::from_opaque_node_and_style(whitespace_node, let fragment = Fragment::from_opaque_node_and_style(whitespace_node,
whitespace_pseudo, whitespace_pseudo,
whitespace_style.clone(),
whitespace_style, whitespace_style,
whitespace_damage, whitespace_damage,
fragment_info); fragment_info);
@ -713,6 +717,8 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
let mut style = (*style).clone(); let mut style = (*style).clone();
properties::modify_style_for_text(&mut style); properties::modify_style_for_text(&mut style);
let selected_style = node.selected_style().clone();
match text_content { match text_content {
TextContent::Text(string) => { TextContent::Text(string) => {
let info = box UnscannedTextFragmentInfo::new(string, selection); let info = box UnscannedTextFragmentInfo::new(string, selection);
@ -720,7 +726,8 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
fragments.fragments.push_back(Fragment::from_opaque_node_and_style( fragments.fragments.push_back(Fragment::from_opaque_node_and_style(
node.opaque(), node.opaque(),
node.get_pseudo_element_type().strip(), node.get_pseudo_element_type().strip(),
style.clone(), style,
selected_style,
node.restyle_damage(), node.restyle_damage(),
specific_fragment_info)) specific_fragment_info))
} }
@ -740,6 +747,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
node.opaque(), node.opaque(),
node.get_pseudo_element_type().strip(), node.get_pseudo_element_type().strip(),
style.clone(), style.clone(),
selected_style.clone(),
node.restyle_damage(), node.restyle_damage(),
specific_fragment_info)) specific_fragment_info))
} }
@ -823,6 +831,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
let kid_node = flow.as_block().fragment.node; let kid_node = flow.as_block().fragment.node;
let kid_pseudo = flow.as_block().fragment.pseudo.clone(); let kid_pseudo = flow.as_block().fragment.pseudo.clone();
let kid_style = flow.as_block().fragment.style.clone(); let kid_style = flow.as_block().fragment.style.clone();
let kid_selected_style = flow.as_block().fragment.selected_style.clone();
let kid_restyle_damage = flow.as_block().fragment.restyle_damage; let kid_restyle_damage = flow.as_block().fragment.restyle_damage;
let fragment_info = SpecificFragmentInfo::InlineAbsolute( let fragment_info = SpecificFragmentInfo::InlineAbsolute(
InlineAbsoluteFragmentInfo::new(flow)); InlineAbsoluteFragmentInfo::new(flow));
@ -830,6 +839,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
kid_node, kid_node,
kid_pseudo, kid_pseudo,
kid_style, kid_style,
kid_selected_style,
kid_restyle_damage, kid_restyle_damage,
fragment_info)); fragment_info));
fragment_accumulator.fragments fragment_accumulator.fragments
@ -864,6 +874,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
properties::modify_style_for_text(&mut whitespace_style); properties::modify_style_for_text(&mut whitespace_style);
let fragment = Fragment::from_opaque_node_and_style(whitespace_node, let fragment = Fragment::from_opaque_node_and_style(whitespace_node,
whitespace_pseudo, whitespace_pseudo,
whitespace_style.clone(),
whitespace_style, whitespace_style,
whitespace_damage, whitespace_damage,
fragment_info); fragment_info);
@ -962,6 +973,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
let fragment = Fragment::from_opaque_node_and_style(node.opaque(), let fragment = Fragment::from_opaque_node_and_style(node.opaque(),
node.get_pseudo_element_type().strip(), node.get_pseudo_element_type().strip(),
modified_style.clone(), modified_style.clone(),
node.selected_style().clone(),
node.restyle_damage(), node.restyle_damage(),
fragment_info); fragment_info);
@ -994,6 +1006,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
let fragment = Fragment::from_opaque_node_and_style(node.opaque(), let fragment = Fragment::from_opaque_node_and_style(node.opaque(),
PseudoElementType::Normal, PseudoElementType::Normal,
style, style,
node.selected_style().clone(),
node.restyle_damage(), node.restyle_damage(),
fragment_info); fragment_info);
@ -1853,6 +1866,8 @@ fn control_chars_to_fragment(node: &InlineFragmentNodeInfo,
properties::modify_style_for_text(&mut style); properties::modify_style_for_text(&mut style);
Fragment::from_opaque_node_and_style(node.address, Fragment::from_opaque_node_and_style(node.address,
node.pseudo, node.pseudo,
style.clone(),
// TODO(emilio): Selected style?
style, style,
restyle_damage, restyle_damage,
info) info)

View file

@ -104,10 +104,6 @@ impl<'a> DisplayListBuildState<'a> {
/// The logical width of an insertion point: at the moment, a one-pixel-wide line. /// The logical width of an insertion point: at the moment, a one-pixel-wide line.
const INSERTION_POINT_LOGICAL_WIDTH: Au = Au(1 * AU_PER_PX); const INSERTION_POINT_LOGICAL_WIDTH: Au = Au(1 * AU_PER_PX);
// Colors for selected text. TODO (#8077): Use the ::selection pseudo-element to set these.
const SELECTION_FOREGROUND_COLOR: RGBA = RGBA { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 };
const SELECTION_BACKGROUND_COLOR: RGBA = RGBA { red: 0.69, green: 0.84, blue: 1.0, alpha: 1.0 };
// TODO(gw): The transforms spec says that perspective length must // TODO(gw): The transforms spec says that perspective length must
// be positive. However, there is some confusion between the spec // be positive. However, there is some confusion between the spec
// and browser implementations as to handling the case of 0 for the // and browser implementations as to handling the case of 0 for the
@ -931,6 +927,8 @@ impl FragmentDisplayListBuilding for Fragment {
// //
// TODO: Allow non-text fragments to be selected too. // TODO: Allow non-text fragments to be selected too.
if scanned_text_fragment_info.selected() { if scanned_text_fragment_info.selected() {
let style = self.selected_style();
let background_color = style.resolve_color(style.get_background().background_color);
state.add_display_item( state.add_display_item(
DisplayItem::SolidColorClass(box SolidColorDisplayItem { DisplayItem::SolidColorClass(box SolidColorDisplayItem {
base: BaseDisplayItem::new(stacking_relative_border_box, base: BaseDisplayItem::new(stacking_relative_border_box,
@ -938,7 +936,7 @@ impl FragmentDisplayListBuilding for Fragment {
&*self.style, &*self.style,
Cursor::DefaultCursor), Cursor::DefaultCursor),
&clip), &clip),
color: SELECTION_BACKGROUND_COLOR.to_gfx_color() color: background_color.to_gfx_color(),
}), display_list_section); }), display_list_section);
} }
@ -1116,11 +1114,14 @@ impl FragmentDisplayListBuilding for Fragment {
// //
// NB: According to CSS-BACKGROUNDS, text shadows render in *reverse* order (front // NB: According to CSS-BACKGROUNDS, text shadows render in *reverse* order (front
// to back). // to back).
// TODO(emilio): Allow changing more properties by ::selection
let text_color = if text_fragment.selected() { let text_color = if text_fragment.selected() {
SELECTION_FOREGROUND_COLOR self.selected_style().get_color().color
} else { } else {
self.style().get_color().color self.style().get_color().color
}; };
for text_shadow in self.style.get_effects().text_shadow.0.iter().rev() { for text_shadow in self.style.get_effects().text_shadow.0.iter().rev() {
let offset = &Point2D::new(text_shadow.offset_x, text_shadow.offset_y); let offset = &Point2D::new(text_shadow.offset_x, text_shadow.offset_y);
let color = self.style().resolve_color(text_shadow.color); let color = self.style().resolve_color(text_shadow.color);

View file

@ -1286,6 +1286,7 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
node.opaque(), node.opaque(),
PseudoElementType::Normal, PseudoElementType::Normal,
style, style,
node.selected_style().clone(),
node.restyle_damage(), node.restyle_damage(),
SpecificFragmentInfo::TableRow); SpecificFragmentInfo::TableRow);
Arc::new(TableRowFlow::from_fragment(fragment)) Arc::new(TableRowFlow::from_fragment(fragment))
@ -1298,6 +1299,7 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
node.opaque(), node.opaque(),
PseudoElementType::Normal, PseudoElementType::Normal,
style, style,
node.selected_style().clone(),
node.restyle_damage(), node.restyle_damage(),
SpecificFragmentInfo::TableCell); SpecificFragmentInfo::TableCell);
let hide = node.style().get_inheritedtable().empty_cells == empty_cells::T::hide; let hide = node.style().get_inheritedtable().empty_cells == empty_cells::T::hide;
@ -1308,6 +1310,7 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
Fragment::from_opaque_node_and_style(node.opaque(), Fragment::from_opaque_node_and_style(node.opaque(),
PseudoElementType::Normal, PseudoElementType::Normal,
style, style,
node.selected_style().clone(),
node.restyle_damage(), node.restyle_damage(),
SpecificFragmentInfo::Generic); SpecificFragmentInfo::Generic);
Arc::new(BlockFlow::from_fragment(fragment, None)) Arc::new(BlockFlow::from_fragment(fragment, None))

View file

@ -85,6 +85,9 @@ pub struct Fragment {
/// The CSS style of this fragment. /// The CSS style of this fragment.
pub style: Arc<ServoComputedValues>, pub style: Arc<ServoComputedValues>,
/// The CSS style of this fragment when it's selected
pub selected_style: Arc<ServoComputedValues>,
/// The position of this fragment relative to its owning flow. The size includes padding and /// The position of this fragment relative to its owning flow. The size includes padding and
/// border, but not margin. /// border, but not margin.
/// ///
@ -798,6 +801,7 @@ impl Fragment {
Fragment { Fragment {
node: node.opaque(), node: node.opaque(),
style: style, style: style,
selected_style: node.selected_style().clone(),
restyle_damage: restyle_damage, restyle_damage: restyle_damage,
border_box: LogicalRect::zero(writing_mode), border_box: LogicalRect::zero(writing_mode),
border_padding: LogicalMargin::zero(writing_mode), border_padding: LogicalMargin::zero(writing_mode),
@ -815,6 +819,7 @@ impl Fragment {
pub fn from_opaque_node_and_style(node: OpaqueNode, pub fn from_opaque_node_and_style(node: OpaqueNode,
pseudo: PseudoElementType<()>, pseudo: PseudoElementType<()>,
style: Arc<ServoComputedValues>, style: Arc<ServoComputedValues>,
selected_style: Arc<ServoComputedValues>,
mut restyle_damage: RestyleDamage, mut restyle_damage: RestyleDamage,
specific: SpecificFragmentInfo) specific: SpecificFragmentInfo)
-> Fragment { -> Fragment {
@ -825,6 +830,7 @@ impl Fragment {
Fragment { Fragment {
node: node, node: node,
style: style, style: style,
selected_style: selected_style,
restyle_damage: restyle_damage, restyle_damage: restyle_damage,
border_box: LogicalRect::zero(writing_mode), border_box: LogicalRect::zero(writing_mode),
border_padding: LogicalMargin::zero(writing_mode), border_padding: LogicalMargin::zero(writing_mode),
@ -858,6 +864,7 @@ impl Fragment {
Fragment { Fragment {
node: self.node, node: self.node,
style: self.style.clone(), style: self.style.clone(),
selected_style: self.selected_style.clone(),
restyle_damage: restyle_damage, restyle_damage: restyle_damage,
border_box: new_border_box, border_box: new_border_box,
border_padding: self.border_padding, border_padding: self.border_padding,
@ -1286,6 +1293,11 @@ impl Fragment {
&*self.style &*self.style
} }
#[inline(always)]
pub fn selected_style(&self) -> &ServoComputedValues {
&*self.selected_style
}
pub fn white_space(&self) -> white_space::T { pub fn white_space(&self) -> white_space::T {
self.style().get_inheritedtext().white_space self.style().get_inheritedtext().white_space
} }

View file

@ -438,6 +438,7 @@ fn render_text(layout_context: &LayoutContext,
box UnscannedTextFragmentInfo::new(string, None)); box UnscannedTextFragmentInfo::new(string, None));
fragments.push_back(Fragment::from_opaque_node_and_style(node, fragments.push_back(Fragment::from_opaque_node_and_style(node,
pseudo, pseudo,
style.clone(),
style, style,
RestyleDamage::rebuild_and_reflow(), RestyleDamage::rebuild_and_reflow(),
info)); info));

View file

@ -534,11 +534,12 @@ pub fn process_resolved_style_request<N: LayoutNode>(
requested_node: N, pseudo: &Option<PseudoElement>, requested_node: N, pseudo: &Option<PseudoElement>,
property: &Atom, layout_root: &mut FlowRef) -> Option<String> { property: &Atom, layout_root: &mut FlowRef) -> Option<String> {
let layout_node = requested_node.to_threadsafe(); let layout_node = requested_node.to_threadsafe();
let layout_node = match pseudo { let layout_node = match *pseudo {
&Some(PseudoElement::Before) => layout_node.get_before_pseudo(), Some(PseudoElement::Before) => layout_node.get_before_pseudo(),
&Some(PseudoElement::After) => layout_node.get_after_pseudo(), Some(PseudoElement::After) => layout_node.get_after_pseudo(),
&Some(PseudoElement::DetailsSummary) => layout_node.get_details_summary_pseudo(), Some(PseudoElement::DetailsSummary) => layout_node.get_details_summary_pseudo(),
&Some(PseudoElement::DetailsContent) => layout_node.get_details_content_pseudo(), Some(PseudoElement::DetailsContent) => layout_node.get_details_content_pseudo(),
Some(PseudoElement::Selection) => None,
_ => Some(layout_node) _ => Some(layout_node)
}; };

View file

@ -368,6 +368,7 @@ impl TextRunScanner {
let new_fragment = old_fragment.transform( let new_fragment = old_fragment.transform(
bounding_box_size, bounding_box_size,
SpecificFragmentInfo::ScannedText(new_text_fragment_info)); SpecificFragmentInfo::ScannedText(new_text_fragment_info));
out_fragments.push(new_fragment) out_fragments.push(new_fragment)
} }
} }

View file

@ -702,11 +702,14 @@ pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq {
}) })
} }
// TODO(emilio): Since the ::-details-* pseudos are internal, just affecting one element, and
// only changing `display` property when the element `open` attribute changes, this should be
// eligible for not being cascaded eagerly, reading the display property from layout instead.
#[inline] #[inline]
fn get_details_summary_pseudo(&self) -> Option<Self> { fn get_details_summary_pseudo(&self) -> Option<Self> {
if self.is_element() && if self.is_element() &&
self.as_element().get_local_name() == &atom!("details") && self.as_element().get_local_name() == &atom!("details") &&
self.as_element().get_namespace() == &ns!(html) { self.as_element().get_namespace() == &ns!(html) {
self.borrow_layout_data().unwrap() self.borrow_layout_data().unwrap()
.style_data.per_pseudo .style_data.per_pseudo
.get(&PseudoElement::DetailsSummary) .get(&PseudoElement::DetailsSummary)
@ -721,8 +724,8 @@ pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq {
#[inline] #[inline]
fn get_details_content_pseudo(&self) -> Option<Self> { fn get_details_content_pseudo(&self) -> Option<Self> {
if self.is_element() && if self.is_element() &&
self.as_element().get_local_name() == &atom!("details") && self.as_element().get_local_name() == &atom!("details") &&
self.as_element().get_namespace() == &ns!(html) { self.as_element().get_namespace() == &ns!(html) {
self.borrow_layout_data().unwrap() self.borrow_layout_data().unwrap()
.style_data.per_pseudo .style_data.per_pseudo
.get(&PseudoElement::DetailsContent) .get(&PseudoElement::DetailsContent)
@ -764,6 +767,13 @@ pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq {
}) })
} }
#[inline]
fn selected_style(&self) -> Ref<Arc<ServoComputedValues>> {
Ref::map(self.borrow_layout_data().unwrap(), |data| {
data.style_data.per_pseudo.get(&PseudoElement::Selection).unwrap_or(data.style_data.style.as_ref().unwrap())
})
}
/// Removes the style from this node. /// Removes the style from this node.
/// ///
/// Unlike the version on TNode, this handles pseudo-elements. /// Unlike the version on TNode, this handles pseudo-elements.
@ -1015,7 +1025,7 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> {
} }
fn text_content(&self) -> TextContent { fn text_content(&self) -> TextContent {
if self.pseudo != PseudoElementType::Normal { if self.pseudo.is_before_or_after() {
let data = &self.borrow_layout_data().unwrap().style_data; let data = &self.borrow_layout_data().unwrap().style_data;
let style = if self.pseudo.is_before() { let style = if self.pseudo.is_before() {
@ -1136,7 +1146,6 @@ impl<ConcreteNode> Iterator for ThreadSafeLayoutNodeChildrenIterator<ConcreteNod
type Item = ConcreteNode; type Item = ConcreteNode;
fn next(&mut self) -> Option<ConcreteNode> { fn next(&mut self) -> Option<ConcreteNode> {
match self.parent_node.get_pseudo_element_type() { match self.parent_node.get_pseudo_element_type() {
PseudoElementType::Before(_) | PseudoElementType::After(_) => None, PseudoElementType::Before(_) | PseudoElementType::After(_) => None,
PseudoElementType::DetailsSummary(_) => { PseudoElementType::DetailsSummary(_) => {
@ -1162,8 +1171,8 @@ impl<ConcreteNode> Iterator for ThreadSafeLayoutNodeChildrenIterator<ConcreteNod
let node = self.current_node.clone(); let node = self.current_node.clone();
let node = node.and_then(|node| { let node = node.and_then(|node| {
if node.is_element() && if node.is_element() &&
node.as_element().get_local_name() == &atom!("summary") && node.as_element().get_local_name() == &atom!("summary") &&
node.as_element().get_namespace() == &ns!(html) { node.as_element().get_namespace() == &ns!(html) {
unsafe { node.dangerous_next_sibling() } unsafe { node.dangerous_next_sibling() }
} else { } else {
Some(node) Some(node)

View file

@ -26,6 +26,7 @@ pub trait SelectorImplExt : SelectorImpl + Sized {
pub enum PseudoElement { pub enum PseudoElement {
Before, Before,
After, After,
Selection,
DetailsSummary, DetailsSummary,
DetailsContent, DetailsContent,
} }
@ -105,6 +106,7 @@ impl SelectorImpl for ServoSelectorImpl {
let pseudo_element = match_ignore_ascii_case! { name, let pseudo_element = match_ignore_ascii_case! { name,
"before" => Before, "before" => Before,
"after" => After, "after" => After,
"selection" => Selection,
"-servo-details-summary" => if context.in_user_agent_stylesheet { "-servo-details-summary" => if context.in_user_agent_stylesheet {
DetailsSummary DetailsSummary
} else { } else {
@ -136,6 +138,7 @@ impl SelectorImplExt for ServoSelectorImpl {
fun(PseudoElement::After); fun(PseudoElement::After);
fun(PseudoElement::DetailsContent); fun(PseudoElement::DetailsContent);
fun(PseudoElement::DetailsSummary); fun(PseudoElement::DetailsSummary);
fun(PseudoElement::Selection);
} }
#[inline] #[inline]

View file

@ -7,7 +7,7 @@ use std::mem::size_of;
#[test] #[test]
fn test_size_of_fragment() { fn test_size_of_fragment() {
let expected = 160; let expected = 168;
let actual = size_of::<Fragment>(); let actual = size_of::<Fragment>();
if actual < expected { if actual < expected {