Auto merge of #23108 - emilio:gecko-sync, r=emilio

style: Sync changes from mozilla-central.

See each individual commit for details.

<!-- 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/23108)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-03-27 12:39:42 -04:00 committed by GitHub
commit 864f855850
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 961 additions and 1338 deletions

View file

@ -367,7 +367,7 @@ impl FontGroup {
let families = style
.font_family
.0
.families
.iter()
.map(|family| FontGroupFamily::new(descriptor.clone(), &family))
.collect();

View file

@ -119,7 +119,10 @@ fn font_family(names: Vec<&str>) -> FontFamily {
})
.collect();
FontFamily(FontFamilyList::new(names.into_boxed_slice()))
FontFamily {
families: FontFamilyList::new(names.into_boxed_slice()),
is_system_font: false,
}
}
#[test]

View file

@ -77,7 +77,7 @@ where
#[cfg(feature = "gecko")]
unsafe impl HasFFI for AuthorStyles<crate::gecko::data::GeckoStyleSheet> {
type FFIType = crate::gecko_bindings::bindings::RawServoAuthorStyles;
type FFIType = crate::gecko_bindings::structs::RawServoAuthorStyles;
}
#[cfg(feature = "gecko")]
unsafe impl HasSimpleFFI for AuthorStyles<crate::gecko::data::GeckoStyleSheet> {}

View file

