mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Auto merge of #10252 - emilio:selection, r=mbrubeck
Implement ::selection pseudo-element It only supports `color` and `background`, for now, but it shouldn't be hard to add more properties (like text-shadow). r? @mbrubeck <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/10252) <!-- Reviewable:end -->
This commit is contained in:
commit
723989b9dd
12 changed files with 70 additions and 23 deletions
|
@ -217,6 +217,7 @@ impl InlineFragmentsAccumulator {
|
|||
address: node.opaque(),
|
||||
pseudo: node.get_pseudo_element_type().strip(),
|
||||
style: node.style().clone(),
|
||||
selected_style: node.selected_style().clone(),
|
||||
flags: InlineFragmentNodeFlags::empty(),
|
||||
}),
|
||||
bidi_control_chars: None,
|
||||
|
@ -360,6 +361,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
let fragment = Fragment::from_opaque_node_and_style(child_node.opaque(),
|
||||
PseudoElementType::Normal,
|
||||
style,
|
||||
child_node.selected_style().clone(),
|
||||
child_node.restyle_damage(),
|
||||
SpecificFragmentInfo::TableRow);
|
||||
let mut new_child: FlowRef = Arc::new(TableRowFlow::from_fragment(fragment));
|
||||
|
@ -373,6 +375,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
let fragment = Fragment::from_opaque_node_and_style(child_node.opaque(),
|
||||
PseudoElementType::Normal,
|
||||
style,
|
||||
child_node.selected_style().clone(),
|
||||
child_node.restyle_damage(),
|
||||
SpecificFragmentInfo::Table);
|
||||
let mut new_child: FlowRef = Arc::new(TableFlow::from_fragment(fragment));
|
||||
|
@ -387,6 +390,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
Fragment::from_opaque_node_and_style(child_node.opaque(),
|
||||
PseudoElementType::Normal,
|
||||
style,
|
||||
child_node.selected_style().clone(),
|
||||
child_node.restyle_damage(),
|
||||
SpecificFragmentInfo::TableWrapper);
|
||||
let mut new_child: FlowRef = Arc::new(TableWrapperFlow::from_fragment(fragment, None));
|
||||
|
@ -584,6 +588,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
let fragment = Fragment::from_opaque_node_and_style(whitespace_node,
|
||||
whitespace_pseudo,
|
||||
whitespace_style,
|
||||
node.selected_style().clone(),
|
||||
whitespace_damage,
|
||||
fragment_info);
|
||||
inline_fragment_accumulator.fragments.fragments.push_back(fragment);
|
||||
|
@ -713,6 +718,8 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
let mut style = (*style).clone();
|
||||
properties::modify_style_for_text(&mut style);
|
||||
|
||||
let selected_style = node.selected_style();
|
||||
|
||||
match text_content {
|
||||
TextContent::Text(string) => {
|
||||
let info = box UnscannedTextFragmentInfo::new(string, selection);
|
||||
|
@ -720,7 +727,8 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
fragments.fragments.push_back(Fragment::from_opaque_node_and_style(
|
||||
node.opaque(),
|
||||
node.get_pseudo_element_type().strip(),
|
||||
style.clone(),
|
||||
style,
|
||||
selected_style.clone(),
|
||||
node.restyle_damage(),
|
||||
specific_fragment_info))
|
||||
}
|
||||
|
@ -740,6 +748,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
node.opaque(),
|
||||
node.get_pseudo_element_type().strip(),
|
||||
style.clone(),
|
||||
selected_style.clone(),
|
||||
node.restyle_damage(),
|
||||
specific_fragment_info))
|
||||
}
|
||||
|
@ -823,6 +832,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
let kid_node = flow.as_block().fragment.node;
|
||||
let kid_pseudo = flow.as_block().fragment.pseudo.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 fragment_info = SpecificFragmentInfo::InlineAbsolute(
|
||||
InlineAbsoluteFragmentInfo::new(flow));
|
||||
|
@ -830,6 +840,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
kid_node,
|
||||
kid_pseudo,
|
||||
kid_style,
|
||||
kid_selected_style,
|
||||
kid_restyle_damage,
|
||||
fragment_info));
|
||||
fragment_accumulator.fragments
|
||||
|
@ -865,6 +876,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
let fragment = Fragment::from_opaque_node_and_style(whitespace_node,
|
||||
whitespace_pseudo,
|
||||
whitespace_style,
|
||||
node.selected_style().clone(),
|
||||
whitespace_damage,
|
||||
fragment_info);
|
||||
fragment_accumulator.fragments.fragments.push_back(fragment)
|
||||
|
@ -962,6 +974,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
let fragment = Fragment::from_opaque_node_and_style(node.opaque(),
|
||||
node.get_pseudo_element_type().strip(),
|
||||
modified_style.clone(),
|
||||
node.selected_style().clone(),
|
||||
node.restyle_damage(),
|
||||
fragment_info);
|
||||
|
||||
|
@ -994,6 +1007,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
let fragment = Fragment::from_opaque_node_and_style(node.opaque(),
|
||||
PseudoElementType::Normal,
|
||||
style,
|
||||
node.selected_style().clone(),
|
||||
node.restyle_damage(),
|
||||
fragment_info);
|
||||
|
||||
|
@ -1853,7 +1867,8 @@ fn control_chars_to_fragment(node: &InlineFragmentNodeInfo,
|
|||
properties::modify_style_for_text(&mut style);
|
||||
Fragment::from_opaque_node_and_style(node.address,
|
||||
node.pseudo,
|
||||
style,
|
||||
style.clone(),
|
||||
node.selected_style.clone(),
|
||||
restyle_damage,
|
||||
info)
|
||||
}
|
||||
|
|
|
@ -104,10 +104,6 @@ impl<'a> DisplayListBuildState<'a> {
|
|||
/// 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);
|
||||
|
||||
// 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
|
||||
// be positive. However, there is some confusion between the spec
|
||||
// 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.
|
||||
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(
|
||||
DisplayItem::SolidColorClass(box SolidColorDisplayItem {
|
||||
base: BaseDisplayItem::new(stacking_relative_border_box,
|
||||
|
@ -938,7 +936,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
|||
&*self.style,
|
||||
Cursor::DefaultCursor),
|
||||
&clip),
|
||||
color: SELECTION_BACKGROUND_COLOR.to_gfx_color()
|
||||
color: background_color.to_gfx_color(),
|
||||
}), display_list_section);
|
||||
}
|
||||
|
||||
|
@ -1116,11 +1114,14 @@ impl FragmentDisplayListBuilding for Fragment {
|
|||
//
|
||||
// NB: According to CSS-BACKGROUNDS, text shadows render in *reverse* order (front
|
||||
// to back).
|
||||
|
||||
// TODO(emilio): Allow changing more properties by ::selection
|
||||
let text_color = if text_fragment.selected() {
|
||||
SELECTION_FOREGROUND_COLOR
|
||||
self.selected_style().get_color().color
|
||||
} else {
|
||||
self.style().get_color().color
|
||||
};
|
||||
|
||||
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 color = self.style().resolve_color(text_shadow.color);
|
||||
|
|
|
@ -1286,6 +1286,7 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
|
|||
node.opaque(),
|
||||
PseudoElementType::Normal,
|
||||
style,
|
||||
node.selected_style().clone(),
|
||||
node.restyle_damage(),
|
||||
SpecificFragmentInfo::TableRow);
|
||||
Arc::new(TableRowFlow::from_fragment(fragment))
|
||||
|
@ -1298,6 +1299,7 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
|
|||
node.opaque(),
|
||||
PseudoElementType::Normal,
|
||||
style,
|
||||
node.selected_style().clone(),
|
||||
node.restyle_damage(),
|
||||
SpecificFragmentInfo::TableCell);
|
||||
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(),
|
||||
PseudoElementType::Normal,
|
||||
style,
|
||||
node.selected_style().clone(),
|
||||
node.restyle_damage(),
|
||||
SpecificFragmentInfo::Generic);
|
||||
Arc::new(BlockFlow::from_fragment(fragment, None))
|
||||
|
|
|
@ -85,6 +85,9 @@ pub struct Fragment {
|
|||
/// The CSS style of this fragment.
|
||||
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
|
||||
/// border, but not margin.
|
||||
///
|
||||
|
@ -798,6 +801,7 @@ impl Fragment {
|
|||
Fragment {
|
||||
node: node.opaque(),
|
||||
style: style,
|
||||
selected_style: node.selected_style().clone(),
|
||||
restyle_damage: restyle_damage,
|
||||
border_box: LogicalRect::zero(writing_mode),
|
||||
border_padding: LogicalMargin::zero(writing_mode),
|
||||
|
@ -815,6 +819,7 @@ impl Fragment {
|
|||
pub fn from_opaque_node_and_style(node: OpaqueNode,
|
||||
pseudo: PseudoElementType<()>,
|
||||
style: Arc<ServoComputedValues>,
|
||||
selected_style: Arc<ServoComputedValues>,
|
||||
mut restyle_damage: RestyleDamage,
|
||||
specific: SpecificFragmentInfo)
|
||||
-> Fragment {
|
||||
|
@ -825,6 +830,7 @@ impl Fragment {
|
|||
Fragment {
|
||||
node: node,
|
||||
style: style,
|
||||
selected_style: selected_style,
|
||||
restyle_damage: restyle_damage,
|
||||
border_box: LogicalRect::zero(writing_mode),
|
||||
border_padding: LogicalMargin::zero(writing_mode),
|
||||
|
@ -858,6 +864,7 @@ impl Fragment {
|
|||
Fragment {
|
||||
node: self.node,
|
||||
style: self.style.clone(),
|
||||
selected_style: self.selected_style.clone(),
|
||||
restyle_damage: restyle_damage,
|
||||
border_box: new_border_box,
|
||||
border_padding: self.border_padding,
|
||||
|
@ -1286,6 +1293,11 @@ impl Fragment {
|
|||
&*self.style
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn selected_style(&self) -> &ServoComputedValues {
|
||||
&*self.selected_style
|
||||
}
|
||||
|
||||
pub fn white_space(&self) -> white_space::T {
|
||||
self.style().get_inheritedtext().white_space
|
||||
}
|
||||
|
|
|
@ -438,6 +438,7 @@ fn render_text(layout_context: &LayoutContext,
|
|||
box UnscannedTextFragmentInfo::new(string, None));
|
||||
fragments.push_back(Fragment::from_opaque_node_and_style(node,
|
||||
pseudo,
|
||||
style.clone(),
|
||||
style,
|
||||
RestyleDamage::rebuild_and_reflow(),
|
||||
info));
|
||||
|
|
|
@ -1854,6 +1854,7 @@ impl fmt::Debug for InlineFlow {
|
|||
pub struct InlineFragmentNodeInfo {
|
||||
pub address: OpaqueNode,
|
||||
pub style: Arc<ServoComputedValues>,
|
||||
pub selected_style: Arc<ServoComputedValues>,
|
||||
pub pseudo: PseudoElementType<()>,
|
||||
pub flags: InlineFragmentNodeFlags,
|
||||
}
|
||||
|
|
|
@ -534,11 +534,12 @@ pub fn process_resolved_style_request<N: LayoutNode>(
|
|||
requested_node: N, pseudo: &Option<PseudoElement>,
|
||||
property: &Atom, layout_root: &mut FlowRef) -> Option<String> {
|
||||
let layout_node = requested_node.to_threadsafe();
|
||||
let layout_node = match pseudo {
|
||||
&Some(PseudoElement::Before) => layout_node.get_before_pseudo(),
|
||||
&Some(PseudoElement::After) => layout_node.get_after_pseudo(),
|
||||
&Some(PseudoElement::DetailsSummary) => layout_node.get_details_summary_pseudo(),
|
||||
&Some(PseudoElement::DetailsContent) => layout_node.get_details_content_pseudo(),
|
||||
let layout_node = match *pseudo {
|
||||
Some(PseudoElement::Before) => layout_node.get_before_pseudo(),
|
||||
Some(PseudoElement::After) => layout_node.get_after_pseudo(),
|
||||
Some(PseudoElement::DetailsSummary) => layout_node.get_details_summary_pseudo(),
|
||||
Some(PseudoElement::DetailsContent) => layout_node.get_details_content_pseudo(),
|
||||
Some(PseudoElement::Selection) => None,
|
||||
_ => Some(layout_node)
|
||||
};
|
||||
|
||||
|
|
|
@ -368,6 +368,7 @@ impl TextRunScanner {
|
|||
let new_fragment = old_fragment.transform(
|
||||
bounding_box_size,
|
||||
SpecificFragmentInfo::ScannedText(new_text_fragment_info));
|
||||
|
||||
out_fragments.push(new_fragment)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
fn get_details_summary_pseudo(&self) -> Option<Self> {
|
||||
if self.is_element() &&
|
||||
self.as_element().get_local_name() == &atom!("details") &&
|
||||
self.as_element().get_namespace() == &ns!(html) {
|
||||
self.as_element().get_local_name() == &atom!("details") &&
|
||||
self.as_element().get_namespace() == &ns!(html) {
|
||||
self.borrow_layout_data().unwrap()
|
||||
.style_data.per_pseudo
|
||||
.get(&PseudoElement::DetailsSummary)
|
||||
|
@ -721,8 +724,8 @@ pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq {
|
|||
#[inline]
|
||||
fn get_details_content_pseudo(&self) -> Option<Self> {
|
||||
if self.is_element() &&
|
||||
self.as_element().get_local_name() == &atom!("details") &&
|
||||
self.as_element().get_namespace() == &ns!(html) {
|
||||
self.as_element().get_local_name() == &atom!("details") &&
|
||||
self.as_element().get_namespace() == &ns!(html) {
|
||||
self.borrow_layout_data().unwrap()
|
||||
.style_data.per_pseudo
|
||||
.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.
|
||||
///
|
||||
/// Unlike the version on TNode, this handles pseudo-elements.
|
||||
|
@ -1015,7 +1025,7 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> {
|
|||
}
|
||||
|
||||
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 style = if self.pseudo.is_before() {
|
||||
|
@ -1136,7 +1146,6 @@ impl<ConcreteNode> Iterator for ThreadSafeLayoutNodeChildrenIterator<ConcreteNod
|
|||
type Item = ConcreteNode;
|
||||
fn next(&mut self) -> Option<ConcreteNode> {
|
||||
match self.parent_node.get_pseudo_element_type() {
|
||||
|
||||
PseudoElementType::Before(_) | PseudoElementType::After(_) => None,
|
||||
|
||||
PseudoElementType::DetailsSummary(_) => {
|
||||
|
@ -1162,8 +1171,8 @@ impl<ConcreteNode> Iterator for ThreadSafeLayoutNodeChildrenIterator<ConcreteNod
|
|||
let node = self.current_node.clone();
|
||||
let node = node.and_then(|node| {
|
||||
if node.is_element() &&
|
||||
node.as_element().get_local_name() == &atom!("summary") &&
|
||||
node.as_element().get_namespace() == &ns!(html) {
|
||||
node.as_element().get_local_name() == &atom!("summary") &&
|
||||
node.as_element().get_namespace() == &ns!(html) {
|
||||
unsafe { node.dangerous_next_sibling() }
|
||||
} else {
|
||||
Some(node)
|
||||
|
|
|
@ -26,6 +26,7 @@ pub trait SelectorImplExt : SelectorImpl + Sized {
|
|||
pub enum PseudoElement {
|
||||
Before,
|
||||
After,
|
||||
Selection,
|
||||
DetailsSummary,
|
||||
DetailsContent,
|
||||
}
|
||||
|
@ -105,6 +106,7 @@ impl SelectorImpl for ServoSelectorImpl {
|
|||
let pseudo_element = match_ignore_ascii_case! { name,
|
||||
"before" => Before,
|
||||
"after" => After,
|
||||
"selection" => Selection,
|
||||
"-servo-details-summary" => if context.in_user_agent_stylesheet {
|
||||
DetailsSummary
|
||||
} else {
|
||||
|
@ -136,6 +138,7 @@ impl SelectorImplExt for ServoSelectorImpl {
|
|||
fun(PseudoElement::After);
|
||||
fun(PseudoElement::DetailsContent);
|
||||
fun(PseudoElement::DetailsSummary);
|
||||
fun(PseudoElement::Selection);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::mem::size_of;
|
|||
|
||||
#[test]
|
||||
fn test_size_of_fragment() {
|
||||
let expected = 160;
|
||||
let expected = 168;
|
||||
let actual = size_of::<Fragment>();
|
||||
|
||||
if actual < expected {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#![cfg_attr(test, feature(plugin, custom_derive, heap_api))]
|
||||
#![cfg_attr(test, feature(plugin, custom_derive))]
|
||||
#![cfg_attr(test, plugin(plugins))]
|
||||
#![feature(alloc)]
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue