Auto merge of #17912 - bzbarsky:stylo-first-line, r=emilio

Implement ::first-line support in stylo

<!-- Please describe your changes on the following line: -->

Fixes Gecko bug 1324619.

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix https://bugzilla.mozilla.org/show_bug.cgi?id=1324619

<!-- Either: -->
- [ ] There are tests for these changes OR
- [X] These changes do not require tests because there are Gecko tests

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17912)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-07-28 22:51:20 -05:00 committed by GitHub
commit ed75bcae75
9 changed files with 204 additions and 37 deletions

View file

@ -191,7 +191,7 @@ pub struct CascadeInputs {
impl CascadeInputs {
/// Construct inputs from previous cascade results, if any.
pub fn new_from_style(style: &Arc<ComputedValues>) -> Self {
pub fn new_from_style(style: &ComputedValues) -> Self {
CascadeInputs {
rules: style.rules.clone(),
visited_rules: style.get_visited_style().and_then(|v| v.rules.clone()),

View file

@ -204,6 +204,15 @@ impl PerDocumentStyleDataImpl {
pub fn clear_stylist(&mut self) {
self.stylist.clear();
}
/// Returns whether visited links are enabled.
fn visited_links_enabled(&self) -> bool {
unsafe { bindings::Gecko_AreVisitedLinksEnabled() }
}
/// Returns whether visited styles are enabled.
pub fn visited_styles_enabled(&self) -> bool {
self.visited_links_enabled() && !self.is_private_browsing_enabled()
}
}
unsafe impl HasFFI for PerDocumentStyleData {

View file

@ -2817,6 +2817,16 @@ extern "C" {
set: RawServoStyleSetBorrowed)
-> ServoStyleContextStrong;
}
extern "C" {
pub fn Servo_ReparentStyle(style_to_reparent: ServoStyleContextBorrowed,
parent_style: ServoStyleContextBorrowed,
parent_style_ignoring_first_line:
ServoStyleContextBorrowed,
layout_parent_style: ServoStyleContextBorrowed,
element: RawGeckoElementBorrowedOrNull,
set: RawServoStyleSetBorrowed)
-> ServoStyleContextStrong;
}
extern "C" {
pub fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed,
set: RawServoStyleSetBorrowed,

View file

@ -87,6 +87,12 @@ impl PseudoElement {
*self == PseudoElement::FirstLetter
}
/// Whether this pseudo-element is ::first-line.
#[inline]
pub fn is_first_line(&self) -> bool {
*self == PseudoElement::FirstLine
}
/// Whether this pseudo-element is ::-moz-fieldset-content.
#[inline]
pub fn is_fieldset_content(&self) -> bool {

View file

@ -827,7 +827,7 @@ pub trait MatchMethods : TElement {
return StyleDifference::new(RestyleDamage::empty(), StyleChange::Unchanged)
}
if pseudo.map_or(false, |p| p.is_first_letter()) {
if pseudo.map_or(false, |p| p.is_first_letter() || p.is_first_line()) {
// No one cares about this pseudo, and we've checked above that
// we're not switching from a "cares" to a "doesn't care" state
// or vice versa.

View file

@ -41,6 +41,9 @@ use gecko_bindings::bindings::{Gecko_ResetFilters, Gecko_CopyFiltersFrom};
use gecko_bindings::bindings::RawGeckoPresContextBorrowed;
use gecko_bindings::structs;
use gecko_bindings::structs::nsCSSPropertyID;
use gecko_bindings::structs::mozilla::CSSPseudoElementType;
use gecko_bindings::structs::mozilla::CSSPseudoElementType_InheritingAnonBox;
use gecko_bindings::structs::root::NS_STYLE_CONTEXT_TYPE_SHIFT;
use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut};
use gecko::values::convert_nscolor_to_rgba;
use gecko::values::convert_rgba_to_nscolor;
@ -133,6 +136,18 @@ impl ComputedValues {
let atom = Atom::from(atom);
PseudoElement::from_atom(&atom)
}
fn get_pseudo_type(&self) -> CSSPseudoElementType {
let bits = (self.0)._base.mBits;
let our_type = bits >> NS_STYLE_CONTEXT_TYPE_SHIFT;
unsafe { transmute(our_type as u8) }
}
pub fn is_anon_box(&self) -> bool {
let our_type = self.get_pseudo_type();
return our_type == CSSPseudoElementType_InheritingAnonBox ||
our_type == CSSPseudoElementType::NonInheritingAnonBox;
}
}
impl Drop for ComputedValues {

View file

@ -42,6 +42,8 @@ pub enum PseudoElement {
Selection,
// If/when :first-letter is added, update is_first_letter accordingly.
// If/when :first-line is added, update is_first_line accordingly.
// If/when ::first-letter, ::first-line, or ::placeholder are added, adjust
// our property_restriction implementation to do property filtering for
// them. Also, make sure the UA sheet has the !important rules some of the
@ -125,6 +127,12 @@ impl PseudoElement {
false
}
/// Whether the current pseudo element is :first-line
#[inline]
pub fn is_first_line(&self) -> bool {
false
}
/// Whether this pseudo-element is eagerly-cascaded.
#[inline]
pub fn is_eager(&self) -> bool {

View file

@ -20,6 +20,7 @@ use properties::{self, CascadeFlags, ComputedValues};
use properties::{AnimationRules, PropertyDeclarationBlock};
#[cfg(feature = "servo")]
use properties::INHERIT_ALL;
use properties::IS_LINK;
use rule_tree::{CascadeLevel, RuleTree, StyleSource};
use selector_map::{SelectorMap, SelectorMapEntry};
use selector_parser::{SelectorImpl, PseudoElement};
@ -727,6 +728,47 @@ impl Stylist {
return None
}
// FIXME(emilio): The lack of layout_parent_style here could be
// worrying, but we're probably dropping the display fixup for
// pseudos other than before and after, so it's probably ok.
//
// (Though the flags don't indicate so!)
Some(self.compute_style_with_inputs(inputs,
Some(pseudo),
guards,
parent_style,
parent_style,
parent_style,
font_metrics,
CascadeFlags::empty()))
}
/// Computes a style using the given CascadeInputs. This can be used to
/// compute a style any time we know what rules apply and just need to use
/// the given parent styles.
///
/// parent_style is the style to inherit from for properties affected by
/// first-line ancestors.
///
/// parent_style_ignoring_first_line is the style to inherit from for
/// properties not affected by first-line ancestors.
///
/// layout_parent_style is the style used for some property fixups. It's
/// the style of the nearest ancestor with a layout box.
///
/// is_link should be true if we're computing style for a link; that affects
/// how :visited handling is done.
pub fn compute_style_with_inputs(&self,
inputs: &CascadeInputs,
pseudo: Option<&PseudoElement>,
guards: &StylesheetGuards,
parent_style: &ComputedValues,
parent_style_ignoring_first_line: &ComputedValues,
layout_parent_style: &ComputedValues,
font_metrics: &FontMetricsProvider,
cascade_flags: CascadeFlags)
-> Arc<ComputedValues>
{
// We need to compute visited values if we have visited rules or if our
// parent has visited values.
let visited_values = if inputs.visited_rules.is_some() || parent_style.get_visited_style().is_some() {
@ -737,28 +779,37 @@ impl Stylist {
Some(rules) => rules,
None => inputs.rules.as_ref().unwrap(),
};
// We want to use the visited bits (if any) from our parent style as
// our parent.
let inherited_style =
parent_style.get_visited_style().unwrap_or(parent_style);
let inherited_style;
let inherited_style_ignoring_first_line;
let layout_parent_style_for_visited;
if cascade_flags.contains(IS_LINK) {
// We just want to use our parent style as our parent.
inherited_style = parent_style;
inherited_style_ignoring_first_line = parent_style_ignoring_first_line;
layout_parent_style_for_visited = layout_parent_style;
} else {
// We want to use the visited bits (if any) from our parent
// style as our parent.
inherited_style =
parent_style.get_visited_style().unwrap_or(parent_style);
inherited_style_ignoring_first_line =
parent_style_ignoring_first_line.get_visited_style().unwrap_or(parent_style_ignoring_first_line);
layout_parent_style_for_visited =
layout_parent_style.get_visited_style().unwrap_or(layout_parent_style);
}
// FIXME(emilio): The lack of layout_parent_style here could be
// worrying, but we're probably dropping the display fixup for
// pseudos other than before and after, so it's probably ok.
//
// (Though the flags don't indicate so!)
let computed =
properties::cascade(&self.device,
Some(pseudo),
pseudo,
rule_node,
guards,
Some(inherited_style),
Some(inherited_style),
Some(inherited_style),
Some(inherited_style_ignoring_first_line),
Some(layout_parent_style_for_visited),
None,
None,
font_metrics,
CascadeFlags::empty(),
cascade_flags,
self.quirks_mode);
Some(computed)
@ -774,18 +825,18 @@ impl Stylist {
// difficult to assert that display: contents nodes never arrive here
// (tl;dr: It doesn't apply for replaced elements and such, but the
// computed value is still "contents").
Some(properties::cascade(&self.device,
Some(pseudo),
rules,
guards,
Some(parent_style),
Some(parent_style),
Some(parent_style),
visited_values,
None,
font_metrics,
CascadeFlags::empty(),
self.quirks_mode))
properties::cascade(&self.device,
pseudo,
rules,
guards,
Some(parent_style),
Some(parent_style_ignoring_first_line),
Some(layout_parent_style),
visited_values,
None,
font_metrics,
cascade_flags,
self.quirks_mode)
}
/// Computes the cascade inputs for a lazily-cascaded pseudo-element.