@ -32,7 +32,6 @@ mod bindings {
use toml::value::Table;
const STRUCTS_FILE: &'static str = "structs.rs";
const BINDINGS_FILE: &'static str = "bindings.rs";
fn read_config(path: &PathBuf) -> Table {
println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
@ -128,9 +127,6 @@ mod bindings {
trait BuilderExt {
fn get_initial_builder() -> Builder;
fn include<T: Into<String>>(self, file: T) -> Builder;
fn zero_size_type(self, ty: &str, structs_list: &HashSet<&str>) -> Builder;
fn borrowed_type(self, ty: &str) -> Builder;
fn mutable_borrowed_type(self, ty: &str) -> Builder;
}
impl BuilderExt for Builder {
@ -181,42 +177,6 @@ mod bindings {
fn include<T: Into<String>>(self, file: T) -> Builder {
self.clang_arg("-include").clang_arg(file)
}
// This makes an FFI-safe void type that can't be matched on
// &VoidType is UB to have, because you can match on it
// to produce a reachable unreachable. If it's wrapped in
// a struct as a private field it becomes okay again
//
// Not 100% sure of how safe this is, but it's what we're using
// in the XPCOM ffi too
// https://github.com/nikomatsakis/rust-memory-model/issues/2
fn zero_size_type(self, ty: &str, structs_list: &HashSet<&str>) -> Builder {
if !structs_list.contains(ty) {
self.blacklist_type(ty)
.raw_line(format!("enum {}Void {{ }}", ty))
.raw_line(format!("pub struct {0}({0}Void);", ty))
} else {
self
}
}
fn borrowed_type(self, ty: &str) -> Builder {
self.blacklist_type(format!("{}Borrowed", ty))
.raw_line(format!("pub type {0}Borrowed<'a> = &'a {0};", ty))
.blacklist_type(format!("{}BorrowedOrNull", ty))
.raw_line(format!(
"pub type {0}BorrowedOrNull<'a> = Option<&'a {0}>;",
ty
))
}
fn mutable_borrowed_type(self, ty: &str) -> Builder {
self.borrowed_type(ty)
.blacklist_type(format!("{}BorrowedMut", ty))
.raw_line(format!("pub type {0}BorrowedMut<'a> = &'a mut {0};", ty))
.blacklist_type(format!("{}BorrowedMutOrNull", ty))
.raw_line(format!(
"pub type {0}BorrowedMutOrNull<'a> = Option<&'a mut {0}>;",
ty
))
}
}
struct Fixup {
@ -259,58 +219,6 @@ mod bindings {
.expect("Unable to write output");
}
fn get_types(filename: &str, macro_pat: &str) -> Vec<(String, String)> {
// Read the file
let path = DISTDIR_PATH.join("include/mozilla/").join(filename);
let mut list_file = File::open(path).expect(&format!("Unable to open {}", filename));
let mut content = String::new();
list_file
.read_to_string(&mut content)
.expect(&format!("Failed to read {}", filename));
// Remove comments
let block_comment_re = Regex::new(r#"(?s)/\*.*?\*/"#).unwrap();
let line_comment_re = Regex::new(r#"//.*"#).unwrap();
let content = block_comment_re.replace_all(&content, "");
let content = line_comment_re.replace_all(&content, "");
// Extract the list
let re_string = format!(r#"^({})\(.+,\s*(\w+)\)$"#, macro_pat);
let re = Regex::new(&re_string).unwrap();
content
.lines()
.map(|line| line.trim())
.filter(|line| !line.is_empty())
.map(|line| {
let captures = re
.captures(&line)
.expect(&format!("Unrecognized line in {}: '{}'", filename, line));
let macro_name = captures.get(1).unwrap().as_str().to_string();
let type_name = captures.get(2).unwrap().as_str().to_string();
(macro_name, type_name)
})
.collect()
}
fn get_borrowed_types() -> Vec<(bool, String)> {
get_types("BorrowedTypeList.h", "GECKO_BORROWED_TYPE(?:_MUT)?")
.into_iter()
.map(|(macro_name, type_name)| (macro_name.ends_with("MUT"), type_name))
.collect()
}
fn get_arc_types() -> Vec<String> {
get_types("ServoArcTypeList.h", "SERVO_ARC_TYPE")
.into_iter()
.map(|(_, type_name)| type_name)
.collect()
}
fn get_boxed_types() -> Vec<String> {
get_types("ServoBoxedTypeList.h", "SERVO_BOXED_TYPE")
.into_iter()
.map(|(_, type_name)| type_name)
.collect()
}
struct BuilderWithConfig<'a> {
builder: Builder,
config: &'a Table,
@ -386,10 +294,13 @@ mod bindings {
fn generate_structs() {
let builder = Builder::get_initial_builder()
.enable_cxx_namespaces()
.with_codegen_config(CodegenConfig::TYPES | CodegenConfig::VARS);
.with_codegen_config(
CodegenConfig::TYPES | CodegenConfig::VARS | CodegenConfig::FUNCTIONS,
);
let mut fixups = vec![];
let builder = BuilderWithConfig::new(builder, CONFIG["structs"].as_table().unwrap())
.handle_common(&mut fixups)
.handle_str_items("whitelist-functions", |b, item| b.whitelist_function(item))
.handle_str_items("bitfield-enums", |b, item| b.bitfield_enum(item))
.handle_str_items("rusty-enums", |b, item| b.rustified_enum(item))
.handle_str_items("whitelist-vars", |b, item| b.whitelist_var(item))
@ -480,89 +391,6 @@ mod bindings {
}
}
fn generate_bindings() {
let builder = Builder::get_initial_builder()
.disable_name_namespacing()
.with_codegen_config(CodegenConfig::FUNCTIONS);
let config = CONFIG["bindings"].as_table().unwrap();
let mut structs_types = HashSet::new();
let mut fixups = vec![];
let mut builder = BuilderWithConfig::new(builder, config)
.handle_common(&mut fixups)
.handle_str_items("whitelist-functions", |b, item| b.whitelist_function(item))
.handle_str_items("structs-types", |mut builder, ty| {
builder = builder
.blacklist_type(ty)
.raw_line(format!("use gecko_bindings::structs::{};", ty));
structs_types.insert(ty);
// TODO this is hacky, figure out a better way to do it without
// hardcoding everything...
if ty.starts_with("nsStyle") {
builder = builder
.raw_line(format!("unsafe impl Send for {} {{}}", ty))
.raw_line(format!("unsafe impl Sync for {} {{}}", ty));
}
builder
})
// TODO This was added due to servo/rust-bindgen#75, but
// that has been fixed in clang 4.0+. When we switch people
// to libclang 4.0, we can remove this.
.handle_table_items("array-types", |builder, item| {
let cpp_type = item["cpp-type"].as_str().unwrap();
let rust_type = item["rust-type"].as_str().unwrap();
builder.raw_line(format!(
concat!(
"pub type nsTArrayBorrowed_{}<'a> = ",
"&'a mut ::gecko_bindings::structs::nsTArray<{}>;"
),
cpp_type, rust_type
))
})
.handle_str_items("servo-immutable-borrow-types", |b, ty| b.borrowed_type(ty))
// Right now the only immutable borrow types are ones which we import
// from the |structs| module. As such, we don't need to create an opaque
// type with zero_size_type. If we ever introduce immutable borrow types
// which _do_ need to be opaque, we'll need a separate mode.
.handle_str_items("servo-borrow-types", |b, ty| b.mutable_borrowed_type(ty))
.get_builder();
for (is_mut, ty) in get_borrowed_types().iter() {
if *is_mut {
builder = builder.mutable_borrowed_type(ty);
} else {
builder = builder.borrowed_type(ty);
}
}
for ty in get_arc_types().iter() {
builder = builder
.blacklist_type(format!("{}Strong", ty))
.raw_line(format!(
"pub type {0}Strong = ::gecko_bindings::sugar::ownership::Strong<{0}>;",
ty
))
.borrowed_type(ty)
.zero_size_type(ty, &structs_types);
}
for ty in get_boxed_types().iter() {
builder = builder
.blacklist_type(format!("{}Owned", ty))
.raw_line(format!(
"pub type {0}Owned = ::gecko_bindings::sugar::ownership::Owned<{0}>;",
ty
))
.blacklist_type(format!("{}OwnedOrNull", ty))
.raw_line(format!(
concat!(
"pub type {0}OwnedOrNull = ",
"::gecko_bindings::sugar::ownership::OwnedOrNull<{0}>;"
),
ty
))
.mutable_borrowed_type(ty)
.zero_size_type(ty, &structs_types);
}
write_binding_file(builder, BINDINGS_FILE, &fixups);
}
fn generate_atoms() {
let script = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap())
.join("gecko")
@ -580,24 +408,9 @@ mod bindings {
}
pub fn generate() {
use std::thread;
macro_rules! run_tasks {
($($task:expr,)+) => {
if setup_logging() {
$($task;)+
} else {
let threads = vec![$( thread::spawn(|| $task) ),+];
for thread in threads.into_iter() {
thread.join().unwrap();
}
}
}
}
run_tasks! {
generate_structs(),
generate_bindings(),
generate_atoms(),
}
setup_logging();
generate_structs();
generate_atoms();
for path in ADDED_PATHS.lock().unwrap().iter() {
println!("cargo:rerun-if-changed={}", path.to_str().unwrap());

View file

@ -103,6 +103,15 @@ include = [
"ZIndex",
"TransformOrigin",
"WordBreak",
"Contain",
"RestyleHint",
"TouchAction",
"WillChangeBits",
"TextDecorationLine",
"MozListReversed",
"Owned",
"OwnedOrNull",
"Strong",
]
item_types = ["enums", "structs", "typedefs"]
@ -196,3 +205,35 @@ item_types = ["enums", "structs", "typedefs"]
"GenericBorderRadius" = """
inline const StyleLengthPercentage& Get(mozilla::HalfCorner) const;
"""
"RestyleHint" = """
static inline StyleRestyleHint RestyleSubtree();
static inline StyleRestyleHint RecascadeSubtree();
static inline StyleRestyleHint ForAnimations();
"""
# TODO(emilio): Add hooks to cbindgen to be able to generate MOZ_MUST_USE_TYPE
# or MOZ_MUST_USE on the functions.
"Owned" = """
UniquePtr<GeckoType> Consume() {
UniquePtr<GeckoType> ret(ptr);
ptr = nullptr;
return ret;
}
"""
"OwnedOrNull" = """
UniquePtr<GeckoType> Consume() {
UniquePtr<GeckoType> ret(ptr);
ptr = nullptr;
return ret;
}
"""
"Strong" = """
already_AddRefed<GeckoType> Consume() {
already_AddRefed<GeckoType> ret(const_cast<GeckoType*>(ptr));
ptr = nullptr;
return ret;
}
"""

View file

@ -441,6 +441,11 @@ pub trait TElement:
None
}
/// The ::marker pseudo-element of this element, if it exists.
fn marker_pseudo_element(&self) -> Option<Self> {
None
}
/// Execute `f` for each anonymous content child (apart from ::before and
/// ::after) whose originating element is `self`.
fn each_anonymous_content_child<F>(&self, _f: F)

View file

@ -545,7 +545,6 @@ macro_rules! font_face_descriptors {
}
}
// css-name rust_identifier: Type,
#[cfg(feature = "gecko")]
font_face_descriptors! {
mandatory descriptors = [

View file

@ -7,51 +7,42 @@
#![deny(missing_docs)]
use crate::context::SharedStyleContext;
use crate::logical_geometry::WritingMode;
use crate::media_queries::Device;
use crate::properties::style_structs::Font;
use crate::Atom;
use app_units::Au;
/// Represents the font metrics that style needs from a font to compute the
/// value of certain CSS units like `ex`.
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct FontMetrics {
/// The x-height of the font.
pub x_height: Au,
pub x_height: Option<Au>,
/// The zero advance. This is usually writing mode dependent
pub zero_advance_measure: Au,
pub zero_advance_measure: Option<Au>,
}
/// The result for querying font metrics for a given font family.
/// Type of font metrics to retrieve.
#[derive(Clone, Debug, PartialEq)]
pub enum FontMetricsQueryResult {
/// The font is available, but we may or may not have found any font metrics
/// for it.
Available(FontMetrics),
/// The font is not available.
NotAvailable,
pub enum FontMetricsOrientation {
/// Get metrics for horizontal or vertical according to the Context's
/// writing mode.
MatchContext,
/// Force getting horizontal metrics.
Horizontal,
}
/// A trait used to represent something capable of providing us font metrics.
pub trait FontMetricsProvider {
/// Obtain the metrics for given font family.
///
/// TODO: We could make this take the full list, I guess, and save a few
/// virtual calls in the case we are repeatedly unable to find font metrics?
/// That is not too common in practice though.
fn query(
&self,
_font: &Font,
_font_size: Au,
_wm: WritingMode,
_in_media_query: bool,
_device: &Device,
) -> FontMetricsQueryResult {
FontMetricsQueryResult::NotAvailable
_context: &crate::values::computed::Context,
_base_size: crate::values::specified::length::FontBaseSize,
_orientation: FontMetricsOrientation,
) -> FontMetrics {
Default::default()
}
/// Get default size of a given language and generic family
/// Get default size of a given language and generic family.
fn get_size(&self, font_name: &Atom, font_family: u8) -> Au;
/// Construct from a shared style context

View file

@ -9,32 +9,29 @@
#![allow(non_snake_case, missing_docs)]
use crate::gecko::url::CssUrlData;
use crate::gecko_bindings::bindings::RawServoCounterStyleRule;
use crate::gecko_bindings::bindings::RawServoFontFeatureValuesRule;
use crate::gecko_bindings::bindings::RawServoImportRule;
use crate::gecko_bindings::bindings::RawServoKeyframe;
use crate::gecko_bindings::bindings::RawServoKeyframesRule;
use crate::gecko_bindings::bindings::RawServoMediaRule;
use crate::gecko_bindings::bindings::RawServoMozDocumentRule;
use crate::gecko_bindings::bindings::RawServoNamespaceRule;
use crate::gecko_bindings::bindings::RawServoPageRule;
use crate::gecko_bindings::bindings::RawServoRuleNode;
use crate::gecko_bindings::bindings::RawServoRuleNodeStrong;
use crate::gecko_bindings::bindings::RawServoSupportsRule;
use crate::gecko_bindings::bindings::ServoCssRules;
use crate::gecko_bindings::structs::RawServoAnimationValue;
use crate::gecko_bindings::structs::RawServoCounterStyleRule;
use crate::gecko_bindings::structs::RawServoCssUrlData;
use crate::gecko_bindings::structs::RawServoDeclarationBlock;
use crate::gecko_bindings::structs::RawServoFontFaceRule;
use crate::gecko_bindings::structs::RawServoFontFeatureValuesRule;
use crate::gecko_bindings::structs::RawServoImportRule;
use crate::gecko_bindings::structs::RawServoKeyframe;
use crate::gecko_bindings::structs::RawServoKeyframesRule;
use crate::gecko_bindings::structs::RawServoMediaList;
use crate::gecko_bindings::structs::RawServoMediaRule;
use crate::gecko_bindings::structs::RawServoMozDocumentRule;
use crate::gecko_bindings::structs::RawServoNamespaceRule;
use crate::gecko_bindings::structs::RawServoPageRule;
use crate::gecko_bindings::structs::RawServoQuotes;
use crate::gecko_bindings::structs::RawServoStyleRule;
use crate::gecko_bindings::structs::RawServoStyleSheetContents;
use crate::gecko_bindings::structs::RawServoSupportsRule;
use crate::gecko_bindings::structs::ServoCssRules;
use crate::gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI, Strong};
use crate::media_queries::MediaList;
use crate::properties::animated_properties::AnimationValue;
use crate::properties::{ComputedValues, PropertyDeclarationBlock};
use crate::rule_tree::StrongRuleNode;
use crate::shared_lock::Locked;
use crate::stylesheets::keyframes_rule::Keyframe;
use crate::stylesheets::{CounterStyleRule, CssRules, FontFaceRule, FontFeatureValuesRule};
@ -121,31 +118,6 @@ impl_arc_ffi!(CssUrlData => RawServoCssUrlData
impl_arc_ffi!(Box<[QuotePair]> => RawServoQuotes
[Servo_Quotes_AddRef, Servo_Quotes_Release]);
// RuleNode is a Arc-like type but it does not use Arc.
impl StrongRuleNode {
pub fn into_strong(self) -> RawServoRuleNodeStrong {
let ptr = self.ptr();
mem::forget(self);
unsafe { mem::transmute(ptr) }
}
pub fn from_ffi<'a>(ffi: &'a &RawServoRuleNode) -> &'a Self {
unsafe { &*(ffi as *const &RawServoRuleNode as *const StrongRuleNode) }
}
}
#[no_mangle]
pub unsafe extern "C" fn Servo_RuleNode_AddRef(obj: &RawServoRuleNode) {
mem::forget(StrongRuleNode::from_ffi(&obj).clone());
}
#[no_mangle]
pub unsafe extern "C" fn Servo_RuleNode_Release(obj: &RawServoRuleNode) {
let ptr = StrongRuleNode::from_ffi(&obj);
ptr::read(ptr as *const StrongRuleNode);
}
// ComputedStyle is not an opaque type on any side of FFI.
// This means that doing the HasArcFFI type trick is actually unsound,
// since it gives us a way to construct an Arc<ComputedStyle> from

View file

@ -12,8 +12,7 @@
use crate::gecko::values::GeckoStyleCoordConvertible;
use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs::RawGeckoGfxMatrix4x4;
use crate::gecko_bindings::structs::{self, nsStyleCoord_CalcValue};
use crate::gecko_bindings::structs::{self, nsStyleCoord_CalcValue, Matrix4x4Components};
use crate::gecko_bindings::structs::{nsStyleImage, nsresult, SheetType};
use crate::gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue};
use crate::stylesheets::{Origin, RulesMutateError};
@ -986,8 +985,8 @@ pub unsafe fn string_from_chars_pointer(p: *const u16) -> String {
String::from_utf16_lossy(char_vec)
}
impl<'a> From<&'a RawGeckoGfxMatrix4x4> for Matrix3D {
fn from(m: &'a RawGeckoGfxMatrix4x4) -> Matrix3D {
impl<'a> From<&'a Matrix4x4Components> for Matrix3D {
fn from(m: &'a Matrix4x4Components) -> Matrix3D {
Matrix3D {
m11: m[0],
m12: m[1],
@ -1009,8 +1008,8 @@ impl<'a> From<&'a RawGeckoGfxMatrix4x4> for Matrix3D {
}
}
impl From<Matrix3D> for RawGeckoGfxMatrix4x4 {
fn from(matrix: Matrix3D) -> RawGeckoGfxMatrix4x4 {
impl From<Matrix3D> for Matrix4x4Components {
fn from(matrix: Matrix3D) -> Self {
[
matrix.m11, matrix.m12, matrix.m13, matrix.m14, matrix.m21, matrix.m22, matrix.m23,
matrix.m24, matrix.m31, matrix.m32, matrix.m33, matrix.m34, matrix.m41, matrix.m42,

View file

@ -6,8 +6,8 @@
use crate::context::QuirksMode;
use crate::dom::TElement;
use crate::gecko_bindings::bindings::{self, RawServoStyleSet};
use crate::gecko_bindings::structs::{self, ServoStyleSetSizes};
use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs::{self, RawServoStyleSet, ServoStyleSetSizes};
use crate::gecko_bindings::structs::{StyleSheet as DomStyleSheet, StyleSheetInfo};
use crate::gecko_bindings::sugar::ownership::{HasArcFFI, HasBoxFFI, HasFFI, HasSimpleFFI};
use crate::invalidation::media_queries::{MediaListKey, ToMediaListKey};

View file

@ -369,8 +369,8 @@ fn eval_prefers_color_scheme(device: &Device, query_value: Option<PrefersColorSc
}
}
/// https://drafts.csswg.org/mediaqueries-4/#mf-interaction
bitflags! {
/// https://drafts.csswg.org/mediaqueries-4/#mf-interaction
struct PointerCapabilities: u8 {
const COARSE = structs::PointerCapabilities_Coarse;
const FINE = structs::PointerCapabilities_Fine;

View file

@ -33,7 +33,10 @@ impl ::selectors::parser::PseudoElement for PseudoElement {
fn valid_after_slotted(&self) -> bool {
matches!(
*self,
PseudoElement::Before | PseudoElement::After | PseudoElement::Placeholder
PseudoElement::Before |
PseudoElement::After |
PseudoElement::Marker |
PseudoElement::Placeholder
)
}
@ -109,6 +112,12 @@ impl PseudoElement {
*self == PseudoElement::After
}
/// Whether this pseudo-element is the ::marker pseudo.
#[inline]
pub fn is_marker(&self) -> bool {
*self == PseudoElement::Marker
}
/// Whether this pseudo-element is ::first-letter.
#[inline]
pub fn is_first_letter(&self) -> bool {
@ -180,6 +189,8 @@ impl PseudoElement {
/// Whether this pseudo-element should actually exist if it has
/// the given styles.
pub fn should_exist(&self, style: &ComputedValues) -> bool {
debug_assert!(self.is_eager());
if style.get_box().clone_display() == Display::None {
return false;
}

View file

@ -195,13 +195,16 @@ impl PseudoElement {
return Some(${pseudo_element_variant(pseudo)})
}
% endfor
// Alias "-moz-selection" to "selection" at parse time.
// Alias some legacy prefixed pseudos to their standardized name at parse time:
"-moz-selection" => {
return Some(PseudoElement::Selection);
}
"-moz-placeholder" => {
return Some(PseudoElement::Placeholder);
}
"-moz-list-bullet" | "-moz-list-number" => {
return Some(PseudoElement::Marker);
}
_ => {
if starts_with_ignore_ascii_case(name, "-moz-tree-") {
return PseudoElement::tree_pseudo_element(name, Box::new([]))

View file

@ -51,8 +51,8 @@ impl GeckoRestyleDamage {
let mut reset_only = false;
let hint = unsafe {
bindings::Gecko_CalcStyleDifference(
old_style,
new_style,
old_style.as_gecko_computed_style(),
new_style.as_gecko_computed_style(),
&mut any_style_changed,
&mut reset_only,
)

View file

@ -20,7 +20,7 @@ use crate::context::{PostAnimationTasks, QuirksMode, SharedStyleContext, UpdateA
use crate::data::ElementData;
use crate::dom::{LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode, TShadowRoot};
use crate::element_state::{DocumentState, ElementState};
use crate::font_metrics::{FontMetrics, FontMetricsProvider, FontMetricsQueryResult};
use crate::font_metrics::{FontMetrics, FontMetricsOrientation, FontMetricsProvider};
use crate::gecko::data::GeckoStyleSheet;
use crate::gecko::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl};
use crate::gecko::snapshot_helpers;
@ -44,7 +44,6 @@ use crate::gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetDocumentLWThe
use crate::gecko_bindings::bindings::{Gecko_SetNodeFlags, Gecko_UnsetNodeFlags};
use crate::gecko_bindings::structs;
use crate::gecko_bindings::structs::nsChangeHint;
use crate::gecko_bindings::structs::nsRestyleHint;
use crate::gecko_bindings::structs::Document_DocumentTheme as DocumentTheme;
use crate::gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel;
use crate::gecko_bindings::structs::ELEMENT_HANDLED_SNAPSHOT;
@ -54,14 +53,15 @@ use crate::gecko_bindings::structs::ELEMENT_HAS_SNAPSHOT;
use crate::gecko_bindings::structs::NODE_DESCENDANTS_NEED_FRAMES;
use crate::gecko_bindings::structs::NODE_NEEDS_FRAME;
use crate::gecko_bindings::structs::{nsAtom, nsIContent, nsINode_BooleanFlag};
use crate::gecko_bindings::structs::{RawGeckoElement, RawGeckoNode, RawGeckoXBLBinding};
use crate::gecko_bindings::structs::{
nsINode as RawGeckoNode, nsXBLBinding as RawGeckoXBLBinding, Element as RawGeckoElement,
};
use crate::gecko_bindings::sugar::ownership::{HasArcFFI, HasSimpleFFI};
use crate::global_style_data::GLOBAL_STYLE_DATA;
use crate::hash::FxHashMap;
use crate::logical_geometry::WritingMode;
use crate::invalidation::element::restyle_hints::RestyleHint;
use crate::media_queries::Device;
use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
use crate::properties::style_structs::Font;
use crate::properties::{ComputedValues, LonghandId};
use crate::properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
use crate::rule_tree::CascadeLevel as ServoCascadeLevel;
@ -69,6 +69,7 @@ use crate::selector_parser::{AttrValue, HorizontalDirection, Lang};
use crate::shared_lock::Locked;
use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
use crate::stylist::CascadeData;
use crate::values::specified::length::FontBaseSize;
use crate::CaseSensitivityExt;
use app_units::Au;
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
@ -169,11 +170,7 @@ impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> {
where
Self: 'a,
{
let author_styles = unsafe {
(self.0.mServoStyles.mPtr as *const structs::RawServoAuthorStyles
as *const bindings::RawServoAuthorStyles)
.as_ref()?
};
let author_styles = unsafe { self.0.mServoStyles.mPtr.as_ref()? };
let author_styles = AuthorStyles::<GeckoStyleSheet>::from_ffi(author_styles);
@ -326,7 +323,11 @@ impl<'ln> GeckoNode<'ln> {
// `flattened_tree_parent`.
if self.flattened_tree_parent_is_parent() {
debug_assert_eq!(
unsafe { bindings::Gecko_GetFlattenedTreeParentNode(self.0).map(GeckoNode) },
unsafe {
bindings::Gecko_GetFlattenedTreeParentNode(self.0)
.as_ref()
.map(GeckoNode)
},
self.parent_node(),
"Fast path stopped holding!"
);
@ -336,7 +337,11 @@ impl<'ln> GeckoNode<'ln> {
// NOTE(emilio): If this call is too expensive, we could manually
// inline more aggressively.
unsafe { bindings::Gecko_GetFlattenedTreeParentNode(self.0).map(GeckoNode) }
unsafe {
bindings::Gecko_GetFlattenedTreeParentNode(self.0)
.as_ref()
.map(GeckoNode)
}
}
#[inline]
@ -381,12 +386,16 @@ impl<'ln> TNode for GeckoNode<'ln> {
#[inline]
fn last_child(&self) -> Option<Self> {
unsafe { bindings::Gecko_GetLastChild(self.0).map(GeckoNode) }
unsafe { bindings::Gecko_GetLastChild(self.0).as_ref().map(GeckoNode) }
}
#[inline]
fn prev_sibling(&self) -> Option<Self> {
unsafe { bindings::Gecko_GetPreviousSibling(self.0).map(GeckoNode) }
unsafe {
bindings::Gecko_GetPreviousSibling(self.0)
.as_ref()
.map(GeckoNode)
}
}
#[inline]
@ -503,7 +512,9 @@ impl<'a> Iterator for GeckoChildrenIterator<'a> {
// however we can't express this easily with bindgen, and it would
// introduce functions with two input lifetimes into bindgen,
// which would be out of scope for elision.
bindings::Gecko_GetNextStyleChild(&mut *(it as *mut _)).map(GeckoNode)
bindings::Gecko_GetNextStyleChild(&mut *(it as *mut _))
.as_ref()
.map(GeckoNode)
},
}
}
@ -549,7 +560,7 @@ impl<'lb> GeckoXBLBinding<'lb> {
base.each_xbl_cascade_data(f);
}
let data = unsafe { bindings::Gecko_XBLBinding_GetRawServoStyles(self.0) };
let data = unsafe { bindings::Gecko_XBLBinding_GetRawServoStyles(self.0).as_ref() };
if let Some(data) = data {
let data: &'lb _ = AuthorStyles::<GeckoStyleSheet>::from_ffi(data);
@ -710,7 +721,11 @@ impl<'le> GeckoElement<'le> {
// FIXME(heycam): Having trouble with bindgen on nsXULElement,
// where the binding parent is stored in a member variable
// rather than in slots. So just get it through FFI for now.
unsafe { bindings::Gecko_GetBindingParent(self.0).map(GeckoElement) }
unsafe {
bindings::Gecko_GetBindingParent(self.0)
.as_ref()
.map(GeckoElement)
}
} else {
let binding_parent = unsafe { self.non_xul_xbl_binding_parent_raw_content().as_ref() }
.map(GeckoNode::from_content)
@ -718,7 +733,11 @@ impl<'le> GeckoElement<'le> {
debug_assert!(
binding_parent ==
unsafe { bindings::Gecko_GetBindingParent(self.0).map(GeckoElement) }
unsafe {
bindings::Gecko_GetBindingParent(self.0)
.as_ref()
.map(GeckoElement)
}
);
binding_parent
}
@ -778,7 +797,11 @@ impl<'le> GeckoElement<'le> {
return None;
}
unsafe { bindings::Gecko_GetBeforeOrAfterPseudo(self.0, is_before).map(GeckoElement) }
unsafe {
bindings::Gecko_GetBeforeOrAfterPseudo(self.0, is_before)
.as_ref()
.map(GeckoElement)
}
}
#[inline]
@ -800,13 +823,8 @@ impl<'le> GeckoElement<'le> {
/// animation.
///
/// Also this function schedules style flush.
pub unsafe fn note_explicit_hints(
&self,
restyle_hint: nsRestyleHint,
change_hint: nsChangeHint,
) {
pub unsafe fn note_explicit_hints(&self, restyle_hint: RestyleHint, change_hint: nsChangeHint) {
use crate::gecko::restyle_damage::GeckoRestyleDamage;
use crate::invalidation::element::restyle_hints::RestyleHint;
let damage = GeckoRestyleDamage::new(change_hint);
debug!(
@ -814,7 +832,6 @@ impl<'le> GeckoElement<'le> {
self, restyle_hint, change_hint
);
let restyle_hint: RestyleHint = restyle_hint.into();
debug_assert!(
!(restyle_hint.has_animation_hint() && restyle_hint.has_non_animation_hint()),
"Animation restyle hints should not appear with non-animation restyle hints"
@ -890,7 +907,7 @@ impl<'le> GeckoElement<'le> {
let mut map = FxHashMap::with_capacity_and_hasher(collection_length, Default::default());
for i in 0..collection_length {
let raw_end_value = unsafe { Gecko_ElementTransitions_EndValueAt(self.0, i) };
let raw_end_value = unsafe { Gecko_ElementTransitions_EndValueAt(self.0, i).as_ref() };
let end_value = AnimationValue::arc_from_borrowed(&raw_end_value)
.expect("AnimationValue not found in ElementTransitions");
@ -1025,44 +1042,60 @@ impl FontMetricsProvider for GeckoFontMetricsProvider {
}
fn get_size(&self, font_name: &Atom, font_family: u8) -> Au {
use crate::gecko_bindings::bindings::Gecko_GetBaseSize;
let mut cache = self.font_size_cache.borrow_mut();
if let Some(sizes) = cache.iter().find(|el| el.0 == *font_name) {
return sizes.1.size_for_generic(font_family);
}
let sizes = unsafe { Gecko_GetBaseSize(font_name.as_ptr()) };
let sizes = unsafe { bindings::Gecko_GetBaseSize(font_name.as_ptr()) };
cache.push((font_name.clone(), sizes));
sizes.size_for_generic(font_family)
}
fn query(
&self,
font: &Font,
font_size: Au,
wm: WritingMode,
in_media_query: bool,
device: &Device,
) -> FontMetricsQueryResult {
use crate::gecko_bindings::bindings::Gecko_GetFontMetrics;
let pc = match device.pres_context() {
context: &crate::values::computed::Context,
base_size: FontBaseSize,
orientation: FontMetricsOrientation,
) -> FontMetrics {
let pc = match context.device().pres_context() {
Some(pc) => pc,
None => return FontMetricsQueryResult::NotAvailable,
None => return Default::default(),
};
let size = base_size.resolve(context);
let style = context.style();
let (wm, font) = match base_size {
FontBaseSize::CurrentStyle => (style.writing_mode, style.get_font()),
// These are only used for font-size computation, and the first is
// really dubious...
FontBaseSize::InheritedStyleButStripEmUnits | FontBaseSize::InheritedStyle => {
(*style.inherited_writing_mode(), style.get_parent_font())
},
};
let vertical_metrics = match orientation {
FontMetricsOrientation::MatchContext => wm.is_vertical() && wm.is_upright(),
FontMetricsOrientation::Horizontal => false,
};
let gecko_metrics = unsafe {
Gecko_GetFontMetrics(
bindings::Gecko_GetFontMetrics(
pc,
wm.is_vertical() && !wm.is_sideways(),
vertical_metrics,
font.gecko(),
font_size.0,
size.0,
// we don't use the user font set in a media query
!in_media_query,
!context.in_media_query,
)
};
let metrics = FontMetrics {
x_height: Au(gecko_metrics.mXSize),
zero_advance_measure: Au(gecko_metrics.mChSize),
};
FontMetricsQueryResult::Available(metrics)
FontMetrics {
x_height: Some(Au(gecko_metrics.mXSize)),
zero_advance_measure: if gecko_metrics.mChSize >= 0 {
Some(Au(gecko_metrics.mChSize))
} else {
None
},
}
}
}
@ -1125,6 +1158,18 @@ impl<'le> TElement for GeckoElement<'le> {
self.before_or_after_pseudo(/* is_before = */ false)
}
fn marker_pseudo_element(&self) -> Option<Self> {
if !self.has_properties() {
return None;
}
unsafe {
bindings::Gecko_GetMarkerPseudo(self.0)
.as_ref()
.map(GeckoElement)
}
}
#[inline]
fn is_html_element(&self) -> bool {
self.namespace_id() == structs::kNameSpaceID_XHTML as i32
@ -1254,7 +1299,7 @@ impl<'le> TElement for GeckoElement<'le> {
return None;
}
let declarations = unsafe { Gecko_GetStyleAttrDeclarationBlock(self.0) };
let declarations = unsafe { Gecko_GetStyleAttrDeclarationBlock(self.0).as_ref() };
let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>> =
declarations.and_then(|s| s.as_arc_opt());
declarations.map(|s| s.borrow_arc())
@ -1516,7 +1561,7 @@ impl<'le> TElement for GeckoElement<'le> {
);
unsafe {
self.note_explicit_hints(
nsRestyleHint::eRestyle_Subtree,
RestyleHint::restyle_subtree(),
nsChangeHint::nsChangeHint_Empty,
);
}
@ -1535,8 +1580,12 @@ impl<'le> TElement for GeckoElement<'le> {
// should destroy all CSS animations in display:none subtree.
let computed_data = self.borrow_data();
let computed_values = computed_data.as_ref().map(|d| d.styles.primary());
let before_change_values = before_change_style.as_ref().map(|x| &**x);
let computed_values_opt = computed_values.as_ref().map(|x| &***x);
let before_change_values = before_change_style
.as_ref()
.map_or(ptr::null(), |x| x.as_gecko_computed_style());
let computed_values_opt = computed_values
.as_ref()
.map_or(ptr::null(), |x| x.as_gecko_computed_style());
unsafe {
Gecko_UpdateAnimations(
self.0,
@ -1812,7 +1861,8 @@ impl<'le> TElement for GeckoElement<'le> {
hints.push(SVG_TEXT_DISABLE_ZOOM_RULE.clone());
}
}
let declarations = unsafe { Gecko_GetHTMLPresentationAttrDeclarationBlock(self.0) };
let declarations =
unsafe { Gecko_GetHTMLPresentationAttrDeclarationBlock(self.0).as_ref() };
let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>> =
declarations.and_then(|s| s.as_arc_opt());
if let Some(decl) = declarations {
@ -1821,7 +1871,7 @@ impl<'le> TElement for GeckoElement<'le> {
ServoCascadeLevel::PresHints,
));
}
let declarations = unsafe { Gecko_GetExtraContentStyleDeclarations(self.0) };
let declarations = unsafe { Gecko_GetExtraContentStyleDeclarations(self.0).as_ref() };
let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>> =
declarations.and_then(|s| s.as_arc_opt());
if let Some(decl) = declarations {
@ -1843,10 +1893,10 @@ impl<'le> TElement for GeckoElement<'le> {
);
},
VisitedHandlingMode::AllLinksUnvisited => unsafe {
Gecko_GetUnvisitedLinkAttrDeclarationBlock(self.0)
Gecko_GetUnvisitedLinkAttrDeclarationBlock(self.0).as_ref()
},
VisitedHandlingMode::RelevantLinkVisited => unsafe {
Gecko_GetVisitedLinkAttrDeclarationBlock(self.0)
Gecko_GetVisitedLinkAttrDeclarationBlock(self.0).as_ref()
},
};
let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>> =
@ -1862,7 +1912,8 @@ impl<'le> TElement for GeckoElement<'le> {
.state()
.intersects(NonTSPseudoClass::Active.state_flag());
if active {
let declarations = unsafe { Gecko_GetActiveLinkAttrDeclarationBlock(self.0) };
let declarations =
unsafe { Gecko_GetActiveLinkAttrDeclarationBlock(self.0).as_ref() };
let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>> =
declarations.and_then(|s| s.as_arc_opt());
if let Some(decl) = declarations {

View file

@ -4,11 +4,6 @@
//! Gecko's C++ bindings, along with some rust helpers to ease its use.
#[allow(dead_code, improper_ctypes, non_camel_case_types, missing_docs)]
pub mod bindings {
include!(concat!(env!("OUT_DIR"), "/gecko/bindings.rs"));
}
// FIXME: We allow `improper_ctypes` (for now), because the lint doesn't allow
// foreign structs to have `PhantomData`. We should remove this once the lint
// ignores this case.
@ -25,4 +20,6 @@ pub mod structs {
include!(concat!(env!("OUT_DIR"), "/gecko/structs.rs"));
}
pub use self::structs as bindings;
pub mod sugar;

View file

@ -135,12 +135,12 @@ pub unsafe trait HasArcFFI: HasFFI {
}
}
#[repr(C)]
/// Gecko-FFI-safe Arc (T is an ArcInner).
///
/// This can be null.
///
/// Leaks on drop. Please don't drop this.
#[repr(C)]
pub struct Strong<GeckoType> {
ptr: *const GeckoType,
_marker: PhantomData<GeckoType>,
@ -320,27 +320,6 @@ impl<GeckoType> OwnedOrNull<GeckoType> {
self.ptr.is_null()
}
/// Returns an owned pointer if this is non-null, and `None` otherwise.
pub fn into_box_opt<ServoType>(self) -> Option<Box<ServoType>>
where
ServoType: HasBoxFFI<FFIType = GeckoType>,
{
if self.is_null() {
None
} else {
Some(unsafe { transmute(self) })
}
}
/// Returns an `Owned<GeckoType>` if non-null, `None` otherwise.
pub fn into_owned_opt(self) -> Option<Owned<GeckoType>> {
if self.is_null() {
None
} else {
Some(unsafe { transmute(self) })
}
}
/// Gets a immutable reference to the underlying Gecko type, or `None` if
/// null.
pub fn borrow(&self) -> Option<&GeckoType> {

View file

@ -285,7 +285,7 @@ macro_rules! impl_threadsafe_refcount {
}
impl_threadsafe_refcount!(
structs::RawGeckoURLExtraData,
structs::mozilla::URLExtraData,
bindings::Gecko_AddRefURLExtraDataArbitraryThread,
bindings::Gecko_ReleaseURLExtraDataArbitraryThread
);

View file

@ -11,7 +11,6 @@ use crate::parallel::STYLE_THREAD_STACK_SIZE_KB;
use crate::shared_lock::SharedRwLock;
use crate::thread_state;
use rayon;
use servo_config::pref;
use std::env;
/// Global style data
@ -67,6 +66,7 @@ lazy_static! {
Ok(num) => num,
#[cfg(feature = "servo")]
_ => {
use servo_config::pref;
// We always set this pref on startup, before layout or script
// have had a chance of accessing (and thus creating) the
// thread-pool.

View file

@ -542,6 +542,10 @@ where
any_descendant |= self.invalidate_dom_descendants_of(anon_content, invalidations);
}
if let Some(marker) = self.element.marker_pseudo_element() {
any_descendant |= self.invalidate_pseudo_element_or_nac(marker, invalidations);
}
if let Some(before) = self.element.before_pseudo_element() {
any_descendant |= self.invalidate_pseudo_element_or_nac(before, invalidations);
}

View file

@ -4,12 +4,11 @@
//! Restyle hints: an optimization to avoid unnecessarily matching selectors.
#[cfg(feature = "gecko")]
use crate::gecko_bindings::structs::nsRestyleHint;
use crate::traversal_flags::TraversalFlags;
bitflags! {
/// The kind of restyle we need to do for a given element.
#[repr(C)]
pub struct RestyleHint: u8 {
/// Do a selector match of the element.
const RESTYLE_SELF = 1 << 0;
@ -190,75 +189,5 @@ impl Default for RestyleHint {
}
}
#[cfg(feature = "gecko")]
impl From<nsRestyleHint> for RestyleHint {
fn from(mut raw: nsRestyleHint) -> Self {
let mut hint = RestyleHint::empty();
debug_assert!(
raw.0 & nsRestyleHint::eRestyle_LaterSiblings.0 == 0,
"Handle later siblings manually if necessary plz."
);
if (raw.0 & (nsRestyleHint::eRestyle_Self.0 | nsRestyleHint::eRestyle_Subtree.0)) != 0 {
raw.0 &= !nsRestyleHint::eRestyle_Self.0;
hint.insert(RestyleHint::RESTYLE_SELF);
}
if (raw.0 & (nsRestyleHint::eRestyle_Subtree.0 | nsRestyleHint::eRestyle_SomeDescendants.0)) !=
0
{
raw.0 &= !nsRestyleHint::eRestyle_Subtree.0;
raw.0 &= !nsRestyleHint::eRestyle_SomeDescendants.0;
hint.insert(RestyleHint::RESTYLE_DESCENDANTS);
}
if (raw.0 & (nsRestyleHint::eRestyle_ForceDescendants.0 | nsRestyleHint::eRestyle_Force.0)) !=
0
{
raw.0 &= !nsRestyleHint::eRestyle_Force.0;
hint.insert(RestyleHint::RECASCADE_SELF);
}
if (raw.0 & nsRestyleHint::eRestyle_ForceDescendants.0) != 0 {
raw.0 &= !nsRestyleHint::eRestyle_ForceDescendants.0;
hint.insert(RestyleHint::RECASCADE_DESCENDANTS);
}
hint.insert(RestyleHint::from_bits_truncate(raw.0 as u8));
hint
}
}
#[cfg(feature = "servo")]
malloc_size_of_is_0!(RestyleHint);
/// Asserts that all replacement hints have a matching nsRestyleHint value.
#[cfg(feature = "gecko")]
#[inline]
pub fn assert_restyle_hints_match() {
use crate::gecko_bindings::structs;
macro_rules! check_restyle_hints {
( $( $a:ident => $b:path),*, ) => {
if cfg!(debug_assertions) {
let mut replacements = RestyleHint::replacements();
$(
assert_eq!(structs::nsRestyleHint::$a.0 as usize, $b.bits() as usize, stringify!($b));
replacements.remove($b);
)*
assert_eq!(replacements, RestyleHint::empty(),
"all RestyleHint replacement bits should have an \
assertion");
}
}
}
check_restyle_hints! {
eRestyle_CSSTransitions => RestyleHint::RESTYLE_CSS_TRANSITIONS,
eRestyle_CSSAnimations => RestyleHint::RESTYLE_CSS_ANIMATIONS,
eRestyle_StyleAttribute => RestyleHint::RESTYLE_STYLE_ATTRIBUTE,
eRestyle_StyleAttribute_Animations => RestyleHint::RESTYLE_SMIL,
}
}

View file

@ -102,20 +102,3 @@ macro_rules! define_keyword_type {
}
};
}
#[cfg(feature = "gecko")]
macro_rules! impl_bitflags_conversions {
($name:ident) => {
impl From<u8> for $name {
fn from(bits: u8) -> $name {
$name::from_bits(bits).expect("bits contain valid flag")
}
}
impl From<$name> for u8 {
fn from(v: $name) -> u8 {
v.bits()
}
}
};
}

View file

@ -384,8 +384,6 @@ struct Cascade<'a, 'b: 'a> {
cascade_mode: CascadeMode<'a>,
seen: LonghandIdSet,
reverted: PerOrigin<LonghandIdSet>,
saved_font_size: Option<PropertyDeclaration>,
saved_font_family: Option<PropertyDeclaration>,
}
impl<'a, 'b: 'a> Cascade<'a, 'b> {
@ -395,8 +393,6 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
cascade_mode,
seen: LonghandIdSet::default(),
reverted: Default::default(),
saved_font_size: None,
saved_font_family: None,
}
}
@ -424,33 +420,8 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
))
}
fn apply_declaration<Phase: CascadePhase>(
&mut self,
longhand_id: LonghandId,
declaration: &PropertyDeclaration,
) {
// FIXME(emilio): Find a cleaner abstraction for this.
//
// font-size and font-family are special because in Gecko they're
// they're dependent on other early props, like lang and
// -moz-min-font-size-ratio. This sucks a bit, we should ideally
// move the font-size computation code somewhere else...
if Phase::is_early() {
if longhand_id == LonghandId::FontSize {
self.saved_font_size = Some(declaration.clone());
return;
}
if longhand_id == LonghandId::FontFamily {
self.saved_font_family = Some(declaration.clone());
return;
}
}
self.apply_declaration_ignoring_phase(longhand_id, declaration);
}
#[inline(always)]
fn apply_declaration_ignoring_phase(
fn apply_declaration<Phase: CascadePhase>(
&mut self,
longhand_id: LonghandId,
declaration: &PropertyDeclaration,
@ -577,7 +548,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
}
if Phase::is_early() {
self.fixup_font_and_apply_saved_font_properties();
self.fixup_font_stuff();
self.compute_writing_mode();
} else {
self.finished_applying_properties();
@ -702,30 +673,156 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
true
}
// FIXME(emilio): It'd be really nice to simplify all this, somehow. This is
// very annoying code in lots of ways, and there are various bits about it
// which I think are broken or could be improved, see the various FIXMEs
// below.
fn fixup_font_and_apply_saved_font_properties(&mut self) {
let font_family = self.saved_font_family.take();
let font_size = self.saved_font_size.take();
let mut _skip_font_family = false;
/// The default font type (which is stored in FontFamilyList's
/// `mDefaultFontType`) depends on the current lang group and generic font
/// family, so we may need to recompute it if or the family changed.
///
/// Also, we prioritize non-document fonts here if we need to (see the pref
/// `browser.display.use_document_fonts`).
#[inline]
#[cfg(feature = "gecko")]
{
// <svg:text> is not affected by text zoom, and it uses a preshint
// to disable it. We fix up the struct when this happens by
// unzooming its contained font values, which will have been zoomed
// in the parent.
//
// FIXME(emilio): Could be cleaner if we just removed this property
// and made a style adjustment o something like that.
if self.seen.contains(LonghandId::XTextZoom) {
fn recompute_default_font_family_type_if_needed(&mut self) {
use crate::gecko_bindings::{bindings, structs};
if !self.seen.contains(LonghandId::XLang) &&
!self.seen.contains(LonghandId::FontFamily) {
return;
}
let use_document_fonts = unsafe { structs::StaticPrefs_sVarCache_browser_display_use_document_fonts != 0 };
let builder = &mut self.context.builder;
let (default_font_type, prioritize_user_fonts) = {
let font = builder.get_font().gecko();
// System fonts are all right, and should have the default font type
// set to none already, so bail out early.
if font.mFont.systemFont {
debug_assert_eq!(font.mFont.fontlist.mDefaultFontType, structs::FontFamilyType::eFamily_none);
return;
}
let default_font_type = unsafe {
bindings::Gecko_nsStyleFont_ComputeDefaultFontType(
builder.device.document(),
font.mGenericID,
font.mLanguage.mRawPtr,
)
};
// We prioritize user fonts over document fonts if the pref is set,
// and we don't have a generic family already (or we're using
// cursive or fantasy, since they're ignored, see bug 789788), and
// we have a generic family to actually replace it with.
let prioritize_user_fonts =
!use_document_fonts &&
matches!(
font.mGenericID,
structs::kGenericFont_NONE |
structs::kGenericFont_fantasy |
structs::kGenericFont_cursive
) &&
default_font_type != structs::FontFamilyType::eFamily_none;
if !prioritize_user_fonts && default_font_type == font.mFont.fontlist.mDefaultFontType {
// Nothing to do.
return;
}
(default_font_type, prioritize_user_fonts)
};
let font = builder.mutate_font().gecko_mut();
font.mFont.fontlist.mDefaultFontType = default_font_type;
if prioritize_user_fonts {
unsafe {
bindings::Gecko_nsStyleFont_PrioritizeUserFonts(font, default_font_type)
}
}
}
/// Some keyword sizes depend on the font family and language.
#[cfg(feature = "gecko")]
fn recompute_keyword_font_size_if_needed(&mut self) {
use crate::values::computed::ToComputedValue;
use crate::values::specified;
if !self.seen.contains(LonghandId::XLang) &&
!self.seen.contains(LonghandId::FontFamily) {
return;
}
let new_size = {
let font = self.context.builder.get_font();
let new_size = match font.clone_font_size().keyword_info {
Some(info) => {
self.context.for_non_inherited_property = None;
specified::FontSize::Keyword(info).to_computed_value(self.context)
}
None => return,
};
if font.gecko().mScriptUnconstrainedSize == new_size.size().0 {
return;
}
new_size
};
self.context.builder.mutate_font().set_font_size(new_size);
}
/// Some properties, plus setting font-size itself, may make us go out of
/// our minimum font-size range.
#[cfg(feature = "gecko")]
fn constrain_font_size_if_needed(&mut self) {
use crate::gecko_bindings::bindings;
if !self.seen.contains(LonghandId::XLang) &&
!self.seen.contains(LonghandId::FontFamily) &&
!self.seen.contains(LonghandId::MozMinFontSizeRatio) &&
!self.seen.contains(LonghandId::FontSize) {
return;
}
let builder = &mut self.context.builder;
let min_font_size = {
let font = builder.get_font().gecko();
let min_font_size = unsafe {
bindings::Gecko_nsStyleFont_ComputeMinSize(
font,
builder.device.document(),
)
};
if font.mFont.size >= min_font_size {
return;
}
min_font_size
};
builder.mutate_font().gecko_mut().mFont.size = min_font_size;
}
/// <svg:text> is not affected by text zoom, and it uses a preshint
/// to disable it. We fix up the struct when this happens by
/// unzooming its contained font values, which will have been zoomed
/// in the parent.
///
/// FIXME(emilio): Also, why doing this _before_ handling font-size? That
/// sounds wrong.
#[cfg(feature = "gecko")]
fn unzoom_fonts_if_needed(&mut self) {
if !self.seen.contains(LonghandId::XTextZoom) {
return;
}
let builder = &mut self.context.builder;
let parent_zoom = builder.get_parent_font().gecko().mAllowZoom;
let zoom = builder.get_font().gecko().mAllowZoom;
if zoom != parent_zoom {
if zoom == parent_zoom {
return;
}
debug_assert!(
!zoom,
"We only ever disable text zoom (in svg:text), never enable it"
@ -733,111 +830,92 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
let device = builder.device;
builder.mutate_font().unzoom_fonts(device);
}
/// MathML script* attributes do some very weird shit with font-size.
///
/// Handle them specially here, separate from other font-size stuff.
///
/// How this should interact with lang="" and font-family-dependent sizes is
/// not clear to me. For now just pretend those don't exist here.
#[cfg(feature = "gecko")]
fn handle_mathml_scriptlevel_if_needed(&mut self) {
use app_units::Au;
use std::cmp;
if !self.seen.contains(LonghandId::MozScriptLevel) &&
!self.seen.contains(LonghandId::MozScriptMinSize) &&
!self.seen.contains(LonghandId::MozScriptSizeMultiplier) {
return;
}
// Whenever a single generic value is specified, Gecko used to do a
// bunch of recalculation walking up the rule tree, including
// handling the font-size stuff.
//
// It basically repopulated the font struct with the default font
// for a given generic and language. We handle the font-size stuff
// separately, so this boils down to just copying over the
// font-family lists (no other aspect of the default font can be
// configured).
if self.seen.contains(LonghandId::XLang) || self.seen.contains(LonghandId::FontFamily) {
// If just the language changed, the inherited generic is all we
// need.
let mut generic = self.context.builder.get_parent_font().gecko().mGenericID;
// FIXME(emilio): Isn't this bogus for CSS wide keywords like
// reset or such?
if let Some(ref declaration) = font_family {
if let PropertyDeclaration::FontFamily(ref fam) = *declaration {
if let Some(id) = fam.single_generic() {
generic = id;
// In case of a specified font family with a single
// generic, we will end up setting font family
// below, but its value would get overwritten later
// in the pipeline when cascading.
//
// We instead skip cascading font-family in that
// case.
//
// In case of the language changing, we wish for a
// specified font-family to override this, so we do
// not skip cascading then.
_skip_font_family = true;
}
}
// If the user specifies a font-size, just let it be.
if self.seen.contains(LonghandId::FontSize) {
return;
}
// FIXME(emilio): Why both setting the generic and passing it
// down?
let doc = self.context.builder.device.document();
let gecko_font = self.context.builder.mutate_font().gecko_mut();
gecko_font.mGenericID = generic;
unsafe {
crate::gecko_bindings::bindings::Gecko_nsStyleFont_PrefillDefaultForGeneric(
gecko_font,
doc,
generic,
);
}
}
let builder = &mut self.context.builder;
let (new_size, new_unconstrained_size) = {
let font = builder.get_font().gecko();
let parent_font = builder.get_parent_font().gecko();
let delta =
font.mScriptLevel.saturating_sub(parent_font.mScriptLevel);
if delta == 0 {
return;
}
// It is important that font-size is computed before the late
// properties (for em units), but after font-family (for the
// base-font-size dependence for default and keyword font-sizes).
//
// It's important that font-family comes after the other font properties
// to support system fonts.
//
// NOTE(emilio): I haven't verified that comment, but it was there.
// Verify, and if it's false make font-size the only weird property?
if !_skip_font_family {
if let Some(ref declaration) = font_family {
self.apply_declaration_ignoring_phase(LonghandId::FontFamily, declaration);
let mut min = Au(parent_font.mScriptMinSize);
if font.mAllowZoom {
min = builder.device.zoom_text(min);
}
let scale = (parent_font.mScriptSizeMultiplier as f32).powi(delta as i32);
let parent_size = Au(parent_font.mSize);
let parent_unconstrained_size = Au(parent_font.mScriptUnconstrainedSize);
let new_size = parent_size.scale_by(scale);
let new_unconstrained_size = parent_unconstrained_size.scale_by(scale);
if scale <= 1. {
// The parent size can be smaller than scriptminsize, e.g. if it
// was specified explicitly. Don't scale in this case, but we
// don't want to set it to scriptminsize either since that will
// make it larger.
if parent_size <= min {
(parent_size, new_unconstrained_size)
} else {
(cmp::max(min, new_size), new_unconstrained_size)
}
} else {
// If the new unconstrained size is larger than the min size,
// this means we have escaped the grasp of scriptminsize and can
// revert to using the unconstrained size.
// However, if the new size is even larger (perhaps due to usage
// of em units), use that instead.
(
cmp::min(new_size, cmp::max(new_unconstrained_size, min)),
new_unconstrained_size
)
}
};
let font = builder.mutate_font().gecko_mut();
font.mFont.size = new_size.0;
font.mSize = new_size.0;
font.mScriptUnconstrainedSize = new_unconstrained_size.0;
}
/// Various properties affect how font-size and font-family are computed.
///
/// These need to be handled here, since relative lengths and ex / ch units
/// for late properties depend on these.
fn fixup_font_stuff(&mut self) {
#[cfg(feature = "gecko")]
{
let context = &mut self.context;
let device = context.builder.device;
if let PropertyDeclaration::FontFamily(ref val) = *declaration {
if val.get_system().is_some() {
let default = context
.cached_system_font
.as_ref()
.unwrap()
.default_font_type;
context.builder.mutate_font().fixup_system(default);
} else {
context.builder.mutate_font().fixup_none_generic(device);
}
}
}
}
}
if let Some(declaration) = font_size {
self.apply_declaration_ignoring_phase(LonghandId::FontSize, &declaration);
} else {
#[cfg(feature = "gecko")]
{
if self.seen.contains(LonghandId::XLang) ||
self.seen.contains(LonghandId::MozScriptLevel) ||
self.seen.contains(LonghandId::MozMinFontSizeRatio) ||
self.seen.contains(LonghandId::FontFamily)
{
use crate::values::computed::FontSize;
// font-size must be explicitly inherited to handle lang
// changes and scriptlevel changes.
//
// FIXME(emilio): That looks a bit bogus...
self.context.for_non_inherited_property = None;
FontSize::cascade_inherit_font_size(&mut self.context);
}
}
self.unzoom_fonts_if_needed();
self.recompute_default_font_family_type_if_needed();
self.recompute_keyword_font_size_if_needed();
self.handle_mathml_scriptlevel_if_needed();
self.constrain_font_size_if_needed()
}
}
}

View file

@ -56,10 +56,13 @@ bitflags! {
/// Whether the child explicitly inherits any reset property.
const INHERITS_RESET_STYLE = 1 << 8;
/// Whether any value on our style is font-metric-dependent.
const DEPENDS_ON_FONT_METRICS = 1 << 9;
/// Whether the style or any of the ancestors has a multicol style.
///
/// Only used in Servo.
const CAN_BE_FRAGMENTED = 1 << 9;
const CAN_BE_FRAGMENTED = 1 << 10;
}
}
@ -94,3 +97,22 @@ impl ComputedValueFlags {
self & Self::maybe_inherited_flags()
}
}
/// Asserts that the relevant servo and Gecko representations match.
#[cfg(feature = "gecko")]
#[inline]
pub fn assert_match() {
use crate::gecko_bindings::structs;
macro_rules! assert_bit {
($rust:ident, $cpp:ident) => {
debug_assert_eq!(ComputedValueFlags::$rust.bits, structs::$cpp);
}
}
assert_bit!(HAS_TEXT_DECORATION_LINES, ComputedStyleBit_HasTextDecorationLines);
assert_bit!(IS_IN_PSEUDO_ELEMENT_SUBTREE, ComputedStyleBit_HasPseudoElementData);
assert_bit!(SHOULD_SUPPRESS_LINEBREAK, ComputedStyleBit_SuppressLineBreak);
assert_bit!(IS_TEXT_COMBINED, ComputedStyleBit_IsTextCombined);
assert_bit!(IS_RELEVANT_LINK_VISITED, ComputedStyleBit_RelevantLinkVisited);
assert_bit!(DEPENDS_ON_FONT_METRICS, ComputedStyleBit_DependsOnFontMetrics);
}

View file

@ -324,6 +324,7 @@ class Longhand(object):
"JustifyItems",
"JustifySelf",
"MozForceBrokenImageIcon",
"MozListReversed",
"MozScriptLevel",
"MozScriptMinSize",
"MozScriptSizeMultiplier",

View file

@ -66,16 +66,25 @@ use crate::values::generics::url::UrlOrNone;
pub mod style_structs {
% for style_struct in data.style_structs:
pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};
unsafe impl Send for ${style_struct.name} {}
unsafe impl Sync for ${style_struct.name} {}
% endfor
}
/// FIXME(emilio): This is completely duplicated with the other properties code.
pub type ComputedValuesInner = crate::gecko_bindings::structs::ServoComputedData;
pub type ComputedValuesInner = structs::ServoComputedData;
#[repr(C)]
pub struct ComputedValues(crate::gecko_bindings::structs::mozilla::ComputedStyle);
pub struct ComputedValues(structs::mozilla::ComputedStyle);
impl ComputedValues {
#[inline]
pub (crate) fn as_gecko_computed_style(&self) -> &structs::ComputedStyle {
&self.0
}
pub fn new(
pseudo: Option<<&PseudoElement>,
custom_properties: Option<Arc<CustomPropertiesMap>>,
@ -929,7 +938,7 @@ transform_functions = [
debug_assert!(!${item}${index + 1}.0.is_empty());
% endif
${css_value_setters[item] % (
"bindings::Gecko_CSSValue_GetArrayItem(gecko_value, %d)" % (index + 1),
"(&mut *bindings::Gecko_CSSValue_GetArrayItem(gecko_value, %d))" % (index + 1),
item + str(index + 1)
)};
% endfor
@ -980,7 +989,7 @@ transform_functions = [
% endif
<%
getter = css_value_getters[item] % (
"bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, %d)" % (index + 1)
"(&*bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, %d))" % (index + 1)
)
%>
${getter},
@ -989,6 +998,7 @@ transform_functions = [
},
</%def>
#[allow(unused_parens)]
fn set_single_transform_function(
servo_value: &values::computed::TransformOperation,
gecko_value: &mut structs::nsCSSValue /* output */
@ -1031,6 +1041,7 @@ pub fn convert_transform(
output.set_move(list);
}
#[allow(unused_parens)]
fn clone_single_transform_function(
gecko_value: &structs::nsCSSValue
) -> values::computed::TransformOperation {
@ -1236,6 +1247,7 @@ impl Clone for ${style_struct.gecko_struct_name} {
"Length": impl_absolute_length,
"LengthOrNormal": impl_style_coord,
"LengthPercentageOrAuto": impl_style_coord,
"MozListReversed": impl_simple,
"MozScriptMinSize": impl_absolute_length,
"RGBAColor": impl_rgba_color,
"SVGLength": impl_svg_length,
@ -1971,25 +1983,19 @@ fn static_assert() {
<% impl_font_settings("font_feature_settings", "gfxFontFeature", "FeatureTagValue", "i32", "u32") %>
<% impl_font_settings("font_variation_settings", "gfxFontVariation", "VariationValue", "f32", "f32") %>
pub fn fixup_none_generic(&mut self, device: &Device) {
self.gecko.mFont.systemFont = false;
unsafe {
bindings::Gecko_nsStyleFont_FixupNoneGeneric(&mut self.gecko, device.document())
}
}
pub fn fixup_system(&mut self, default_font_type: structs::FontFamilyType) {
self.gecko.mFont.systemFont = true;
self.gecko.mGenericID = structs::kGenericFont_NONE;
self.gecko.mFont.fontlist.mDefaultFontType = default_font_type;
}
pub fn set_font_family(&mut self, v: longhands::font_family::computed_value::T) {
self.gecko.mGenericID = structs::kGenericFont_NONE;
if let Some(generic) = v.0.single_generic() {
self.gecko.mGenericID = generic;
}
self.gecko.mFont.fontlist.mFontlist.mBasePtr.set_move((v.0).0.clone());
use crate::gecko_bindings::structs::FontFamilyType;
let is_system_font = v.is_system_font;
self.gecko.mFont.systemFont = is_system_font;
self.gecko.mGenericID = if is_system_font {
structs::kGenericFont_NONE
} else {
v.families.single_generic().unwrap_or(structs::kGenericFont_NONE)
};
self.gecko.mFont.fontlist.mFontlist.mBasePtr.set_move(v.families.0.clone());
// Fixed-up if needed in Cascade::fixup_font_stuff.
self.gecko.mFont.fontlist.mDefaultFontType = FontFamilyType::eFamily_none;
}
pub fn copy_font_family_from(&mut self, other: &Self) {
@ -2009,7 +2015,7 @@ fn static_assert() {
let fontlist = &self.gecko.mFont.fontlist;
let shared_fontlist = unsafe { fontlist.mFontlist.mBasePtr.to_safe() };
if shared_fontlist.mNames.is_empty() {
let families = if shared_fontlist.mNames.is_empty() {
let default = fontlist.mDefaultFontType;
let default = match default {
FontFamilyType::eFamily_serif => {
@ -2030,9 +2036,14 @@ fn static_assert() {
SingleFontFamily::Generic(atom!("sans-serif"))
}
};
FontFamily(FontFamilyList::new(Box::new([default])))
FontFamilyList::new(Box::new([default]))
} else {
FontFamily(FontFamilyList(shared_fontlist))
FontFamilyList(shared_fontlist)
};
FontFamily {
families,
is_system_font: self.gecko.mFont.systemFont,
}
}
@ -2042,10 +2053,32 @@ fn static_assert() {
self.gecko.mFont.size = device.unzoom_text(Au(self.gecko.mFont.size)).0;
}
pub fn copy_font_size_from(&mut self, other: &Self) {
self.gecko.mScriptUnconstrainedSize = other.gecko.mScriptUnconstrainedSize;
self.gecko.mSize = other.gecko.mScriptUnconstrainedSize;
self.gecko.mFont.size = other.gecko.mSize;
self.gecko.mFontSizeKeyword = other.gecko.mFontSizeKeyword;
// TODO(emilio): Should we really copy over these two?
self.gecko.mFontSizeFactor = other.gecko.mFontSizeFactor;
self.gecko.mFontSizeOffset = other.gecko.mFontSizeOffset;
}
pub fn reset_font_size(&mut self, other: &Self) {
self.copy_font_size_from(other)
}
pub fn set_font_size(&mut self, v: FontSize) {
use crate::values::generics::font::KeywordSize;
self.gecko.mSize = v.size().0;
self.gecko.mScriptUnconstrainedSize = v.size().0;
let size = v.size();
self.gecko.mScriptUnconstrainedSize = size.0;
// These two may be changed from Cascade::fixup_font_stuff.
self.gecko.mSize = size.0;
self.gecko.mFont.size = size.0;
if let Some(info) = v.keyword_info {
self.gecko.mFontSizeKeyword = match info.kw {
KeywordSize::XXSmall => structs::NS_STYLE_FONT_SIZE_XXSMALL,
@ -2066,196 +2099,6 @@ fn static_assert() {
}
}
/// Set font size, taking into account scriptminsize and scriptlevel
/// Returns Some(size) if we have to recompute the script unconstrained size
pub fn apply_font_size(
&mut self,
v: FontSize,
parent: &Self,
device: &Device,
) -> Option<NonNegativeLength> {
let (adjusted_size, adjusted_unconstrained_size) =
self.calculate_script_level_size(parent, device);
// In this case, we have been unaffected by scriptminsize, ignore it
if parent.gecko.mSize == parent.gecko.mScriptUnconstrainedSize &&
adjusted_size == adjusted_unconstrained_size {
self.set_font_size(v);
self.fixup_font_min_size(device);
None
} else {
self.gecko.mSize = v.size().0;
self.fixup_font_min_size(device);
Some(Au(parent.gecko.mScriptUnconstrainedSize).into())
}
}
pub fn fixup_font_min_size(&mut self, device: &Device) {
unsafe { bindings::Gecko_nsStyleFont_FixupMinFontSize(&mut self.gecko, device.document()) }
}
pub fn apply_unconstrained_font_size(&mut self, v: NonNegativeLength) {
self.gecko.mScriptUnconstrainedSize = v.0.to_i32_au();
}
/// Calculates the constrained and unconstrained font sizes to be inherited
/// from the parent.
///
/// This is a port of Gecko's old ComputeScriptLevelSize function:
/// https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#3103
///
/// scriptlevel is a property that affects how font-size is inherited. If scriptlevel is
/// +1, for example, it will inherit as the script size multiplier times
/// the parent font. This does not affect cases where the font-size is
/// explicitly set.
///
/// However, this transformation is not allowed to reduce the size below
/// scriptminsize. If this inheritance will reduce it to below
/// scriptminsize, it will be set to scriptminsize or the parent size,
/// whichever is smaller (the parent size could be smaller than the min size
/// because it was explicitly specified).
///
/// Now, within a node that has inherited a font-size which was
/// crossing scriptminsize once the scriptlevel was applied, a negative
/// scriptlevel may be used to increase the size again.
///
/// This should work, however if we have already been capped by the
/// scriptminsize multiple times, this can lead to a jump in the size.
///
/// For example, if we have text of the form:
///
/// huge large medium small tiny reallytiny tiny small medium huge
///
/// which is represented by progressive nesting and scriptlevel values of
/// +1 till the center after which the scriptlevel is -1, the "tiny"s should
/// be the same size, as should be the "small"s and "medium"s, etc.
///
/// However, if scriptminsize kicked it at around "medium", then
/// medium/tiny/reallytiny will all be the same size (the min size).
/// A -1 scriptlevel change after this will increase the min size by the
/// multiplier, making the second tiny larger than medium.
///
/// Instead, we wish for the second "tiny" to still be capped by the script
/// level, and when we reach the second "large", it should be the same size
/// as the original one.
///
/// We do this by cascading two separate font sizes. The font size (mSize)
/// is the actual displayed font size. The unconstrained font size
/// (mScriptUnconstrainedSize) is the font size in the situation where
/// scriptminsize never applied.
///
/// We calculate the proposed inherited font size based on scriptlevel and
/// the parent unconstrained size, instead of using the parent font size.
/// This is stored in the node's unconstrained size and will also be stored
/// in the font size provided that it is above the min size.
///
/// All of this only applies when inheriting. When the font size is
/// manually set, scriptminsize does not apply, and both the real and
/// unconstrained size are set to the explicit value. However, if the font
/// size is manually set to an em or percent unit, the unconstrained size
/// will be set to the value of that unit computed against the parent
/// unconstrained size, whereas the font size will be set computing against
/// the parent font size.
pub fn calculate_script_level_size(&self, parent: &Self, device: &Device) -> (Au, Au) {
use std::cmp;
let delta = self.gecko.mScriptLevel.saturating_sub(parent.gecko.mScriptLevel);
let parent_size = Au(parent.gecko.mSize);
let parent_unconstrained_size = Au(parent.gecko.mScriptUnconstrainedSize);
if delta == 0 {
return (parent_size, parent_unconstrained_size)
}
let mut min = Au(parent.gecko.mScriptMinSize);
if self.gecko.mAllowZoom {
min = device.zoom_text(min);
}
let scale = (parent.gecko.mScriptSizeMultiplier as f32).powi(delta as i32);
let new_size = parent_size.scale_by(scale);
let new_unconstrained_size = parent_unconstrained_size.scale_by(scale);
if scale < 1. {
// The parent size can be smaller than scriptminsize,
// e.g. if it was specified explicitly. Don't scale
// in this case, but we don't want to set it to scriptminsize
// either since that will make it larger.
if parent_size < min {
(parent_size, new_unconstrained_size)
} else {
(cmp::max(min, new_size), new_unconstrained_size)
}
} else {
// If the new unconstrained size is larger than the min size,
// this means we have escaped the grasp of scriptminsize
// and can revert to using the unconstrained size.
// However, if the new size is even larger (perhaps due to usage
// of em units), use that instead.
(cmp::min(new_size, cmp::max(new_unconstrained_size, min)),
new_unconstrained_size)
}
}
/// This function will also handle scriptminsize and scriptlevel
/// so should not be called when you just want the font sizes to be copied.
/// Hence the different name.
pub fn inherit_font_size_from(&mut self, parent: &Self,
kw_inherited_size: Option<NonNegativeLength>,
device: &Device) {
let (adjusted_size, adjusted_unconstrained_size)
= self.calculate_script_level_size(parent, device);
if adjusted_size.0 != parent.gecko.mSize ||
adjusted_unconstrained_size.0 != parent.gecko.mScriptUnconstrainedSize {
// FIXME(Manishearth): This is incorrect. When there is both a
// keyword size being inherited and a scriptlevel change, we must
// handle the keyword size the same way we handle em units. This
// complicates things because we now have to keep track of the
// adjusted and unadjusted ratios in the kw font size. This only
// affects the use case of a generic font being used in MathML.
//
// If we were to fix this I would prefer doing it not doing
// something like the ruletree walk that Gecko used to do in
// nsRuleNode::SetGenericFont and instead using extra bookkeeping in
// the mSize and mScriptUnconstrainedSize values, and reusing those
// instead of font_size_keyword.
// In the case that MathML has given us an adjusted size, apply it.
// Keep track of the unconstrained adjusted size.
self.gecko.mSize = adjusted_size.0;
// Technically the MathML constrained size may also be keyword-derived
// but we ignore this since it would be too complicated
// to correctly track and it's mostly unnecessary.
self.gecko.mFontSizeKeyword = structs::NS_STYLE_FONT_SIZE_NO_KEYWORD as u8;
self.gecko.mFontSizeFactor = 1.;
self.gecko.mFontSizeOffset = 0;
self.gecko.mScriptUnconstrainedSize = adjusted_unconstrained_size.0;
} else if let Some(size) = kw_inherited_size {
// Parent element was a keyword-derived size.
self.gecko.mSize = size.0.to_i32_au();
// Copy keyword info over.
self.gecko.mFontSizeFactor = parent.gecko.mFontSizeFactor;
self.gecko.mFontSizeOffset = parent.gecko.mFontSizeOffset;
self.gecko.mFontSizeKeyword = parent.gecko.mFontSizeKeyword;
// MathML constraints didn't apply here, so we can ignore this.
self.gecko.mScriptUnconstrainedSize = size.0.to_i32_au();
} else {
// MathML isn't affecting us, and our parent element does not
// have a keyword-derived size. Set things normally.
self.gecko.mSize = parent.gecko.mSize;
// copy keyword info over
self.gecko.mFontSizeKeyword = structs::NS_STYLE_FONT_SIZE_NO_KEYWORD as u8;
self.gecko.mFontSizeFactor = 1.;
self.gecko.mFontSizeOffset = 0;
self.gecko.mScriptUnconstrainedSize = parent.gecko.mScriptUnconstrainedSize;
}
self.fixup_font_min_size(device);
}
pub fn clone_font_size(&self) -> FontSize {
use crate::values::generics::font::{KeywordInfo, KeywordSize};
let size = Au(self.gecko.mSize).into();
@ -2270,16 +2113,16 @@ fn static_assert() {
structs::NS_STYLE_FONT_SIZE_XXXLARGE => KeywordSize::XXXLarge,
structs::NS_STYLE_FONT_SIZE_NO_KEYWORD => {
return FontSize {
size: size,
size,
keyword_info: None,
}
}
_ => unreachable!("mFontSizeKeyword should be an absolute keyword or NO_KEYWORD")
};
FontSize {
size: size,
size,
keyword_info: Some(KeywordInfo {
kw: kw,
kw,
factor: self.gecko.mFontSizeFactor,
offset: Au(self.gecko.mFontSizeOffset).into()
})
@ -2751,7 +2594,7 @@ fn static_assert() {
transform-style
rotate scroll-snap-points-x scroll-snap-points-y
scroll-snap-coordinate -moz-binding will-change
offset-path shape-outside contain touch-action
offset-path shape-outside
translate scale""" %>
<%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
#[inline]
@ -3109,10 +2952,10 @@ fn static_assert() {
pub fn set_will_change(&mut self, v: longhands::will_change::computed_value::T) {
use crate::gecko_bindings::bindings::{Gecko_AppendWillChange, Gecko_ClearWillChange};
use crate::properties::longhands::will_change::computed_value::T;
use crate::values::specified::box_::{WillChangeBits, WillChange};
match v {
T::AnimateableFeatures { features, bits } => {
WillChange::AnimateableFeatures { features, bits } => {
unsafe {
Gecko_ClearWillChange(&mut self.gecko, features.len());
}
@ -3123,13 +2966,13 @@ fn static_assert() {
}
}
self.gecko.mWillChangeBitField = bits.bits();
self.gecko.mWillChangeBitField = bits;
},
T::Auto => {
WillChange::Auto => {
unsafe {
Gecko_ClearWillChange(&mut self.gecko, 0);
}
self.gecko.mWillChangeBitField = 0;
self.gecko.mWillChangeBitField = WillChangeBits::empty();
},
};
}
@ -3139,7 +2982,7 @@ fn static_assert() {
self.gecko.mWillChangeBitField = other.gecko.mWillChangeBitField;
unsafe {
Gecko_CopyWillChangeFrom(&mut self.gecko, &other.gecko as *const _ as *mut _);
Gecko_CopyWillChangeFrom(&mut self.gecko, &other.gecko);
}
}
@ -3148,116 +2991,27 @@ fn static_assert() {
}
pub fn clone_will_change(&self) -> longhands::will_change::computed_value::T {
use crate::properties::longhands::will_change::computed_value::T;
use crate::gecko_bindings::structs::nsAtom;
use crate::values::CustomIdent;
use crate::values::specified::box_::WillChangeBits;
use crate::values::specified::box_::WillChange;
if self.gecko.mWillChange.len() == 0 {
return T::Auto
return WillChange::Auto
}
let custom_idents: Vec<CustomIdent> = self.gecko.mWillChange.iter().map(|gecko_atom| {
unsafe {
CustomIdent(Atom::from_raw(gecko_atom.mRawPtr as *mut nsAtom))
CustomIdent(Atom::from_raw(gecko_atom.mRawPtr))
}
}).collect();
T::AnimateableFeatures {
WillChange::AnimateableFeatures {
features: custom_idents.into_boxed_slice(),
bits: WillChangeBits::from_bits_truncate(self.gecko.mWillChangeBitField),
bits: self.gecko.mWillChangeBitField,
}
}
<% impl_shape_source("shape_outside", "mShapeOutside") %>
pub fn set_contain(&mut self, v: longhands::contain::computed_value::T) {
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_NONE;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_STRICT;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_SIZE;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_LAYOUT;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_BITS;
use crate::properties::longhands::contain::SpecifiedValue;
if v.is_empty() {
self.gecko.mContain = NS_STYLE_CONTAIN_NONE as u8;
return;
}
if v.contains(SpecifiedValue::STRICT) {
self.gecko.mContain = (NS_STYLE_CONTAIN_STRICT | NS_STYLE_CONTAIN_ALL_BITS) as u8;
return;
}
if v.contains(SpecifiedValue::CONTENT) {
self.gecko.mContain = (NS_STYLE_CONTAIN_CONTENT | NS_STYLE_CONTAIN_CONTENT_BITS) as u8;
return;
}
let mut bitfield = 0;
if v.contains(SpecifiedValue::LAYOUT) {
bitfield |= NS_STYLE_CONTAIN_LAYOUT;
}
if v.contains(SpecifiedValue::PAINT) {
bitfield |= NS_STYLE_CONTAIN_PAINT;
}
if v.contains(SpecifiedValue::SIZE) {
bitfield |= NS_STYLE_CONTAIN_SIZE;
}
self.gecko.mContain = bitfield as u8;
}
pub fn clone_contain(&self) -> longhands::contain::computed_value::T {
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_STRICT;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_SIZE;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_LAYOUT;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_BITS;
use crate::properties::longhands::contain::{self, SpecifiedValue};
let mut servo_flags = contain::computed_value::T::empty();
let gecko_flags = self.gecko.mContain;
if gecko_flags & (NS_STYLE_CONTAIN_STRICT as u8) != 0 {
debug_assert_eq!(
gecko_flags & (NS_STYLE_CONTAIN_ALL_BITS as u8),
NS_STYLE_CONTAIN_ALL_BITS as u8,
"When strict is specified, ALL_BITS should be specified as well"
);
servo_flags.insert(SpecifiedValue::STRICT | SpecifiedValue::STRICT_BITS);
return servo_flags;
}
if gecko_flags & (NS_STYLE_CONTAIN_CONTENT as u8) != 0 {
debug_assert_eq!(
gecko_flags & (NS_STYLE_CONTAIN_CONTENT_BITS as u8),
NS_STYLE_CONTAIN_CONTENT_BITS as u8,
"When content is specified, CONTENT_BITS should be specified as well"
);
servo_flags.insert(SpecifiedValue::CONTENT | SpecifiedValue::CONTENT_BITS);
return servo_flags;
}
if gecko_flags & (NS_STYLE_CONTAIN_LAYOUT as u8) != 0 {
servo_flags.insert(SpecifiedValue::LAYOUT);
}
if gecko_flags & (NS_STYLE_CONTAIN_PAINT as u8) != 0 {
servo_flags.insert(SpecifiedValue::PAINT);
}
if gecko_flags & (NS_STYLE_CONTAIN_SIZE as u8) != 0 {
servo_flags.insert(SpecifiedValue::SIZE);
}
return servo_flags;
}
${impl_simple_copy("contain", "mContain")}
${impl_simple_type_with_conversion("touch_action")}
pub fn set_offset_path(&mut self, v: longhands::offset_path::computed_value::T) {
use crate::gecko_bindings::bindings::{Gecko_NewStyleMotion, Gecko_SetStyleMotion};
use crate::gecko_bindings::structs::StyleShapeSourceType;
@ -4250,9 +4004,7 @@ fn static_assert() {
</%self:impl_trait>
<%self:impl_trait style_struct_name="Text"
skip_longhands="text-decoration-line text-overflow initial-letter">
${impl_simple_type_with_conversion("text_decoration_line")}
skip_longhands="text-overflow initial-letter">
fn clear_overflow_sides_if_string(&mut self) {
use crate::gecko_bindings::structs::nsStyleTextOverflowSide;
@ -4366,21 +4118,6 @@ fn static_assert() {
InitialLetter::Specified(self.gecko.mInitialLetterSize, Some(self.gecko.mInitialLetterSink))
}
}
#[inline]
pub fn has_underline(&self) -> bool {
(self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE as u8)) != 0
}
#[inline]
pub fn has_overline(&self) -> bool {
(self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_OVERLINE as u8)) != 0
}
#[inline]
pub fn has_line_through(&self) -> bool {
(self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH as u8)) != 0
}
</%self:impl_trait>
// Set SVGPathData to StyleShapeSource.
@ -4823,7 +4560,7 @@ clip-path
</%self:impl_trait>
<%self:impl_trait style_struct_name="Counters"
skip_longhands="content counter-increment counter-reset">
skip_longhands="content counter-increment counter-reset counter-set">
pub fn ineffective_content_property(&self) -> bool {
self.gecko.mContents.is_empty()
}
@ -5052,7 +4789,7 @@ clip-path
)
}
% for counter_property in ["Increment", "Reset"]:
% for counter_property in ["Increment", "Reset", "Set"]:
pub fn set_counter_${counter_property.lower()}(
&mut self,
v: longhands::counter_${counter_property.lower()}::computed_value::T

View file

@ -332,13 +332,9 @@
CSSWideKeyword::Initial => {
% if not property.style_struct.inherited:
debug_assert!(false, "Should be handled in apply_properties");
% else:
% if property.name == "font-size":
computed::FontSize::cascade_initial_font_size(context);
% else:
context.builder.reset_${property.ident}();
% endif
% endif
},
% if property.style_struct.inherited:
CSSWideKeyword::Unset |
@ -394,16 +390,8 @@
% else:
let computed = specified_value.to_computed_value(context);
% endif
% if property.ident == "font_size":
specified::FontSize::cascade_specified_font_size(
context,
&specified_value,
computed,
);
% else:
context.builder.set_${property.ident}(computed)
% endif
% endif
}
pub fn parse_declared<'i, 't>(

View file

@ -365,7 +365,7 @@ ${helpers.predefined_type(
"generics::transform::Rotate::None",
animation_value_type="ComputedValue",
boxed=True,
flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR",
gecko_pref="layout.css.individual-transform.enabled",
spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms",
servo_restyle_damage = "reflow_out_of_flow",
@ -377,7 +377,7 @@ ${helpers.predefined_type(
"generics::transform::Scale::None",
animation_value_type="ComputedValue",
boxed=True,
flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR",
gecko_pref="layout.css.individual-transform.enabled",
spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms",
servo_restyle_damage = "reflow_out_of_flow",
@ -389,7 +389,7 @@ ${helpers.predefined_type(
"generics::transform::Translate::None",
animation_value_type="ComputedValue",
boxed=True,
flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR",
gecko_pref="layout.css.individual-transform.enabled",
spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms",
servo_restyle_damage="reflow_out_of_flow",

View file

@ -27,9 +27,19 @@ ${helpers.predefined_type(
${helpers.predefined_type(
"counter-reset",
"CounterReset",
"CounterSetOrReset",
initial_value="Default::default()",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-reset",
servo_restyle_damage="rebuild_and_reflow",
)}
${helpers.predefined_type(
"counter-set",
"CounterSetOrReset",
initial_value="Default::default()",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-set",
servo_restyle_damage="rebuild_and_reflow",
products="gecko",
)}

View file

@ -379,7 +379,7 @@ ${helpers.predefined_type(
use crate::gecko_bindings::structs::{LookAndFeel_FontID, nsFont};
use std::mem;
use crate::values::computed::Percentage;
use crate::values::computed::font::{FontSize, FontStretch, FontStyle, FontFamilyList};
use crate::values::computed::font::{FontFamily, FontSize, FontStretch, FontStyle, FontFamilyList};
use crate::values::generics::NonNegative;
let id = match *self {
@ -405,11 +405,12 @@ ${helpers.predefined_type(
})));
let font_style = FontStyle::from_gecko(system.style);
let ret = ComputedSystemFont {
font_family: longhands::font_family::computed_value::T(
FontFamilyList(
unsafe { system.fontlist.mFontlist.mBasePtr.to_safe() }
)
),
font_family: FontFamily {
families: FontFamilyList(unsafe {
system.fontlist.mFontlist.mBasePtr.to_safe()
}),
is_system_font: true,
},
font_size: FontSize {
size: Au(system.size).into(),
keyword_info: None

View file

@ -68,3 +68,15 @@ ${helpers.predefined_type(
boxed=True,
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-image-region)",
)}
${helpers.predefined_type(
"-moz-list-reversed",
"MozListReversed",
"computed::MozListReversed::False",
animation_value_type="discrete",
products="gecko",
enabled_in="ua",
needs_context=False,
spec="Internal implementation detail for <ol reversed>",
servo_restyle_damage="rebuild_and_reflow",
)}

View file

@ -5,16 +5,7 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Method %>
<% data.new_style_struct(
"Text",
inherited=False,
gecko_name="TextReset",
additional_methods=[
Method("has_underline", "bool"),
Method("has_overline", "bool"),
Method("has_line_through", "bool"),
]
) %>
<% data.new_style_struct("Text", inherited=False, gecko_name="TextReset") %>
${helpers.predefined_type(
"text-overflow",

View file

@ -45,6 +45,7 @@ use crate::rule_tree::StrongRuleNode;
use crate::Zero;
use self::computed_value_flags::*;
use crate::str::{CssString, CssStringBorrow, CssStringWriter};
use std::cell::Cell;
pub use self::declaration_block::*;
pub use self::cascade::*;
@ -1257,8 +1258,12 @@ impl LonghandId {
LonghandId::MozScriptLevel |
% endif
// Needed to compute font-relative lengths correctly.
// Needed to compute the first available font, in order to
// compute font-relative units correctly.
LonghandId::FontSize |
LonghandId::FontWeight |
LonghandId::FontStretch |
LonghandId::FontStyle |
LonghandId::FontFamily |
// Needed to resolve currentcolor at computed value time properly.
@ -2634,24 +2639,6 @@ pub mod style_structs {
use crate::Zero;
!self.outline_width.is_zero()
}
% elif style_struct.name == "Text":
/// Whether the text decoration has an underline.
#[inline]
pub fn has_underline(&self) -> bool {
self.text_decoration_line.contains(longhands::text_decoration_line::SpecifiedValue::UNDERLINE)
}
/// Whether the text decoration has an overline.
#[inline]
pub fn has_overline(&self) -> bool {
self.text_decoration_line.contains(longhands::text_decoration_line::SpecifiedValue::OVERLINE)
}
/// Whether the text decoration has a line through.
#[inline]
pub fn has_line_through(&self) -> bool {
self.text_decoration_line.contains(longhands::text_decoration_line::SpecifiedValue::LINE_THROUGH)
}
% elif style_struct.name == "Box":
/// Sets the display property, but without touching original_display,
/// except when the adjustment comes from root or item display fixups.
@ -3336,8 +3323,10 @@ pub struct StyleBuilder<'a> {
///
/// TODO(emilio): Make private.
pub writing_mode: WritingMode,
/// Flags for the computed value.
pub flags: ComputedValueFlags,
pub flags: Cell<ComputedValueFlags>,
/// The element's style if visited, only computed if there's a relevant link
/// for this element. A element's "relevant link" is the element being
/// matched if it is a link or the nearest ancestor link.
@ -3379,7 +3368,7 @@ impl<'a> StyleBuilder<'a> {
modified_reset: false,
custom_properties,
writing_mode: inherited_style.writing_mode,
flags,
flags: Cell::new(flags),
visited_style: None,
% for style_struct in data.active_style_structs():
% if style_struct.inherited:
@ -3418,7 +3407,7 @@ impl<'a> StyleBuilder<'a> {
rules: None,
custom_properties: style_to_derive_from.custom_properties().cloned(),
writing_mode: style_to_derive_from.writing_mode,
flags: style_to_derive_from.flags,
flags: Cell::new(style_to_derive_from.flags),
visited_style: None,
% for style_struct in data.active_style_structs():
${style_struct.ident}: StyleStructRef::Borrowed(
@ -3448,14 +3437,14 @@ impl<'a> StyleBuilder<'a> {
.get_${property.style_struct.name_lower}();
self.modified_reset = true;
self.flags.insert(ComputedValueFlags::INHERITS_RESET_STYLE);
self.add_flags(ComputedValueFlags::INHERITS_RESET_STYLE);
% if property.ident == "content":
self.flags.insert(ComputedValueFlags::INHERITS_CONTENT);
self.add_flags(ComputedValueFlags::INHERITS_CONTENT);
% endif
% if property.ident == "display":
self.flags.insert(ComputedValueFlags::INHERITS_DISPLAY);
self.add_flags(ComputedValueFlags::INHERITS_DISPLAY);
% endif
if self.${property.style_struct.ident}.ptr_eq(inherited_struct) {
@ -3470,7 +3459,7 @@ impl<'a> StyleBuilder<'a> {
% endif
);
}
% elif property.name != "font-size":
% else:
/// Reset `${property.ident}` to the initial value.
#[allow(non_snake_case)]
pub fn reset_${property.ident}(&mut self) {
@ -3642,13 +3631,33 @@ impl<'a> StyleBuilder<'a> {
self.modified_reset
}
/// Return the current flags.
#[inline]
pub fn flags(&self) -> ComputedValueFlags {
self.flags.get()
}
/// Add a flag to the current builder.
#[inline]
pub fn add_flags(&self, flag: ComputedValueFlags) {
let flags = self.flags() | flag;
self.flags.set(flags);
}
/// Removes a flag to the current builder.
#[inline]
pub fn remove_flags(&self, flag: ComputedValueFlags) {
let flags = self.flags() & !flag;
self.flags.set(flags);
}
/// Turns this `StyleBuilder` into a proper `ComputedValues` instance.
pub fn build(self) -> Arc<ComputedValues> {
ComputedValues::new(
self.pseudo,
self.custom_properties,
self.writing_mode,
self.flags,
self.flags.get(),
self.rules,
self.visited_style,
% for style_struct in data.active_style_structs():
@ -3710,8 +3719,8 @@ mod lazy_static_module {
use super::{ComputedValues, ComputedValuesInner, longhands, style_structs};
use super::computed_value_flags::ComputedValueFlags;
/// The initial values for all style structs as defined by the specification.
lazy_static! {
/// The initial values for all style structs as defined by the specification.
pub static ref INITIAL_SERVO_VALUES: ComputedValues = ComputedValues {
inner: ComputedValuesInner {
% for style_struct in data.active_style_structs():

View file

@ -142,6 +142,12 @@ impl PseudoElement {
false
}
/// Whether this pseudo-element is the ::marker pseudo.
#[inline]
pub fn is_marker(&self) -> bool {
false
}
/// Whether this pseudo-element is the ::before pseudo.
#[inline]
pub fn is_before(&self) -> bool {

View file

@ -174,10 +174,14 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
/// Apply the blockification rules based on the table in CSS 2.2 section 9.7.
/// <https://drafts.csswg.org/css2/visuren.html#dis-pos-flo>
/// A ::marker pseudo-element with 'list-style-position:outside' needs to
/// have its 'display' blockified.
fn blockify_if_necessary<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
where
E: TElement,
{
use crate::computed_values::list_style_position::T as ListStylePosition;
let mut blockify = false;
macro_rules! blockify_if {
($if_what:expr) => {
@ -200,6 +204,11 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
blockify_if!(self.style.floated());
blockify_if!(self.style.out_of_flow_positioned());
blockify_if!(
self.style.pseudo.map_or(false, |p| p.is_marker()) &&
self.style.get_parent_list().clone_list_style_position() ==
ListStylePosition::Outside
);
if !blockify {
return;
@ -226,22 +235,18 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
.is_empty()
{
self.style
.flags
.insert(ComputedValueFlags::HAS_TEXT_DECORATION_LINES);
.add_flags(ComputedValueFlags::HAS_TEXT_DECORATION_LINES);
}
if self.style.is_pseudo_element() {
self.style
.flags
.insert(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE);
.add_flags(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE);
}
#[cfg(feature = "servo")]
{
if self.style.get_parent_column().is_multicol() {
self.style
.flags
.insert(ComputedValueFlags::CAN_BE_FRAGMENTED);
self.style.add_flags(ComputedValueFlags::CAN_BE_FRAGMENTED);
}
}
}
@ -280,9 +285,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
if writing_mode != WritingMode::HorizontalTb &&
text_combine_upright == TextCombineUpright::All
{
self.style
.flags
.insert(ComputedValueFlags::IS_TEXT_COMBINED);
self.style.add_flags(ComputedValueFlags::IS_TEXT_COMBINED);
self.style
.mutate_inherited_box()
.set_writing_mode(WritingMode::HorizontalTb);
@ -304,8 +307,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
.contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
{
self.style
.flags
.insert(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
.add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
}
}
@ -592,8 +594,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
// Check whether line break should be suppressed for this element.
if self.should_suppress_linebreak(layout_parent_style) {
self.style
.flags
.insert(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
.add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
// Inlinify the display type if allowed.
if !self.skip_item_display_fixup(element) {
let inline_display = self_display.inlinify();
@ -652,13 +653,11 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
if element.unwrap().is_visited_link() {
self.style
.flags
.insert(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
.add_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
} else {
// Need to remove to handle unvisited link inside visited.
self.style
.flags
.remove(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
.remove_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
}
}
@ -724,6 +723,52 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
}
}
/// For HTML elements with 'display:list-item' we add a default 'counter-increment:list-item'
/// unless 'counter-increment' already has a value for 'list-item'.
///
/// https://drafts.csswg.org/css-lists-3/#declaring-a-list-item
#[cfg(feature = "gecko")]
fn adjust_for_list_item<E>(&mut self, element: Option<E>)
where
E: TElement,
{
use crate::properties::longhands::counter_increment::computed_value::T as ComputedIncrement;
use crate::values::generics::counters::CounterPair;
use crate::values::specified::list::MozListReversed;
use crate::values::CustomIdent;
if self.style.get_box().clone_display() != Display::ListItem {
return;
}
if self.style.pseudo.is_some() {
return;
}
if !element.map_or(false, |e| e.is_html_element()) {
return;
}
// Note that we map <li value=INTEGER> to 'counter-set: list-item INTEGER;
// counter-increment: list-item 0;' so we'll return here unless the author
// explicitly specified something else.
let increments = self.style.get_counters().clone_counter_increment();
if increments.iter().any(|i| i.name.0 == atom!("list-item")) {
return;
}
let reversed = self.style.get_list().clone__moz_list_reversed() == MozListReversed::True;
let increment = if reversed { -1 } else { 1 };
let list_increment = CounterPair {
name: CustomIdent(atom!("list-item")),
value: increment,
};
let increments = increments
.iter()
.cloned()
.chain(std::iter::once(list_increment));
self.style
.mutate_counters()
.set_counter_increment(ComputedIncrement::new(increments.collect()));
}
/// Adjusts the style to account for various fixups that don't fit naturally
/// into the cascade.
///
@ -788,6 +833,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
#[cfg(feature = "gecko")]
{
self.adjust_for_appearance(element);
self.adjust_for_list_item(element);
}
self.set_bits();
}

View file

@ -189,6 +189,13 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule))
}
// FIXME(emilio): We should always be able to have a loader
// around! See bug 1533783.
if self.loader.is_none() {
error!("Saw @import rule, but no way to trigger the load");
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule))
}
let url_string = input.expect_url_or_string()?.as_ref().to_owned();
let url = CssUrl::parse_from_string(url_string, &self.context);

View file

@ -27,7 +27,6 @@ use cssparser::CowRcStr;
use cssparser::{parse_important, AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
use euclid::TypedSize2D;
use selectors::parser::SelectorParseErrorKind;
use servo_config::pref;
use std::borrow::Cow;
use std::cell::RefCell;
use std::fmt::{self, Write};
@ -39,6 +38,7 @@ use style_traits::{CssWriter, ParseError, PinchZoomFactor, StyleParseErrorKind,
/// Whether parsing and processing of `@viewport` rules is enabled.
#[cfg(feature = "servo")]
pub fn enabled() -> bool {
use servo_config::pref;
pref!(layout.viewport.enabled)
}

View file

@ -7,13 +7,13 @@
use crate::values::computed::url::ComputedImageUrl;
use crate::values::generics::counters as generics;
use crate::values::generics::counters::CounterIncrement as GenericCounterIncrement;
use crate::values::generics::counters::CounterReset as GenericCounterReset;
use crate::values::generics::counters::CounterSetOrReset as GenericCounterSetOrReset;
/// A computed value for the `counter-increment` property.
pub type CounterIncrement = GenericCounterIncrement<i32>;
/// A computed value for the `counter-increment` property.
pub type CounterReset = GenericCounterReset<i32>;
/// A computed value for the `counter-set` and `counter-reset` properties.
pub type CounterSetOrReset = GenericCounterSetOrReset<i32>;
/// A computed value for the `content` property.
pub type Content = generics::Content<ComputedImageUrl>;

View file

@ -152,49 +152,6 @@ impl FontSize {
keyword_info: Some(KeywordInfo::medium()),
}
}
/// FIXME(emilio): This is very complex. Also, it should move to
/// StyleBuilder.
pub fn cascade_inherit_font_size(context: &mut Context) {
// If inheriting, we must recompute font-size in case of language
// changes using the font_size_keyword. We also need to do this to
// handle mathml scriptlevel changes
let kw_inherited_size = context
.builder
.get_parent_font()
.clone_font_size()
.keyword_info
.map(|info| {
specified::FontSize::Keyword(info)
.to_computed_value(context)
.size
});
let mut font = context.builder.take_font();
font.inherit_font_size_from(
context.builder.get_parent_font(),
kw_inherited_size,
context.builder.device,
);
context.builder.put_font(font);
}
/// Cascade the initial value for the `font-size` property.
///
/// FIXME(emilio): This is the only function that is outside of the
/// `StyleBuilder`, and should really move inside!
///
/// Can we move the font stuff there?
pub fn cascade_initial_font_size(context: &mut Context) {
// font-size's default ("medium") does not always
// compute to the same value and depends on the font
let computed = specified::FontSize::medium().to_computed_value(context);
context.builder.mutate_font().set_font_size(computed);
#[cfg(feature = "gecko")]
{
let device = context.builder.device;
context.builder.mutate_font().fixup_font_min_size(device);
}
}
}
/// XXXManishearth it might be better to
@ -221,15 +178,21 @@ impl ToAnimatedValue for FontSize {
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
/// Specifies a prioritized list of font family names or generic family names.
pub struct FontFamily(pub FontFamilyList);
pub struct FontFamily {
/// The actual list of family names.
pub families: FontFamilyList,
/// Whether this font-family came from a specified system-font.
pub is_system_font: bool,
}
impl FontFamily {
#[inline]
/// Get default font family as `serif` which is a generic font-family
pub fn serif() -> Self {
FontFamily(FontFamilyList::new(Box::new([SingleFontFamily::Generic(
atom!("serif"),
)])))
FontFamily {
families: FontFamilyList::new(Box::new([SingleFontFamily::Generic(atom!("serif"))])),
is_system_font: false,
}
}
}
@ -239,7 +202,9 @@ impl MallocSizeOf for FontFamily {
// SharedFontList objects are generally shared from the pointer
// stored in the specified value. So only count this if the
// SharedFontList is unshared.
unsafe { bindings::Gecko_SharedFontList_SizeOfIncludingThisIfUnshared((self.0).0.get()) }
unsafe {
bindings::Gecko_SharedFontList_SizeOfIncludingThisIfUnshared(self.families.0.get())
}
}
}
@ -248,7 +213,7 @@ impl ToCss for FontFamily {
where
W: fmt::Write,
{
let mut iter = self.0.iter();
let mut iter = self.families.iter();
iter.next().unwrap().to_css(dest)?;
for family in iter {
dest.write_str(", ")?;

View file

@ -6,6 +6,7 @@
#[cfg(feature = "gecko")]
pub use crate::values::specified::list::ListStyleType;
pub use crate::values::specified::list::MozListReversed;
pub use crate::values::specified::list::{QuotePair, Quotes};
use servo_arc::Arc;

View file

@ -45,7 +45,7 @@ pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize};
pub use self::box_::{ScrollSnapAlign, ScrollSnapType, TouchAction, VerticalAlign, WillChange};
pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, RGBAColor};
pub use self::column::ColumnCount;
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset};
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterSetOrReset};
pub use self::easing::TimingFunction;
pub use self::effects::{BoxShadow, Filter, SimpleShadow};
pub use self::flex::FlexBasis;
@ -64,6 +64,7 @@ pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, MaxSize, Size};
pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto};
#[cfg(feature = "gecko")]
pub use self::list::ListStyleType;
pub use self::list::MozListReversed;
pub use self::list::{QuotePair, Quotes};
pub use self::motion::OffsetPath;
pub use self::outline::OutlineStyle;

View file

@ -19,8 +19,8 @@ use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
pub use crate::values::specified::TextAlignKeyword as TextAlign;
pub use crate::values::specified::TextEmphasisPosition;
pub use crate::values::specified::{OverflowWrap, WordBreak};
pub use crate::values::specified::{TextDecorationLine, TextEmphasisPosition};
/// A computed value for the `initial-letter` property.
pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;
@ -182,11 +182,11 @@ impl TextDecorationsInEffect {
.clone(),
};
let text_style = style.get_text();
let line = style.get_text().clone_text_decoration_line();
result.underline |= text_style.has_underline();
result.overline |= text_style.has_overline();
result.line_through |= text_style.has_line_through();
result.underline |= line.contains(TextDecorationLine::UNDERLINE);
result.overline |= line.contains(TextDecorationLine::OVERLINE);
result.line_through |= line.contains(TextDecorationLine::LINE_THROUGH);
result
}

View file

@ -45,21 +45,21 @@ impl<I> Deref for CounterIncrement<I> {
}
}
/// A generic value for the `counter-reset` property.
/// A generic value for the `counter-set` and `counter-reset` properties.
#[derive(
Clone, Debug, Default, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss,
)]
pub struct CounterReset<I>(Counters<I>);
pub struct CounterSetOrReset<I>(Counters<I>);
impl<I> CounterReset<I> {
/// Returns a new value for `counter-reset`.
impl<I> CounterSetOrReset<I> {
/// Returns a new value for `counter-set` / `counter-reset`.
#[inline]
pub fn new(counters: Vec<CounterPair<I>>) -> Self {
CounterReset(Counters(counters.into_boxed_slice()))
CounterSetOrReset(Counters(counters.into_boxed_slice()))
}
}
impl<I> Deref for CounterReset<I> {
impl<I> Deref for CounterSetOrReset<I> {
type Target = [CounterPair<I>];
#[inline]

View file

@ -555,9 +555,8 @@ impl WillChange {
bitflags! {
/// The change bits that we care about.
///
/// These need to be in sync with NS_STYLE_WILL_CHANGE_*.
#[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)]
#[repr(C)]
pub struct WillChangeBits: u8 {
/// Whether the stacking context will change.
const STACKING_CONTEXT = 1 << 0;
@ -616,7 +615,7 @@ impl Parse for WillChange {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<WillChange, ParseError<'i>> {
) -> Result<Self, ParseError<'i>> {
if input
.try(|input| input.expect_ident_matching("auto"))
.is_ok()
@ -650,21 +649,22 @@ impl Parse for WillChange {
}
bitflags! {
/// Values for the `touch-action` property.
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
#[derive(SpecifiedValueInfo, ToComputedValue)]
/// These constants match Gecko's `NS_STYLE_TOUCH_ACTION_*` constants.
#[value_info(other_values = "auto,none,manipulation,pan-x,pan-y")]
#[repr(C)]
pub struct TouchAction: u8 {
/// `none` variant
const TOUCH_ACTION_NONE = 1 << 0;
const NONE = 1 << 0;
/// `auto` variant
const TOUCH_ACTION_AUTO = 1 << 1;
const AUTO = 1 << 1;
/// `pan-x` variant
const TOUCH_ACTION_PAN_X = 1 << 2;
const PAN_X = 1 << 2;
/// `pan-y` variant
const TOUCH_ACTION_PAN_Y = 1 << 3;
const PAN_Y = 1 << 3;
/// `manipulation` variant
const TOUCH_ACTION_MANIPULATION = 1 << 4;
const MANIPULATION = 1 << 4;
}
}
@ -672,7 +672,7 @@ impl TouchAction {
#[inline]
/// Get default `touch-action` as `auto`
pub fn auto() -> TouchAction {
TouchAction::TOUCH_ACTION_AUTO
TouchAction::AUTO
}
}
@ -682,16 +682,14 @@ impl ToCss for TouchAction {
W: Write,
{
match *self {
TouchAction::TOUCH_ACTION_NONE => dest.write_str("none"),
TouchAction::TOUCH_ACTION_AUTO => dest.write_str("auto"),
TouchAction::TOUCH_ACTION_MANIPULATION => dest.write_str("manipulation"),
_ if self
.contains(TouchAction::TOUCH_ACTION_PAN_X | TouchAction::TOUCH_ACTION_PAN_Y) =>
{
TouchAction::NONE => dest.write_str("none"),
TouchAction::AUTO => dest.write_str("auto"),
TouchAction::MANIPULATION => dest.write_str("manipulation"),
_ if self.contains(TouchAction::PAN_X | TouchAction::PAN_Y) => {
dest.write_str("pan-x pan-y")
},
_ if self.contains(TouchAction::TOUCH_ACTION_PAN_X) => dest.write_str("pan-x"),
_ if self.contains(TouchAction::TOUCH_ACTION_PAN_Y) => dest.write_str("pan-y"),
_ if self.contains(TouchAction::PAN_X) => dest.write_str("pan-x"),
_ if self.contains(TouchAction::PAN_Y) => dest.write_str("pan-y"),
_ => panic!("invalid touch-action value"),
}
}
@ -703,68 +701,45 @@ impl Parse for TouchAction {
input: &mut Parser<'i, 't>,
) -> Result<TouchAction, ParseError<'i>> {
try_match_ident_ignore_ascii_case! { input,
"auto" => Ok(TouchAction::TOUCH_ACTION_AUTO),
"none" => Ok(TouchAction::TOUCH_ACTION_NONE),
"manipulation" => Ok(TouchAction::TOUCH_ACTION_MANIPULATION),
"auto" => Ok(TouchAction::AUTO),
"none" => Ok(TouchAction::NONE),
"manipulation" => Ok(TouchAction::MANIPULATION),
"pan-x" => {
if input.try(|i| i.expect_ident_matching("pan-y")).is_ok() {
Ok(TouchAction::TOUCH_ACTION_PAN_X | TouchAction::TOUCH_ACTION_PAN_Y)
Ok(TouchAction::PAN_X | TouchAction::PAN_Y)
} else {
Ok(TouchAction::TOUCH_ACTION_PAN_X)
Ok(TouchAction::PAN_X)
}
},
"pan-y" => {
if input.try(|i| i.expect_ident_matching("pan-x")).is_ok() {
Ok(TouchAction::TOUCH_ACTION_PAN_X | TouchAction::TOUCH_ACTION_PAN_Y)
Ok(TouchAction::PAN_X | TouchAction::PAN_Y)
} else {
Ok(TouchAction::TOUCH_ACTION_PAN_Y)
Ok(TouchAction::PAN_Y)
}
},
}
}
}
#[cfg(feature = "gecko")]
impl_bitflags_conversions!(TouchAction);
/// Asserts that all touch-action matches its NS_STYLE_TOUCH_ACTION_* value.
#[cfg(feature = "gecko")]
#[inline]
pub fn assert_touch_action_matches() {
use crate::gecko_bindings::structs;
macro_rules! check_touch_action {
( $( $a:ident => $b:path),*, ) => {
$(
debug_assert_eq!(structs::$a as u8, $b.bits());
)*
}
}
check_touch_action! {
NS_STYLE_TOUCH_ACTION_NONE => TouchAction::TOUCH_ACTION_NONE,
NS_STYLE_TOUCH_ACTION_AUTO => TouchAction::TOUCH_ACTION_AUTO,
NS_STYLE_TOUCH_ACTION_PAN_X => TouchAction::TOUCH_ACTION_PAN_X,
NS_STYLE_TOUCH_ACTION_PAN_Y => TouchAction::TOUCH_ACTION_PAN_Y,
NS_STYLE_TOUCH_ACTION_MANIPULATION => TouchAction::TOUCH_ACTION_MANIPULATION,
}
}
bitflags! {
#[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)]
#[value_info(other_values = "none,strict,content,size,layout,paint")]
#[repr(C)]
/// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property
pub struct Contain: u8 {
/// `none` variant, just for convenience.
const NONE = 0;
/// 'size' variant, turns on size containment
const SIZE = 0x01;
const SIZE = 1 << 0;
/// `layout` variant, turns on layout containment
const LAYOUT = 0x02;
const LAYOUT = 1 << 1;
/// `paint` variant, turns on paint containment
const PAINT = 0x04;
const PAINT = 1 << 2;
/// `strict` variant, turns on all types of containment
const STRICT = 0x08;
const STRICT = 1 << 3;
/// 'content' variant, turns on layout and paint containment
const CONTENT = 0x10;
const CONTENT = 1 << 4;
/// variant with all the bits that contain: strict turns on
const STRICT_BITS = Contain::LAYOUT.bits | Contain::PAINT.bits | Contain::SIZE.bits;
/// variant with all the bits that contain: content turns on

View file

@ -10,7 +10,7 @@ use crate::parser::{Parse, ParserContext};
use crate::values::generics::counters as generics;
use crate::values::generics::counters::CounterIncrement as GenericCounterIncrement;
use crate::values::generics::counters::CounterPair;
use crate::values::generics::counters::CounterReset as GenericCounterReset;
use crate::values::generics::counters::CounterSetOrReset as GenericCounterSetOrReset;
#[cfg(feature = "gecko")]
use crate::values::generics::CounterStyleOrNone;
use crate::values::specified::url::SpecifiedImageUrl;
@ -34,10 +34,10 @@ impl Parse for CounterIncrement {
}
}
/// A specified value for the `counter-increment` property.
pub type CounterReset = GenericCounterReset<Integer>;
/// A specified value for the `counter-set` and `counter-reset` properties.
pub type CounterSetOrReset = GenericCounterSetOrReset<Integer>;
impl Parse for CounterReset {
impl Parse for CounterSetOrReset {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,

View file

@ -548,13 +548,16 @@ impl ToComputedValue for FontFamily {
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
match *self {
FontFamily::Values(ref v) => computed::FontFamily(v.clone()),
FontFamily::Values(ref v) => computed::FontFamily {
families: v.clone(),
is_system_font: false,
},
FontFamily::System(_) => self.compute_system(context),
}
}
fn from_computed_value(other: &computed::FontFamily) -> Self {
FontFamily::Values(other.0.clone())
FontFamily::Values(other.families.clone())
}
}
@ -958,48 +961,6 @@ impl FontSize {
"larger" => Ok(FontSize::Larger),
}
}
#[allow(unused_mut)]
/// Cascade `font-size` with specified value
pub fn cascade_specified_font_size(
context: &mut Context,
specified_value: &FontSize,
mut computed: computed::FontSize,
) {
// we could use clone_language and clone_font_family() here but that's
// expensive. Do it only in gecko mode for now.
#[cfg(feature = "gecko")]
{
// if the language or generic changed, we need to recalculate
// the font size from the stored font-size origin information.
if context.builder.get_font().gecko().mLanguage.mRawPtr !=
context.builder.get_parent_font().gecko().mLanguage.mRawPtr ||
context.builder.get_font().gecko().mGenericID !=
context.builder.get_parent_font().gecko().mGenericID
{
if let Some(info) = computed.keyword_info {
computed.size = info.to_computed_value(context);
}
}
}
let device = context.builder.device;
let mut font = context.builder.take_font();
let parent_unconstrained = {
let parent_font = context.builder.get_parent_font();
font.apply_font_size(computed, parent_font, device)
};
context.builder.put_font(font);
if let Some(parent) = parent_unconstrained {
let new_unconstrained = specified_value
.to_computed_value_against(context, FontBaseSize::Custom(Au::from(parent)));
context
.builder
.mutate_font()
.apply_unconstrained_font_size(new_unconstrained.size);
}
}
}
impl Parse for FontSize {

View file

@ -7,8 +7,9 @@
//! [length]: https://drafts.csswg.org/css-values/#lengths
use super::{AllowQuirks, Number, Percentage, ToComputedValue};
use crate::font_metrics::FontMetricsQueryResult;
use crate::font_metrics::{FontMetrics, FontMetricsOrientation};
use crate::parser::{Parse, ParserContext};
use crate::properties::computed_value_flags::ComputedValueFlags;
use crate::values::computed::{self, CSSPixelLength, Context};
use crate::values::generics::length as generics;
use crate::values::generics::length::{
@ -82,17 +83,12 @@ pub enum FontBaseSize {
///
/// FIXME(emilio): This is very complex, and should go away.
InheritedStyleButStripEmUnits,
/// Use a custom base size.
///
/// FIXME(emilio): This is very dubious, and only used for MathML.
Custom(Au),
}
impl FontBaseSize {
/// Calculate the actual size for a given context
pub fn resolve(&self, context: &Context) -> Au {
match *self {
FontBaseSize::Custom(size) => size,
FontBaseSize::CurrentStyle => context.style().get_font().clone_font_size().size(),
FontBaseSize::InheritedStyleButStripEmUnits | FontBaseSize::InheritedStyle => {
context.style().get_parent_font().clone_font_size().size()
@ -136,15 +132,12 @@ impl FontRelativeLength {
) -> (Au, CSSFloat) {
fn query_font_metrics(
context: &Context,
reference_font_size: Au,
) -> FontMetricsQueryResult {
context.font_metrics_provider.query(
context.style().get_font(),
reference_font_size,
context.style().writing_mode,
context.in_media_query,
context.device(),
)
base_size: FontBaseSize,
orientation: FontMetricsOrientation,
) -> FontMetrics {
context
.font_metrics_provider
.query(context, base_size, orientation)
}
let reference_font_size = base_size.resolve(context);
@ -169,24 +162,40 @@ impl FontRelativeLength {
if context.for_non_inherited_property.is_some() {
context.rule_cache_conditions.borrow_mut().set_uncacheable();
}
let reference_size = match query_font_metrics(context, reference_font_size) {
FontMetricsQueryResult::Available(metrics) => metrics.x_height,
context
.builder
.add_flags(ComputedValueFlags::DEPENDS_ON_FONT_METRICS);
// The x-height is an intrinsically horizontal metric.
let metrics =
query_font_metrics(context, base_size, FontMetricsOrientation::Horizontal);
let reference_size = metrics.x_height.unwrap_or_else(|| {
// https://drafts.csswg.org/css-values/#ex
//
// In the cases where it is impossible or impractical to
// determine the x-height, a value of 0.5em must be
// assumed.
//
FontMetricsQueryResult::NotAvailable => reference_font_size.scale_by(0.5),
};
reference_font_size.scale_by(0.5)
});
(reference_size, length)
},
FontRelativeLength::Ch(length) => {
if context.for_non_inherited_property.is_some() {
context.rule_cache_conditions.borrow_mut().set_uncacheable();
}
let reference_size = match query_font_metrics(context, reference_font_size) {
FontMetricsQueryResult::Available(metrics) => metrics.zero_advance_measure,
context
.builder
.add_flags(ComputedValueFlags::DEPENDS_ON_FONT_METRICS);
// https://drafts.csswg.org/css-values/#ch:
//
// Equal to the used advance measure of the “0” (ZERO,
// U+0030) glyph in the font used to render it. (The advance
// measure of a glyph is its advance width or height,
// whichever is in the inline axis of the element.)
//
let metrics =
query_font_metrics(context, base_size, FontMetricsOrientation::MatchContext);
let reference_size = metrics.zero_advance_measure.unwrap_or_else(|| {
// https://drafts.csswg.org/css-values/#ch
//
// In the cases where it is impossible or impractical to
@ -197,14 +206,13 @@ impl FontRelativeLength {
// writing-mode is vertical-rl or vertical-lr and
// text-orientation is upright).
//
FontMetricsQueryResult::NotAvailable => {
if context.style().writing_mode.is_vertical() {
let wm = context.style().writing_mode;
if wm.is_vertical() && wm.is_upright() {
reference_font_size
} else {
reference_font_size.scale_by(0.5)
}
},
};
});
(reference_size, length)
},
FontRelativeLength::Rem(length) => {

View file

@ -123,3 +123,25 @@ impl Parse for Quotes {
}
}
}
/// Specified and computed `-moz-list-reversed` property (for UA sheets only).
#[derive(
Clone,
Copy,
Debug,
Eq,
Hash,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
)]
#[repr(u8)]
pub enum MozListReversed {
/// the initial value
False,
/// exclusively used for <ol reversed> in our html.css UA sheet
True,
}

View file

@ -44,7 +44,7 @@ pub use self::box_::{ScrollSnapAlign, ScrollSnapType};
pub use self::box_::{TouchAction, TransitionProperty, VerticalAlign, WillChange};
pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, RGBAColor};
pub use self::column::ColumnCount;
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset};
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterSetOrReset};
pub use self::easing::TimingFunction;
pub use self::effects::{BoxShadow, Filter, SimpleShadow};
pub use self::flex::FlexBasis;
@ -66,6 +66,7 @@ pub use self::length::{NoCalcLength, ViewportPercentageLength};
pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto};
#[cfg(feature = "gecko")]
pub use self::list::ListStyleType;
pub use self::list::MozListReversed;
pub use self::list::{QuotePair, Quotes};
pub use self::motion::OffsetPath;
pub use self::outline::OutlineStyle;

View file

@ -22,8 +22,7 @@ use cssparser::{Parser, Token};
use selectors::parser::SelectorParseErrorKind;
use std::fmt::{self, Write};
use style_traits::values::SequenceWriter;
use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
use unicode_segmentation::UnicodeSegmentation;
/// A specified type for the `initial-letter` property.
@ -255,24 +254,22 @@ impl ToComputedValue for TextOverflow {
}
}
macro_rules! impl_text_decoration_line {
{
$(
$(#[$($meta:tt)+])*
$ident:ident / $css:expr => $value:expr,
)+
} => {
bitflags! {
#[derive(MallocSizeOf, ToComputedValue)]
#[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)]
#[value_info(other_values = "none,underline,overline,line-through,blink")]
#[repr(C)]
/// Specified keyword values for the text-decoration-line property.
pub struct TextDecorationLine: u8 {
/// No text decoration line is specified
/// No text decoration line is specified.
const NONE = 0;
$(
$(#[$($meta)+])*
const $ident = $value;
)+
#[cfg(feature = "gecko")]
/// underline
const UNDERLINE = 1 << 0;
/// overline
const OVERLINE = 1 << 1;
/// line-through
const LINE_THROUGH = 1 << 2;
/// blink
const BLINK = 1 << 3;
/// Only set by presentation attributes
///
/// Setting this will mean that text-decorations use the color
@ -280,6 +277,7 @@ macro_rules! impl_text_decoration_line {
///
/// For example, this gives <a href=foo><font color="red">text</font></a>
/// a red text decoration
#[cfg(feature = "gecko")]
const COLOR_OVERRIDE = 0x10;
}
}
@ -289,35 +287,40 @@ macro_rules! impl_text_decoration_line {
fn parse<'i, 't>(
_context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<TextDecorationLine, ParseError<'i>> {
let mut result = TextDecorationLine::NONE;
if input
.try(|input| input.expect_ident_matching("none"))
.is_ok()
{
return Ok(result);
) -> Result<Self, ParseError<'i>> {
let mut result = TextDecorationLine::empty();
// NOTE(emilio): this loop has this weird structure because we run this
// code to parse the text-decoration shorthand as well, so we need to
// ensure we don't return an error if we don't consume the whole thing
// because we find an invalid identifier or other kind of token.
loop {
let flag: Result<_, ParseError<'i>> = input.try(|input| {
let flag = try_match_ident_ignore_ascii_case! { input,
"none" if result.is_empty() => TextDecorationLine::NONE,
"underline" => TextDecorationLine::UNDERLINE,
"overline" => TextDecorationLine::OVERLINE,
"line-through" => TextDecorationLine::LINE_THROUGH,
"blink" => TextDecorationLine::BLINK,
};
Ok(flag)
});
let flag = match flag {
Ok(flag) => flag,
Err(..) => break,
};
if flag.is_empty() {
return Ok(TextDecorationLine::NONE);
}
loop {
let result = input.try(|input| {
let ident = input.expect_ident().map_err(|_| ())?;
match_ignore_ascii_case! { ident,
$(
$css => {
if result.contains(TextDecorationLine::$ident) {
Err(())
} else {
result.insert(TextDecorationLine::$ident);
Ok(())
}
}
)+
_ => Err(()),
}
});
if result.is_err() {
break;
if result.contains(flag) {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
result.insert(flag)
}
if !result.is_empty() {
@ -337,38 +340,36 @@ macro_rules! impl_text_decoration_line {
return dest.write_str("none");
}
let mut writer = SequenceWriter::new(dest, " ");
$(
if self.contains(TextDecorationLine::$ident) {
writer.raw_item($css)?;
#[cfg(feature = "gecko")]
{
if *self == TextDecorationLine::COLOR_OVERRIDE {
return Ok(());
}
)+
}
let mut writer = SequenceWriter::new(dest, " ");
let mut any = false;
macro_rules! maybe_write {
($ident:ident => $str:expr) => {
if self.contains(TextDecorationLine::$ident) {
any = true;
writer.raw_item($str)?;
}
};
}
maybe_write!(UNDERLINE => "underline");
maybe_write!(OVERLINE => "overline");
maybe_write!(LINE_THROUGH => "line-through");
maybe_write!(BLINK => "blink");
debug_assert!(any);
Ok(())
}
}
impl SpecifiedValueInfo for TextDecorationLine {
fn collect_completion_keywords(f: KeywordsCollectFn) {
f(&["none", $($css,)+]);
}
}
}
}
impl_text_decoration_line! {
/// Underline
UNDERLINE / "underline" => 1 << 0,
/// Overline
OVERLINE / "overline" => 1 << 1,
/// Line through
LINE_THROUGH / "line-through" => 1 << 2,
/// Blink
BLINK / "blink" => 1 << 3,
}
#[cfg(feature = "gecko")]
impl_bitflags_conversions!(TextDecorationLine);
impl TextDecorationLine {
#[inline]
/// Returns the initial value of text-decoration-line

View file

@ -10,7 +10,7 @@ use std::sync::Arc as StdArc;
/// Type of value that a property supports. This is used by Gecko's
/// devtools to make sense about value it parses, and types listed
/// here should match TYPE_* constants in InspectorUtils.webidl.
/// here should match InspectorPropertyType in InspectorUtils.webidl.
///
/// XXX This should really be a bitflags rather than a namespace mod,
/// but currently we cannot use bitflags in const.