mirror of
https://github.com/servo/servo.git
synced 2025-08-10 16:05:43 +01:00
Auto merge of #21237 - emilio:gecko-sync, r=emilio
style: Sync changes from mozilla-central. See individual commits for details. https://bugzilla.mozilla.org/show_bug.cgi?id=1477883 <!-- 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/21237) <!-- Reviewable:end -->
This commit is contained in:
commit
ba6123583d
33 changed files with 873 additions and 497 deletions
|
@ -17,7 +17,7 @@ use dom_struct::dom_struct;
|
|||
use servo_arc::Arc;
|
||||
use servo_url::ServoUrl;
|
||||
use style::attr::AttrValue;
|
||||
use style::properties::{DeclarationPushMode, Importance, PropertyDeclarationBlock, PropertyId, LonghandId, ShorthandId};
|
||||
use style::properties::{Importance, PropertyDeclarationBlock, PropertyId, LonghandId, ShorthandId};
|
||||
use style::properties::{parse_one_declaration_into, parse_style_attribute, SourcePropertyDeclaration};
|
||||
use style::selector_parser::PseudoElement;
|
||||
use style::shared_lock::Locked;
|
||||
|
@ -285,8 +285,14 @@ impl CSSStyleDeclaration {
|
|||
let quirks_mode = window.Document().quirks_mode();
|
||||
let mut declarations = SourcePropertyDeclaration::new();
|
||||
let result = parse_one_declaration_into(
|
||||
&mut declarations, id, &value, &self.owner.base_url(),
|
||||
window.css_error_reporter(), ParsingMode::DEFAULT, quirks_mode);
|
||||
&mut declarations,
|
||||
id,
|
||||
&value,
|
||||
&self.owner.base_url(),
|
||||
window.css_error_reporter(),
|
||||
ParsingMode::DEFAULT,
|
||||
quirks_mode,
|
||||
);
|
||||
|
||||
// Step 6
|
||||
match result {
|
||||
|
@ -297,13 +303,17 @@ impl CSSStyleDeclaration {
|
|||
}
|
||||
}
|
||||
|
||||
let mut updates = Default::default();
|
||||
*changed =
|
||||
pdb.prepare_for_update(&declarations, importance, &mut updates);
|
||||
|
||||
if !*changed {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Step 7
|
||||
// Step 8
|
||||
*changed = pdb.extend(
|
||||
declarations.drain(),
|
||||
importance,
|
||||
DeclarationPushMode::Update,
|
||||
);
|
||||
pdb.update(declarations.drain(), importance, &mut updates);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
|
|
@ -418,10 +418,6 @@ where
|
|||
match combinator {
|
||||
Combinator::NextSibling | Combinator::LaterSibling => element.prev_sibling_element(),
|
||||
Combinator::Child | Combinator::Descendant => {
|
||||
if element.blocks_ancestor_combinators() {
|
||||
return None;
|
||||
}
|
||||
|
||||
match element.parent_element() {
|
||||
Some(e) => return Some(e),
|
||||
None => {},
|
||||
|
|
|
@ -129,11 +129,4 @@ pub trait Element: Sized + Clone + Debug {
|
|||
fn ignores_nth_child_selectors(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Return true if we want to stop lookup ancestor of the current
|
||||
/// element while matching complex selectors with descendant/child
|
||||
/// combinator.
|
||||
fn blocks_ancestor_combinators(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -349,15 +349,10 @@ impl<T: ?Sized> Arc<T> {
|
|||
|
||||
#[inline]
|
||||
pub fn is_unique(&self) -> bool {
|
||||
// We can use Relaxed here, but the justification is a bit subtle.
|
||||
// See the extensive discussion in [1] for why this needs to be Acquire.
|
||||
//
|
||||
// The reason to use Acquire would be to synchronize with other threads
|
||||
// that are modifying the refcount with Release, i.e. to ensure that
|
||||
// their writes to memory guarded by this refcount are flushed. However,
|
||||
// we know that threads only modify the contents of the Arc when they
|
||||
// observe the refcount to be 1, and no other thread could observe that
|
||||
// because we're holding one strong reference here.
|
||||
self.inner().count.load(Relaxed) == 1
|
||||
// [1] https://github.com/servo/servo/issues/21186
|
||||
self.inner().count.load(Acquire) == 1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use bezier::Bezier;
|
|||
use context::SharedStyleContext;
|
||||
use dom::{OpaqueNode, TElement};
|
||||
use font_metrics::FontMetricsProvider;
|
||||
use properties::{self, CascadeFlags, ComputedValues, LonghandId};
|
||||
use properties::{self, CascadeMode, ComputedValues, LonghandId};
|
||||
use properties::animated_properties::AnimatedProperty;
|
||||
use properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
|
||||
use properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
|
||||
|
@ -504,9 +504,8 @@ where
|
|||
Some(previous_style),
|
||||
Some(previous_style),
|
||||
Some(previous_style),
|
||||
/* visited_style = */ None,
|
||||
font_metrics_provider,
|
||||
CascadeFlags::empty(),
|
||||
CascadeMode::Unvisited { visited_rules: None },
|
||||
context.quirks_mode(),
|
||||
/* rule_cache = */ None,
|
||||
&mut Default::default(),
|
||||
|
|
|
@ -28,5 +28,6 @@ predefined! {
|
|||
f.write(' "%s",\n' % name)
|
||||
f.write('}\n')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(os.path.join(os.path.dirname(__file__), "predefined.rs"))
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
use cssparser::ToCss;
|
||||
use gecko_bindings::structs::{self, CSSPseudoElementType};
|
||||
use properties::{CascadeFlags, ComputedValues, PropertyFlags};
|
||||
use properties::{ComputedValues, PropertyFlags};
|
||||
use properties::longhands::display::computed_value::T as Display;
|
||||
use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl};
|
||||
use std::fmt;
|
||||
|
@ -55,14 +55,12 @@ impl PseudoElement {
|
|||
PseudoElementCascadeType::Lazy
|
||||
}
|
||||
|
||||
/// The CascadeFlags needed to cascade this pseudo-element.
|
||||
/// Whether cascading this pseudo-element makes it inherit all properties,
|
||||
/// even reset ones.
|
||||
///
|
||||
/// This is only needed to support the broken INHERIT_ALL pseudo mode for
|
||||
/// Servo.
|
||||
/// This is used in Servo for anonymous boxes, though it's likely broken.
|
||||
#[inline]
|
||||
pub fn cascade_flags(&self) -> CascadeFlags {
|
||||
CascadeFlags::empty()
|
||||
}
|
||||
pub fn inherits_all(&self) -> bool { false }
|
||||
|
||||
/// Whether the pseudo-element should inherit from the default computed
|
||||
/// values instead of from the parent element.
|
||||
|
|
|
@ -21,7 +21,7 @@ PRELUDE = """
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Autogenerated file created by components/style/gecko/binding_tools/regen_atoms.py, DO NOT EDIT DIRECTLY */
|
||||
"""[1:]
|
||||
"""[1:] # NOQA: E501
|
||||
|
||||
|
||||
def gnu_symbolify(source, ident):
|
||||
|
@ -57,7 +57,7 @@ class CSSPseudoElementsAtomSource:
|
|||
|
||||
|
||||
class CSSAnonBoxesAtomSource:
|
||||
PATTERN = re.compile('^(CSS_ANON_BOX|CSS_NON_INHERITING_ANON_BOX|CSS_WRAPPER_ANON_BOX)\(([^,]*),[^"]*"([^"]*)"\)',
|
||||
PATTERN = re.compile('^(CSS_ANON_BOX|CSS_NON_INHERITING_ANON_BOX|CSS_WRAPPER_ANON_BOX)\(([^,]*),[^"]*"([^"]*)"\)', # NOQA: E501
|
||||
re.MULTILINE)
|
||||
FILE = "include/nsCSSAnonBoxList.h"
|
||||
CLASS = "nsCSSAnonBoxes"
|
||||
|
|
|
@ -856,13 +856,12 @@ impl<'le> GeckoElement<'le> {
|
|||
/// Returns true if this node is the shadow root of an use-element shadow tree.
|
||||
#[inline]
|
||||
fn is_root_of_use_element_shadow_tree(&self) -> bool {
|
||||
if !self.is_root_of_anonymous_subtree() {
|
||||
if !self.as_node().is_in_shadow_tree() {
|
||||
return false;
|
||||
}
|
||||
match self.parent_element() {
|
||||
match self.containing_shadow_host() {
|
||||
Some(e) => {
|
||||
e.local_name() == &*local_name!("use") &&
|
||||
e.is_svg_element()
|
||||
e.is_svg_element() && e.local_name() == &*local_name!("use")
|
||||
},
|
||||
None => false,
|
||||
}
|
||||
|
@ -882,6 +881,7 @@ impl<'le> GeckoElement<'le> {
|
|||
.expect("AnimationValue not found in ElementTransitions");
|
||||
|
||||
let property = end_value.id();
|
||||
debug_assert!(!property.is_logical());
|
||||
map.insert(property, end_value.clone_arc());
|
||||
}
|
||||
map
|
||||
|
@ -896,6 +896,7 @@ impl<'le> GeckoElement<'le> {
|
|||
existing_transitions: &FnvHashMap<LonghandId, Arc<AnimationValue>>,
|
||||
) -> bool {
|
||||
use values::animated::{Animate, Procedure};
|
||||
debug_assert!(!longhand_id.is_logical());
|
||||
|
||||
// If there is an existing transition, update only if the end value
|
||||
// differs.
|
||||
|
@ -1587,25 +1588,24 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
|
||||
fn might_need_transitions_update(
|
||||
&self,
|
||||
old_values: Option<&ComputedValues>,
|
||||
new_values: &ComputedValues,
|
||||
old_style: Option<&ComputedValues>,
|
||||
new_style: &ComputedValues,
|
||||
) -> bool {
|
||||
use properties::longhands::display::computed_value::T as Display;
|
||||
|
||||
let old_values = match old_values {
|
||||
let old_style = match old_style {
|
||||
Some(v) => v,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let new_box_style = new_values.get_box();
|
||||
let transition_not_running = !self.has_css_transitions() &&
|
||||
new_box_style.transition_property_count() == 1 &&
|
||||
new_box_style.transition_combined_duration_at(0) <= 0.0f32;
|
||||
let new_display_style = new_box_style.clone_display();
|
||||
let old_display_style = old_values.get_box().clone_display();
|
||||
let new_box_style = new_style.get_box();
|
||||
if !self.has_css_transitions() && !new_box_style.specifies_transitions() {
|
||||
return false;
|
||||
}
|
||||
|
||||
new_box_style.transition_property_count() > 0 && !transition_not_running &&
|
||||
(new_display_style != Display::None && old_display_style != Display::None)
|
||||
if new_box_style.clone_display().is_none() || old_style.clone_display().is_none() {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Detect if there are any changes that require us to update transitions.
|
||||
|
@ -1657,6 +1657,8 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
let transition_property: TransitionProperty = property.into();
|
||||
|
||||
let mut property_check_helper = |property: LonghandId| -> bool {
|
||||
let property =
|
||||
property.to_physical(after_change_style.writing_mode);
|
||||
transitions_to_keep.insert(property);
|
||||
self.needs_transitions_update_per_property(
|
||||
property,
|
||||
|
@ -2329,14 +2331,6 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
fn ignores_nth_child_selectors(&self) -> bool {
|
||||
self.is_root_of_anonymous_subtree()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn blocks_ancestor_combinators(&self) -> bool {
|
||||
// If this element is the shadow root of an use-element shadow tree,
|
||||
// according to the spec, we should not match rules cross the shadow
|
||||
// DOM boundary.
|
||||
self.is_root_of_use_element_shadow_tree()
|
||||
}
|
||||
}
|
||||
|
||||
/// A few helpers to help with attribute selectors and snapshotting.
|
||||
|
|
|
@ -367,8 +367,4 @@ where
|
|||
.assigned_slot()
|
||||
.map(|e| ElementWrapper::new(e, self.snapshot_map))
|
||||
}
|
||||
|
||||
fn blocks_ancestor_combinators(&self) -> bool {
|
||||
self.element.blocks_ancestor_combinators()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -233,18 +233,23 @@ trait PrivateMatchMethods: TElement {
|
|||
fn needs_animations_update(
|
||||
&self,
|
||||
context: &mut StyleContext<Self>,
|
||||
old_values: Option<&ComputedValues>,
|
||||
new_values: &ComputedValues,
|
||||
old_style: Option<&ComputedValues>,
|
||||
new_style: &ComputedValues,
|
||||
) -> bool {
|
||||
let new_box_style = new_values.get_box();
|
||||
let has_new_animation_style = new_box_style.specifies_animations();
|
||||
let new_box_style = new_style.get_box();
|
||||
let new_style_specifies_animations = new_box_style.specifies_animations();
|
||||
|
||||
let old = match old_values {
|
||||
let old_style = match old_style {
|
||||
Some(old) => old,
|
||||
None => return has_new_animation_style,
|
||||
None => return new_style_specifies_animations,
|
||||
};
|
||||
|
||||
let old_box_style = old.get_box();
|
||||
let has_animations = self.has_css_animations();
|
||||
if !new_style_specifies_animations && !has_animations {
|
||||
return false;
|
||||
}
|
||||
|
||||
let old_box_style = old_style.get_box();
|
||||
|
||||
let keyframes_could_have_changed = context
|
||||
.shared
|
||||
|
@ -258,7 +263,7 @@ trait PrivateMatchMethods: TElement {
|
|||
//
|
||||
// TODO: We should check which @keyframes were added/changed/deleted and
|
||||
// update only animations corresponding to those @keyframes.
|
||||
if keyframes_could_have_changed && (has_new_animation_style || self.has_css_animations()) {
|
||||
if keyframes_could_have_changed {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -272,19 +277,27 @@ trait PrivateMatchMethods: TElement {
|
|||
|
||||
// If we were display: none, we may need to trigger animations.
|
||||
if old_display == Display::None && new_display != Display::None {
|
||||
return has_new_animation_style;
|
||||
return new_style_specifies_animations;
|
||||
}
|
||||
|
||||
// If we are becoming display: none, we may need to stop animations.
|
||||
if old_display != Display::None && new_display == Display::None {
|
||||
return self.has_css_animations();
|
||||
return has_animations;
|
||||
}
|
||||
|
||||
// We might need to update animations if writing-mode or direction
|
||||
// changed, and any of the animations contained logical properties.
|
||||
//
|
||||
// We may want to be more granular, but it's probably not worth it.
|
||||
if new_style.writing_mode != old_style.writing_mode {
|
||||
return has_animations;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Create a SequentialTask for resolving descendants in a SMIL display property
|
||||
/// animation if the display property changed from none.
|
||||
/// Create a SequentialTask for resolving descendants in a SMIL display
|
||||
/// property animation if the display property changed from none.
|
||||
#[cfg(feature = "gecko")]
|
||||
fn handle_display_change_for_smil_if_needed(
|
||||
&self,
|
||||
|
|
|
@ -49,7 +49,8 @@ STYLE_STRUCT_LIST = [
|
|||
|
||||
|
||||
def main():
|
||||
usage = "Usage: %s [ servo | gecko ] [ style-crate | geckolib <template> | html ]" % sys.argv[0]
|
||||
usage = ("Usage: %s [ servo | gecko ] [ style-crate | geckolib <template> | html ]" %
|
||||
sys.argv[0])
|
||||
if len(sys.argv) < 3:
|
||||
abort(usage)
|
||||
product = sys.argv[1]
|
||||
|
@ -122,7 +123,7 @@ def render(filename, **context):
|
|||
# Uncomment to debug generated Python code:
|
||||
# write("/tmp", "mako_%s.py" % os.path.basename(filename), template.code)
|
||||
return template.render(**context).encode("utf8")
|
||||
except:
|
||||
except Exception:
|
||||
# Uncomment to see a traceback in generated Python code:
|
||||
# raise
|
||||
abort(exceptions.text_error_template().render().encode("utf8"))
|
||||
|
|
|
@ -56,13 +56,10 @@ bitflags! {
|
|||
/// Whether the child explicitly inherits any reset property.
|
||||
const INHERITS_RESET_STYLE = 1 << 8;
|
||||
|
||||
/// A flag to mark a style which is a visited style.
|
||||
const IS_STYLE_IF_VISITED = 1 << 9;
|
||||
|
||||
/// Whether the style or any of the ancestors has a multicol style.
|
||||
///
|
||||
/// Only used in Servo.
|
||||
const CAN_BE_FRAGMENTED = 1 << 10;
|
||||
const CAN_BE_FRAGMENTED = 1 << 9;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,7 +67,6 @@ impl ComputedValueFlags {
|
|||
/// Flags that are unconditionally propagated to descendants.
|
||||
#[inline]
|
||||
fn inherited_flags() -> Self {
|
||||
ComputedValueFlags::IS_STYLE_IF_VISITED |
|
||||
ComputedValueFlags::IS_RELEVANT_LINK_VISITED |
|
||||
ComputedValueFlags::CAN_BE_FRAGMENTED |
|
||||
ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE |
|
||||
|
|
|
@ -75,7 +75,8 @@ class Keyword(object):
|
|||
self.name = name
|
||||
self.values = values.split()
|
||||
if gecko_constant_prefix and gecko_enum_prefix:
|
||||
raise TypeError("Only one of gecko_constant_prefix and gecko_enum_prefix can be specified")
|
||||
raise TypeError("Only one of gecko_constant_prefix and gecko_enum_prefix "
|
||||
"can be specified")
|
||||
self.gecko_constant_prefix = gecko_constant_prefix or \
|
||||
"NS_STYLE_" + self.name.upper().replace("-", "_")
|
||||
self.gecko_enum_prefix = gecko_enum_prefix
|
||||
|
@ -116,7 +117,8 @@ class Keyword(object):
|
|||
raise Exception("Bad product: " + product)
|
||||
|
||||
def gecko_constant(self, value):
|
||||
moz_stripped = value.replace("-moz-", '') if self.gecko_strip_moz_prefix else value.replace("-moz-", 'moz-')
|
||||
moz_stripped = (value.replace("-moz-", '')
|
||||
if self.gecko_strip_moz_prefix else value.replace("-moz-", 'moz-'))
|
||||
mapped = self.consts_map.get(value)
|
||||
if self.gecko_enum_prefix:
|
||||
parts = moz_stripped.replace('-', '_').split('_')
|
||||
|
@ -166,8 +168,9 @@ class Longhand(object):
|
|||
enabled_in="content", need_index=False,
|
||||
gecko_ffi_name=None,
|
||||
allowed_in_keyframe_block=True, cast_type='u8',
|
||||
logical=False, alias=None, extra_prefixes=None, boxed=False,
|
||||
flags=None, allowed_in_page_rule=False, allow_quirks=False, ignored_when_colors_disabled=False,
|
||||
logical=False, logical_group=None, alias=None, extra_prefixes=None, boxed=False,
|
||||
flags=None, allowed_in_page_rule=False, allow_quirks=False,
|
||||
ignored_when_colors_disabled=False,
|
||||
vector=False, servo_restyle_damage="repaint"):
|
||||
self.name = name
|
||||
if not spec:
|
||||
|
@ -193,6 +196,9 @@ class Longhand(object):
|
|||
self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case
|
||||
self.cast_type = cast_type
|
||||
self.logical = arg_to_bool(logical)
|
||||
self.logical_group = logical_group
|
||||
if self.logical:
|
||||
assert logical_group, "Property " + name + " must have a logical group"
|
||||
self.alias = parse_property_aliases(alias)
|
||||
self.extra_prefixes = parse_property_aliases(extra_prefixes)
|
||||
self.boxed = arg_to_bool(boxed)
|
||||
|
@ -220,12 +226,6 @@ class Longhand(object):
|
|||
and animation_value_type != "discrete"
|
||||
self.is_animatable_with_computed_value = animation_value_type == "ComputedValue" \
|
||||
or animation_value_type == "discrete"
|
||||
if self.logical:
|
||||
# Logical properties will be animatable (i.e. the animation type is
|
||||
# discrete). For now, it is still non-animatable.
|
||||
self.animatable = False
|
||||
self.transitionable = False
|
||||
self.animation_value_type = None
|
||||
|
||||
# See compute_damage for the various values this can take
|
||||
self.servo_restyle_damage = servo_restyle_damage
|
||||
|
@ -234,6 +234,20 @@ class Longhand(object):
|
|||
def type():
|
||||
return "longhand"
|
||||
|
||||
# For a given logical property return all the physical
|
||||
# property names corresponding to it.
|
||||
def all_physical_mapped_properties(self):
|
||||
assert self.logical
|
||||
logical_side = None
|
||||
for s in LOGICAL_SIDES + LOGICAL_SIZES:
|
||||
if s in self.name:
|
||||
assert not logical_side
|
||||
logical_side = s
|
||||
assert logical_side
|
||||
physical = PHYSICAL_SIDES if logical_side in LOGICAL_SIDES else PHYSICAL_SIZES
|
||||
return [self.name.replace(logical_side, physical_side).replace("inset-", "")
|
||||
for physical_side in physical]
|
||||
|
||||
def experimental(self, product):
|
||||
if product == "gecko":
|
||||
return bool(self.gecko_pref)
|
||||
|
|
|
@ -11,6 +11,7 @@ use cssparser::{DeclarationListParser, parse_important, ParserInput, CowRcStr};
|
|||
use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter, ParseErrorKind};
|
||||
use custom_properties::CustomPropertiesBuilder;
|
||||
use error_reporting::{ParseErrorReporter, ContextualParseError};
|
||||
use itertools::Itertools;
|
||||
use parser::ParserContext;
|
||||
use properties::animated_properties::{AnimationValue, AnimationValueMap};
|
||||
use shared_lock::Locked;
|
||||
|
@ -39,21 +40,28 @@ impl AnimationRules {
|
|||
}
|
||||
}
|
||||
|
||||
/// Enum for how a given declaration should be pushed into a declaration block.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
|
||||
pub enum DeclarationPushMode {
|
||||
/// Mode used when declarations were obtained from CSS parsing.
|
||||
/// If there is an existing declaration of the same property with a higher
|
||||
/// importance, the new declaration will be discarded. Otherwise, it will
|
||||
/// be appended to the end of the declaration block.
|
||||
Parsing,
|
||||
/// In this mode, if there is an existing declaration of the same property,
|
||||
/// the value is updated in-place. Otherwise it's appended. This is one
|
||||
/// possible behavior of CSSOM.
|
||||
Update,
|
||||
/// In this mode, the new declaration is always pushed to the end of the
|
||||
/// declaration block. This is another possible behavior of CSSOM.
|
||||
/// An enum describes how a declaration should update
|
||||
/// the declaration block.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum DeclarationUpdate {
|
||||
/// The given declaration doesn't update anything.
|
||||
None,
|
||||
/// The given declaration is new, and should be append directly.
|
||||
Append,
|
||||
/// The given declaration can be updated in-place at the given position.
|
||||
UpdateInPlace { pos: usize },
|
||||
/// The given declaration cannot be updated in-place, and an existing
|
||||
/// one needs to be removed at the given position.
|
||||
AppendAndRemove { pos: usize },
|
||||
}
|
||||
|
||||
/// A struct describes how a declaration block should be updated by
|
||||
/// a `SourcePropertyDeclaration`.
|
||||
#[derive(Default)]
|
||||
pub struct SourcePropertyDeclarationUpdate {
|
||||
updates: SubpropertiesVec<DeclarationUpdate>,
|
||||
new_count: usize,
|
||||
any_removal: bool,
|
||||
}
|
||||
|
||||
/// A declaration [importance][importance].
|
||||
|
@ -131,12 +139,15 @@ impl<'a> DoubleEndedIterator for DeclarationImportanceIterator<'a> {
|
|||
}
|
||||
|
||||
/// Iterator over `PropertyDeclaration` for Importance::Normal.
|
||||
///
|
||||
/// TODO(emilio): This should be replaced by `impl Trait`, returning a
|
||||
/// filter()ed iterator when available instead, and all the boilerplate below
|
||||
/// should go.
|
||||
pub struct NormalDeclarationIterator<'a>(DeclarationImportanceIterator<'a>);
|
||||
|
||||
impl<'a> NormalDeclarationIterator<'a> {
|
||||
/// Constructor.
|
||||
#[inline]
|
||||
pub fn new(declarations: &'a [PropertyDeclaration], important: &'a SmallBitVec) -> Self {
|
||||
fn new(declarations: &'a [PropertyDeclaration], important: &'a SmallBitVec) -> Self {
|
||||
NormalDeclarationIterator(
|
||||
DeclarationImportanceIterator::new(declarations, important)
|
||||
)
|
||||
|
@ -146,6 +157,7 @@ impl<'a> NormalDeclarationIterator<'a> {
|
|||
impl<'a> Iterator for NormalDeclarationIterator<'a> {
|
||||
type Item = &'a PropertyDeclaration;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
let (decl, importance) = self.0.iter.next()?;
|
||||
|
@ -155,11 +167,24 @@ impl<'a> Iterator for NormalDeclarationIterator<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.0.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DoubleEndedIterator for NormalDeclarationIterator<'a> {
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
let (decl, importance) = self.0.iter.next_back()?;
|
||||
if !importance {
|
||||
return Some(decl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator for AnimationValue to be generated from PropertyDeclarationBlock.
|
||||
pub struct AnimationValueIterator<'a, 'cx, 'cx_a:'cx> {
|
||||
iter: NormalDeclarationIterator<'a>,
|
||||
|
@ -423,98 +448,47 @@ impl PropertyDeclarationBlock {
|
|||
/// and it doesn't exist in the block, and returns false otherwise.
|
||||
#[inline]
|
||||
fn is_definitely_new(&self, decl: &PropertyDeclaration) -> bool {
|
||||
match decl.id() {
|
||||
PropertyDeclarationId::Longhand(id) => !self.longhands.contains(id),
|
||||
PropertyDeclarationId::Custom(..) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether calling extend with `DeclarationPushMode::Update`
|
||||
/// will cause this declaration block to change.
|
||||
pub fn will_change_in_update_mode(
|
||||
&self,
|
||||
source_declarations: &SourcePropertyDeclaration,
|
||||
importance: Importance,
|
||||
) -> bool {
|
||||
// XXX The type of parameter seems to be necessary because otherwise
|
||||
// the compiler complains about `decl` not living long enough in the
|
||||
// all_shorthand expression. Why?
|
||||
let needs_update = |decl: &_| {
|
||||
if self.is_definitely_new(decl) {
|
||||
return true;
|
||||
}
|
||||
self.declarations.iter().enumerate()
|
||||
.find(|&(_, ref slot)| slot.id() == decl.id())
|
||||
.map_or(true, |(i, slot)| {
|
||||
let important = self.declarations_importance[i];
|
||||
*slot != *decl || important != importance.important()
|
||||
})
|
||||
};
|
||||
source_declarations.declarations.iter().any(&needs_update) ||
|
||||
source_declarations.all_shorthand.declarations().any(|decl| needs_update(&decl))
|
||||
decl.id().as_longhand().map_or(false, |id| !self.longhands.contains(id))
|
||||
}
|
||||
|
||||
/// Adds or overrides the declaration for a given property in this block.
|
||||
///
|
||||
/// See the documentation of `push` to see what impact `source` has when the
|
||||
/// property is already there.
|
||||
///
|
||||
/// When calling with `DeclarationPushMode::Update`, this should not change
|
||||
/// anything if `will_change_in_update_mode` returns false.
|
||||
pub fn extend(
|
||||
&mut self,
|
||||
mut drain: SourcePropertyDeclarationDrain,
|
||||
importance: Importance,
|
||||
mode: DeclarationPushMode,
|
||||
) -> bool {
|
||||
match mode {
|
||||
DeclarationPushMode::Parsing => {
|
||||
let all_shorthand_len = match drain.all_shorthand {
|
||||
AllShorthand::NotSet => 0,
|
||||
AllShorthand::CSSWideKeyword(_) |
|
||||
AllShorthand::WithVariables(_) => shorthands::ALL_SHORTHAND_MAX_LEN,
|
||||
};
|
||||
let push_calls_count =
|
||||
drain.declarations.len() + all_shorthand_len;
|
||||
let all_shorthand_len = match drain.all_shorthand {
|
||||
AllShorthand::NotSet => 0,
|
||||
AllShorthand::CSSWideKeyword(_) |
|
||||
AllShorthand::WithVariables(_) => shorthands::ALL_SHORTHAND_MAX_LEN,
|
||||
};
|
||||
let push_calls_count =
|
||||
drain.declarations.len() + all_shorthand_len;
|
||||
|
||||
// With deduplication the actual length increase may be less than this.
|
||||
self.declarations.reserve(push_calls_count);
|
||||
}
|
||||
_ => {
|
||||
// Don't bother reserving space, since it's usually the case
|
||||
// that CSSOM just overrides properties and we don't need to use
|
||||
// more memory. See bug 1424346 for an example where this
|
||||
// matters.
|
||||
//
|
||||
// TODO: Would it be worth to call reserve() just if it's empty?
|
||||
}
|
||||
}
|
||||
// With deduplication the actual length increase may be less than this.
|
||||
self.declarations.reserve(push_calls_count);
|
||||
|
||||
let mut changed = false;
|
||||
for decl in &mut drain.declarations {
|
||||
changed |= self.push(
|
||||
decl,
|
||||
importance,
|
||||
mode,
|
||||
);
|
||||
changed |= self.push(decl, importance);
|
||||
}
|
||||
drain.all_shorthand.declarations().fold(changed, |changed, decl| {
|
||||
changed | self.push(decl, importance, mode)
|
||||
changed | self.push(decl, importance)
|
||||
})
|
||||
}
|
||||
|
||||
/// Adds or overrides the declaration for a given property in this block.
|
||||
///
|
||||
/// Depending on the value of `mode`, this has a different behavior in the
|
||||
/// presence of another declaration with the same ID in the declaration
|
||||
/// block.
|
||||
///
|
||||
/// Returns whether the declaration has changed.
|
||||
///
|
||||
/// This is only used for parsing and internal use.
|
||||
pub fn push(
|
||||
&mut self,
|
||||
declaration: PropertyDeclaration,
|
||||
importance: Importance,
|
||||
mode: DeclarationPushMode,
|
||||
) -> bool {
|
||||
if !self.is_definitely_new(&declaration) {
|
||||
let mut index_to_remove = None;
|
||||
|
@ -523,40 +497,29 @@ impl PropertyDeclarationBlock {
|
|||
continue;
|
||||
}
|
||||
|
||||
if matches!(mode, DeclarationPushMode::Parsing) {
|
||||
let important = self.declarations_importance[i];
|
||||
let important = self.declarations_importance[i];
|
||||
|
||||
// For declarations from parsing, non-important declarations
|
||||
// shouldn't override existing important one.
|
||||
if important && !importance.important() {
|
||||
return false;
|
||||
}
|
||||
// For declarations from parsing, non-important declarations
|
||||
// shouldn't override existing important one.
|
||||
if important && !importance.important() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// As a compatibility hack, specially on Android,
|
||||
// don't allow to override a prefixed webkit display
|
||||
// value with an unprefixed version from parsing
|
||||
// code.
|
||||
//
|
||||
// TODO(emilio): Unship.
|
||||
if let PropertyDeclaration::Display(old_display) = *slot {
|
||||
use properties::longhands::display::computed_value::T as display;
|
||||
// As a compatibility hack, specially on Android,
|
||||
// don't allow to override a prefixed webkit display
|
||||
// value with an unprefixed version from parsing
|
||||
// code.
|
||||
//
|
||||
// TODO(emilio): Unship.
|
||||
if let PropertyDeclaration::Display(old_display) = *slot {
|
||||
use properties::longhands::display::computed_value::T as display;
|
||||
|
||||
if let PropertyDeclaration::Display(new_display) = declaration {
|
||||
if display::should_ignore_parsed_value(old_display, new_display) {
|
||||
return false;
|
||||
}
|
||||
if let PropertyDeclaration::Display(new_display) = declaration {
|
||||
if display::should_ignore_parsed_value(old_display, new_display) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if matches!(mode, DeclarationPushMode::Update) {
|
||||
let important = self.declarations_importance[i];
|
||||
if *slot == declaration && important == importance.important() {
|
||||
return false;
|
||||
}
|
||||
*slot = declaration;
|
||||
self.declarations_importance.set(i, importance.important());
|
||||
return true;
|
||||
}
|
||||
|
||||
index_to_remove = Some(i);
|
||||
break;
|
||||
|
@ -579,6 +542,183 @@ impl PropertyDeclarationBlock {
|
|||
true
|
||||
}
|
||||
|
||||
/// Prepares updating this declaration block with the given
|
||||
/// `SourcePropertyDeclaration` and importance, and returns whether
|
||||
/// there is something to update.
|
||||
pub fn prepare_for_update(
|
||||
&self,
|
||||
source_declarations: &SourcePropertyDeclaration,
|
||||
importance: Importance,
|
||||
updates: &mut SourcePropertyDeclarationUpdate,
|
||||
) -> bool {
|
||||
debug_assert!(updates.updates.is_empty());
|
||||
// Check whether we are updating for an all shorthand change.
|
||||
if !matches!(source_declarations.all_shorthand, AllShorthand::NotSet) {
|
||||
debug_assert!(source_declarations.declarations.is_empty());
|
||||
return source_declarations.all_shorthand.declarations().any(|decl| {
|
||||
self.is_definitely_new(&decl) ||
|
||||
self.declarations.iter().enumerate()
|
||||
.find(|&(_, ref d)| d.id() == decl.id())
|
||||
.map_or(true, |(i, d)| {
|
||||
let important = self.declarations_importance[i];
|
||||
*d != decl || important != importance.important()
|
||||
})
|
||||
});
|
||||
}
|
||||
// Fill `updates` with update information.
|
||||
let mut any_update = false;
|
||||
let new_count = &mut updates.new_count;
|
||||
let any_removal = &mut updates.any_removal;
|
||||
let updates = &mut updates.updates;
|
||||
updates.extend(source_declarations.declarations.iter().map(|declaration| {
|
||||
if self.is_definitely_new(declaration) {
|
||||
return DeclarationUpdate::Append;
|
||||
}
|
||||
let longhand_id = declaration.id().as_longhand();
|
||||
if let Some(longhand_id) = longhand_id {
|
||||
if let Some(logical_group) = longhand_id.logical_group() {
|
||||
let mut needs_append = false;
|
||||
for (pos, decl) in self.declarations.iter().enumerate().rev() {
|
||||
let id = match decl.id().as_longhand() {
|
||||
Some(id) => id,
|
||||
None => continue,
|
||||
};
|
||||
if id == longhand_id {
|
||||
if needs_append {
|
||||
return DeclarationUpdate::AppendAndRemove { pos };
|
||||
}
|
||||
let important = self.declarations_importance[pos];
|
||||
if decl == declaration && important == importance.important() {
|
||||
return DeclarationUpdate::None;
|
||||
}
|
||||
return DeclarationUpdate::UpdateInPlace { pos };
|
||||
}
|
||||
if !needs_append &&
|
||||
id.logical_group() == Some(logical_group) &&
|
||||
id.is_logical() != longhand_id.is_logical() {
|
||||
needs_append = true;
|
||||
}
|
||||
}
|
||||
unreachable!("Longhand should be found in loop above");
|
||||
}
|
||||
}
|
||||
self.declarations.iter().enumerate()
|
||||
.find(|&(_, ref decl)| decl.id() == declaration.id())
|
||||
.map_or(DeclarationUpdate::Append, |(pos, decl)| {
|
||||
let important = self.declarations_importance[pos];
|
||||
if decl == declaration && important == importance.important() {
|
||||
DeclarationUpdate::None
|
||||
} else {
|
||||
DeclarationUpdate::UpdateInPlace { pos }
|
||||
}
|
||||
})
|
||||
}).inspect(|update| {
|
||||
if matches!(update, DeclarationUpdate::None) {
|
||||
return;
|
||||
}
|
||||
any_update = true;
|
||||
match update {
|
||||
DeclarationUpdate::Append => {
|
||||
*new_count += 1;
|
||||
}
|
||||
DeclarationUpdate::AppendAndRemove { .. } => {
|
||||
*any_removal = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}));
|
||||
any_update
|
||||
}
|
||||
|
||||
/// Update this declaration block with the given data.
|
||||
pub fn update(
|
||||
&mut self,
|
||||
drain: SourcePropertyDeclarationDrain,
|
||||
importance: Importance,
|
||||
updates: &mut SourcePropertyDeclarationUpdate,
|
||||
) {
|
||||
let important = importance.important();
|
||||
if !matches!(drain.all_shorthand, AllShorthand::NotSet) {
|
||||
debug_assert!(updates.updates.is_empty());
|
||||
for decl in drain.all_shorthand.declarations() {
|
||||
if self.is_definitely_new(&decl) {
|
||||
let longhand_id = decl.id().as_longhand().unwrap();
|
||||
self.declarations.push(decl);
|
||||
self.declarations_importance.push(important);
|
||||
self.longhands.insert(longhand_id);
|
||||
} else {
|
||||
let (idx, slot) = self.declarations.iter_mut()
|
||||
.enumerate().find(|&(_, ref d)| d.id() == decl.id()).unwrap();
|
||||
*slot = decl;
|
||||
self.declarations_importance.set(idx, important);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
self.declarations.reserve(updates.new_count);
|
||||
if updates.any_removal {
|
||||
// Prepare for removal and fixing update positions.
|
||||
struct UpdateOrRemoval<'a> {
|
||||
item: &'a mut DeclarationUpdate,
|
||||
pos: usize,
|
||||
remove: bool,
|
||||
}
|
||||
let mut updates_and_removals: SubpropertiesVec<UpdateOrRemoval> =
|
||||
updates.updates.iter_mut().filter_map(|item| {
|
||||
let (pos, remove) = match *item {
|
||||
DeclarationUpdate::UpdateInPlace { pos } => (pos, false),
|
||||
DeclarationUpdate::AppendAndRemove { pos } => (pos, true),
|
||||
_ => return None,
|
||||
};
|
||||
Some(UpdateOrRemoval { item, pos, remove })
|
||||
}).collect();
|
||||
// Execute removals. It's important to do it in reverse index order,
|
||||
// so that removing doesn't invalidate following positions.
|
||||
updates_and_removals.sort_unstable_by_key(|update| update.pos);
|
||||
updates_and_removals.iter().rev()
|
||||
.filter(|update| update.remove)
|
||||
.for_each(|update| {
|
||||
self.declarations.remove(update.pos);
|
||||
self.declarations_importance.remove(update.pos);
|
||||
});
|
||||
// Fixup pos field for updates.
|
||||
let mut removed_count = 0;
|
||||
for update in updates_and_removals.iter_mut() {
|
||||
if update.remove {
|
||||
removed_count += 1;
|
||||
continue;
|
||||
}
|
||||
debug_assert_eq!(
|
||||
*update.item,
|
||||
DeclarationUpdate::UpdateInPlace { pos: update.pos }
|
||||
);
|
||||
*update.item = DeclarationUpdate::UpdateInPlace {
|
||||
pos: update.pos - removed_count
|
||||
};
|
||||
}
|
||||
}
|
||||
// Execute updates and appends.
|
||||
for (decl, update) in drain.declarations.zip_eq(updates.updates.iter()) {
|
||||
match *update {
|
||||
DeclarationUpdate::None => {},
|
||||
DeclarationUpdate::Append |
|
||||
DeclarationUpdate::AppendAndRemove { .. } => {
|
||||
if let Some(id) = decl.id().as_longhand() {
|
||||
self.longhands.insert(id);
|
||||
}
|
||||
self.declarations.push(decl);
|
||||
self.declarations_importance.push(important);
|
||||
}
|
||||
DeclarationUpdate::UpdateInPlace { pos } => {
|
||||
self.declarations[pos] = decl;
|
||||
self.declarations_importance.set(pos, important);
|
||||
}
|
||||
}
|
||||
}
|
||||
updates.updates.clear();
|
||||
}
|
||||
|
||||
/// Returns the first declaration that would be removed by removing
|
||||
/// `property`.
|
||||
#[inline]
|
||||
|
@ -1225,7 +1365,6 @@ pub fn parse_property_declaration_list(
|
|||
block.extend(
|
||||
iter.parser.declarations.drain(),
|
||||
importance,
|
||||
DeclarationPushMode::Parsing,
|
||||
);
|
||||
}
|
||||
Err((error, slice)) => {
|
||||
|
|
|
@ -5379,21 +5379,21 @@ clip-path
|
|||
|
||||
#[allow(unused_unsafe)]
|
||||
pub fn set_column_count(&mut self, v: longhands::column_count::computed_value::T) {
|
||||
use gecko_bindings::structs::{NS_STYLE_COLUMN_COUNT_AUTO, nsStyleColumn_kMaxColumnCount};
|
||||
use gecko_bindings::structs::{nsStyleColumn_kColumnCountAuto, nsStyleColumn_kMaxColumnCount};
|
||||
|
||||
self.gecko.mColumnCount = match v {
|
||||
ColumnCount::Integer(integer) => {
|
||||
cmp::min(integer.0 as u32, unsafe { nsStyleColumn_kMaxColumnCount })
|
||||
},
|
||||
ColumnCount::Auto => NS_STYLE_COLUMN_COUNT_AUTO
|
||||
ColumnCount::Auto => nsStyleColumn_kColumnCountAuto
|
||||
};
|
||||
}
|
||||
|
||||
${impl_simple_copy('column_count', 'mColumnCount')}
|
||||
|
||||
pub fn clone_column_count(&self) -> longhands::column_count::computed_value::T {
|
||||
use gecko_bindings::structs::{NS_STYLE_COLUMN_COUNT_AUTO, nsStyleColumn_kMaxColumnCount};
|
||||
if self.gecko.mColumnCount != NS_STYLE_COLUMN_COUNT_AUTO {
|
||||
use gecko_bindings::structs::{nsStyleColumn_kColumnCountAuto, nsStyleColumn_kMaxColumnCount};
|
||||
if self.gecko.mColumnCount != nsStyleColumn_kColumnCountAuto {
|
||||
debug_assert!(self.gecko.mColumnCount >= 1 &&
|
||||
self.gecko.mColumnCount <= nsStyleColumn_kMaxColumnCount);
|
||||
ColumnCount::Integer((self.gecko.mColumnCount as i32).into())
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<%namespace name="helpers" file="/helpers.mako.rs" />
|
||||
|
||||
<%
|
||||
from data import to_idl_name, SYSTEM_FONT_LONGHANDS
|
||||
from data import to_idl_name, SYSTEM_FONT_LONGHANDS, to_camel_case
|
||||
from itertools import groupby
|
||||
%>
|
||||
|
||||
|
@ -114,7 +114,7 @@ pub fn nscsspropertyid_is_transitionable(property: nsCSSPropertyID) -> bool {
|
|||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
||||
pub enum AnimatedProperty {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable:
|
||||
% if prop.animatable and not prop.logical:
|
||||
<%
|
||||
value_type = "longhands::{}::computed_value::T".format(prop.ident)
|
||||
if not prop.is_animatable_with_computed_value:
|
||||
|
@ -131,7 +131,7 @@ impl AnimatedProperty {
|
|||
pub fn name(&self) -> &'static str {
|
||||
match *self {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable:
|
||||
% if prop.animatable and not prop.logical:
|
||||
AnimatedProperty::${prop.camel_case}(..) => "${prop.name}",
|
||||
% endif
|
||||
% endfor
|
||||
|
@ -143,7 +143,7 @@ impl AnimatedProperty {
|
|||
pub fn does_animate(&self) -> bool {
|
||||
match *self {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable:
|
||||
% if prop.animatable and not prop.logical:
|
||||
AnimatedProperty::${prop.camel_case}(ref from, ref to) => from != to,
|
||||
% endif
|
||||
% endfor
|
||||
|
@ -154,7 +154,7 @@ impl AnimatedProperty {
|
|||
pub fn has_the_same_end_value_as(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable:
|
||||
% if prop.animatable and not prop.logical:
|
||||
(&AnimatedProperty::${prop.camel_case}(_, ref this_end_value),
|
||||
&AnimatedProperty::${prop.camel_case}(_, ref other_end_value)) => {
|
||||
this_end_value == other_end_value
|
||||
|
@ -173,7 +173,7 @@ impl AnimatedProperty {
|
|||
{
|
||||
match *self {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable:
|
||||
% if prop.animatable and not prop.logical:
|
||||
AnimatedProperty::${prop.camel_case}(ref from, ref to) => {
|
||||
// https://drafts.csswg.org/web-animations/#discrete-animation-type
|
||||
% if prop.animation_value_type == "discrete":
|
||||
|
@ -203,9 +203,12 @@ impl AnimatedProperty {
|
|||
old_style: &ComputedValues,
|
||||
new_style: &ComputedValues,
|
||||
) -> Option<AnimatedProperty> {
|
||||
// FIXME(emilio): Handle the case where old_style and new_style's
|
||||
// writing mode differ.
|
||||
let property = property.to_physical(new_style.writing_mode);
|
||||
Some(match property {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable:
|
||||
% if prop.animatable and not prop.logical:
|
||||
LonghandId::${prop.camel_case} => {
|
||||
let old_computed = old_style.clone_${prop.ident}();
|
||||
let new_computed = new_style.clone_${prop.ident}();
|
||||
|
@ -254,11 +257,10 @@ unsafe impl HasSimpleFFI for AnimationValueMap {}
|
|||
#[repr(u16)]
|
||||
pub enum AnimationValue {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable:
|
||||
/// `${prop.name}`
|
||||
% if prop.animatable and not prop.logical:
|
||||
${prop.camel_case}(${prop.animated_type()}),
|
||||
% else:
|
||||
/// `${prop.name}` (not animatable)
|
||||
${prop.camel_case}(Void),
|
||||
% endif
|
||||
% endfor
|
||||
|
@ -267,8 +269,11 @@ pub enum AnimationValue {
|
|||
<%
|
||||
animated = []
|
||||
unanimated = []
|
||||
animated_with_logical = []
|
||||
for prop in data.longhands:
|
||||
if prop.animatable:
|
||||
animated_with_logical.append(prop)
|
||||
if prop.animatable and not prop.logical:
|
||||
animated.append(prop)
|
||||
else:
|
||||
unanimated.append(prop)
|
||||
|
@ -370,7 +375,7 @@ impl AnimationValue {
|
|||
let id = unsafe { *(self as *const _ as *const LonghandId) };
|
||||
debug_assert_eq!(id, match *self {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable:
|
||||
% if prop.animatable and not prop.logical:
|
||||
AnimationValue::${prop.camel_case}(..) => LonghandId::${prop.camel_case},
|
||||
% else:
|
||||
AnimationValue::${prop.camel_case}(void) => void::unreachable(void),
|
||||
|
@ -444,17 +449,18 @@ impl AnimationValue {
|
|||
%>
|
||||
|
||||
let animatable = match *decl {
|
||||
% for (specified_ty, ty, boxed, to_animated, inherit, system), props in groupby(animated, key=keyfunc):
|
||||
% for (specified_ty, ty, boxed, to_animated, inherit, system), props in groupby(animated_with_logical, key=keyfunc):
|
||||
${" |\n".join("PropertyDeclaration::{}(ref value)".format(prop.camel_case) for prop in props)} => {
|
||||
let decl_repr = unsafe {
|
||||
&*(decl as *const _ as *const PropertyDeclarationVariantRepr<${specified_ty}>)
|
||||
};
|
||||
let longhand_id = unsafe {
|
||||
*(&decl_repr.tag as *const u16 as *const LonghandId)
|
||||
};
|
||||
% if inherit:
|
||||
context.for_non_inherited_property = None;
|
||||
% else:
|
||||
context.for_non_inherited_property = unsafe {
|
||||
Some(*(&decl_repr.tag as *const u16 as *const LonghandId))
|
||||
};
|
||||
context.for_non_inherited_property = Some(longhand_id);
|
||||
% endif
|
||||
% if system:
|
||||
if let Some(sf) = value.get_system() {
|
||||
|
@ -475,7 +481,7 @@ impl AnimationValue {
|
|||
ptr::write(
|
||||
&mut out as *mut _ as *mut AnimationValueVariantRepr<${ty}>,
|
||||
AnimationValueVariantRepr {
|
||||
tag: decl_repr.tag,
|
||||
tag: longhand_id.to_physical(context.builder.writing_mode) as u16,
|
||||
value,
|
||||
},
|
||||
);
|
||||
|
@ -492,24 +498,40 @@ impl AnimationValue {
|
|||
LonghandId::${prop.camel_case} => {
|
||||
let style_struct = match declaration.keyword {
|
||||
% if not prop.style_struct.inherited:
|
||||
CSSWideKeyword::Unset |
|
||||
CSSWideKeyword::Unset |
|
||||
% endif
|
||||
CSSWideKeyword::Initial => {
|
||||
initial.get_${prop.style_struct.name_lower}()
|
||||
},
|
||||
% if prop.style_struct.inherited:
|
||||
CSSWideKeyword::Unset |
|
||||
CSSWideKeyword::Unset |
|
||||
% endif
|
||||
CSSWideKeyword::Inherit => {
|
||||
context.builder
|
||||
.get_parent_${prop.style_struct.name_lower}()
|
||||
},
|
||||
};
|
||||
let computed = style_struct.clone_${prop.ident}();
|
||||
let computed = style_struct
|
||||
% if prop.logical:
|
||||
.clone_${prop.ident}(context.builder.writing_mode);
|
||||
% else:
|
||||
.clone_${prop.ident}();
|
||||
% endif
|
||||
|
||||
% if not prop.is_animatable_with_computed_value:
|
||||
let computed = computed.to_animated_value();
|
||||
% endif
|
||||
AnimationValue::${prop.camel_case}(computed)
|
||||
|
||||
% if prop.logical:
|
||||
let wm = context.builder.writing_mode;
|
||||
<%helpers:logical_setter_helper name="${prop.name}">
|
||||
<%def name="inner(physical_ident)">
|
||||
AnimationValue::${to_camel_case(physical_ident)}(computed)
|
||||
</%def>
|
||||
</%helpers:logical_setter_helper>
|
||||
% else:
|
||||
AnimationValue::${prop.camel_case}(computed)
|
||||
% endif
|
||||
},
|
||||
% endif
|
||||
% endfor
|
||||
|
@ -548,9 +570,10 @@ impl AnimationValue {
|
|||
property: LonghandId,
|
||||
style: &ComputedValues,
|
||||
) -> Option<Self> {
|
||||
let property = property.to_physical(style.writing_mode);
|
||||
Some(match property {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable:
|
||||
% if prop.animatable and not prop.logical:
|
||||
LonghandId::${prop.camel_case} => {
|
||||
let computed = style.clone_${prop.ident}();
|
||||
AnimationValue::${prop.camel_case}(
|
||||
|
@ -656,7 +679,7 @@ impl ToAnimatedZero for AnimationValue {
|
|||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||
match *self {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable and prop.animation_value_type != "discrete":
|
||||
% if prop.animatable and not prop.logical and prop.animation_value_type != "discrete":
|
||||
AnimationValue::${prop.camel_case}(ref base) => {
|
||||
Ok(AnimationValue::${prop.camel_case}(base.to_animated_zero()?))
|
||||
},
|
||||
|
@ -2924,43 +2947,67 @@ impl ToAnimatedZero for AnimatedFilter {
|
|||
}
|
||||
}
|
||||
|
||||
/// A comparator to sort PropertyIds such that longhands are sorted before shorthands,
|
||||
/// shorthands with fewer components are sorted before shorthands with more components,
|
||||
/// and otherwise shorthands are sorted by IDL name as defined by [Web Animations][property-order].
|
||||
/// The category a property falls into for ordering purposes.
|
||||
///
|
||||
/// https://drafts.csswg.org/web-animations/#calculating-computed-keyframes
|
||||
///
|
||||
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
|
||||
enum PropertyCategory {
|
||||
Custom,
|
||||
PhysicalLonghand,
|
||||
LogicalLonghand,
|
||||
Shorthand,
|
||||
}
|
||||
|
||||
impl PropertyCategory {
|
||||
fn of(id: &PropertyId) -> Self {
|
||||
match *id {
|
||||
PropertyId::Shorthand(..) |
|
||||
PropertyId::ShorthandAlias(..) => PropertyCategory::Shorthand,
|
||||
PropertyId::Longhand(id) |
|
||||
PropertyId::LonghandAlias(id, ..) => {
|
||||
if id.is_logical() {
|
||||
PropertyCategory::LogicalLonghand
|
||||
} else {
|
||||
PropertyCategory::PhysicalLonghand
|
||||
}
|
||||
}
|
||||
PropertyId::Custom(..) => PropertyCategory::Custom,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A comparator to sort PropertyIds such that physical longhands are sorted
|
||||
/// before logical longhands and shorthands, shorthands with fewer components
|
||||
/// are sorted before shorthands with more components, and otherwise shorthands
|
||||
/// are sorted by IDL name as defined by [Web Animations][property-order].
|
||||
///
|
||||
/// Using this allows us to prioritize values specified by longhands (or smaller
|
||||
/// shorthand subsets) when longhands and shorthands are both specified on the one keyframe.
|
||||
///
|
||||
/// Example orderings that result from this:
|
||||
///
|
||||
/// margin-left, margin
|
||||
///
|
||||
/// and:
|
||||
///
|
||||
/// border-top-color, border-color, border-top, border
|
||||
/// shorthand subsets) when longhands and shorthands are both specified on the
|
||||
/// one keyframe.
|
||||
///
|
||||
/// [property-order] https://drafts.csswg.org/web-animations/#calculating-computed-keyframes
|
||||
pub fn compare_property_priority(a: &PropertyId, b: &PropertyId) -> cmp::Ordering {
|
||||
match (a.as_shorthand(), b.as_shorthand()) {
|
||||
// Within shorthands, sort by the number of subproperties, then by IDL name.
|
||||
(Ok(a), Ok(b)) => {
|
||||
let subprop_count_a = a.longhands().count();
|
||||
let subprop_count_b = b.longhands().count();
|
||||
subprop_count_a
|
||||
.cmp(&subprop_count_b)
|
||||
.then_with(|| {
|
||||
get_idl_name_sort_order(a).cmp(&get_idl_name_sort_order(b))
|
||||
})
|
||||
},
|
||||
let a_category = PropertyCategory::of(a);
|
||||
let b_category = PropertyCategory::of(b);
|
||||
|
||||
// Longhands go before shorthands.
|
||||
(Ok(_), Err(_)) => cmp::Ordering::Greater,
|
||||
(Err(_), Ok(_)) => cmp::Ordering::Less,
|
||||
|
||||
// Both are longhands or custom properties in which case they don't overlap and should
|
||||
// sort equally.
|
||||
_ => cmp::Ordering::Equal,
|
||||
if a_category != b_category {
|
||||
return a_category.cmp(&b_category);
|
||||
}
|
||||
|
||||
if a_category == PropertyCategory::Shorthand {
|
||||
let a = a.as_shorthand().unwrap();
|
||||
let b = b.as_shorthand().unwrap();
|
||||
// Within shorthands, sort by the number of subproperties, then by IDL
|
||||
// name.
|
||||
let subprop_count_a = a.longhands().count();
|
||||
let subprop_count_b = b.longhands().count();
|
||||
return subprop_count_a.cmp(&subprop_count_b).then_with(|| {
|
||||
get_idl_name_sort_order(a).cmp(&get_idl_name_sort_order(b))
|
||||
});
|
||||
}
|
||||
|
||||
cmp::Ordering::Equal
|
||||
}
|
||||
|
||||
fn get_idl_name_sort_order(shorthand: ShorthandId) -> u32 {
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
spec=maybe_logical_spec(side, "color"),
|
||||
animation_value_type="AnimatedColor",
|
||||
logical=is_logical,
|
||||
logical_group="border-color",
|
||||
allow_quirks=not is_logical,
|
||||
flags="APPLIES_TO_FIRST_LETTER",
|
||||
ignored_when_colors_disabled=True,
|
||||
|
@ -40,6 +41,7 @@
|
|||
flags="APPLIES_TO_FIRST_LETTER",
|
||||
animation_value_type="discrete" if not is_logical else "none",
|
||||
logical=is_logical,
|
||||
logical_group="border-style",
|
||||
needs_context=False,
|
||||
)}
|
||||
|
||||
|
@ -52,6 +54,7 @@
|
|||
spec=maybe_logical_spec(side, "width"),
|
||||
animation_value_type="NonNegativeLength",
|
||||
logical=is_logical,
|
||||
logical_group="border-width",
|
||||
flags="APPLIES_TO_FIRST_LETTER GETCS_NEEDS_LAYOUT_FLUSH",
|
||||
allow_quirks=not is_logical,
|
||||
servo_restyle_damage="reflow rebuild_and_reflow_inline"
|
||||
|
|
|
@ -33,6 +33,7 @@ ${helpers.predefined_type(
|
|||
|
||||
${helpers.single_keyword("column-fill", "balance auto", extra_prefixes="moz",
|
||||
products="gecko", animation_value_type="discrete",
|
||||
gecko_enum_prefix="StyleColumnFill",
|
||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-fill")}
|
||||
|
||||
${helpers.predefined_type("column-rule-width",
|
||||
|
@ -60,6 +61,7 @@ ${helpers.predefined_type(
|
|||
|
||||
${helpers.single_keyword("column-span", "none all",
|
||||
products="gecko", animation_value_type="discrete",
|
||||
gecko_enum_prefix="StyleColumnSpan",
|
||||
gecko_pref="layout.css.column-span.enabled",
|
||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-span",
|
||||
extra_prefixes="moz:layout.css.column-span.enabled")}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
allow_quirks=not side[1],
|
||||
animation_value_type="ComputedValue",
|
||||
logical=side[1],
|
||||
logical_group="margin",
|
||||
spec=spec,
|
||||
flags="APPLIES_TO_FIRST_LETTER GETCS_NEEDS_LAYOUT_FLUSH",
|
||||
allowed_in_page_rule=True,
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
alias=maybe_moz_logical_alias(product, side, "-moz-padding-%s"),
|
||||
animation_value_type="NonNegativeLengthOrPercentage",
|
||||
logical=side[1],
|
||||
logical_group="padding",
|
||||
spec=spec,
|
||||
flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_PLACEHOLDER GETCS_NEEDS_LAYOUT_FLUSH",
|
||||
allow_quirks=not side[1],
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
flags="GETCS_NEEDS_LAYOUT_FLUSH",
|
||||
animation_value_type="ComputedValue",
|
||||
allow_quirks=True,
|
||||
servo_restyle_damage="reflow_out_of_flow"
|
||||
servo_restyle_damage="reflow_out_of_flow",
|
||||
logical_group="inset",
|
||||
)}
|
||||
% endfor
|
||||
// inset-* logical properties, map to "top" / "left" / "bottom" / "right"
|
||||
|
@ -32,6 +33,7 @@
|
|||
alias="offset-%s:layout.css.offset-logical-properties.enabled" % side,
|
||||
animation_value_type="ComputedValue",
|
||||
logical=True,
|
||||
logical_group="inset",
|
||||
)}
|
||||
% endfor
|
||||
|
||||
|
@ -221,6 +223,7 @@ ${helpers.predefined_type(
|
|||
"computed::MozLength::auto()",
|
||||
parse_function,
|
||||
logical=logical,
|
||||
logical_group="size",
|
||||
allow_quirks=not logical,
|
||||
spec=spec % size,
|
||||
animation_value_type="MozLength",
|
||||
|
@ -234,6 +237,7 @@ ${helpers.predefined_type(
|
|||
"computed::MozLength::auto()",
|
||||
parse_function,
|
||||
logical=logical,
|
||||
logical_group="min-size",
|
||||
allow_quirks=not logical,
|
||||
spec=spec % size,
|
||||
animation_value_type="MozLength",
|
||||
|
@ -245,6 +249,7 @@ ${helpers.predefined_type(
|
|||
"computed::MaxLength::none()",
|
||||
parse_function,
|
||||
logical=logical,
|
||||
logical_group="max-size",
|
||||
allow_quirks=not logical,
|
||||
spec=spec % size,
|
||||
animation_value_type="MaxLength",
|
||||
|
@ -252,32 +257,41 @@ ${helpers.predefined_type(
|
|||
)}
|
||||
% else:
|
||||
// servo versions (no keyword support)
|
||||
${helpers.predefined_type(size,
|
||||
"LengthOrPercentageOrAuto",
|
||||
"computed::LengthOrPercentageOrAuto::Auto",
|
||||
"parse_non_negative",
|
||||
spec=spec % size,
|
||||
allow_quirks=not logical,
|
||||
animation_value_type="ComputedValue", logical = logical,
|
||||
servo_restyle_damage = "reflow")}
|
||||
${helpers.predefined_type("min-%s" % size,
|
||||
"LengthOrPercentage",
|
||||
"computed::LengthOrPercentage::Length(computed::Length::new(0.))",
|
||||
"parse_non_negative",
|
||||
spec=spec % ("min-%s" % size),
|
||||
animation_value_type="ComputedValue",
|
||||
logical=logical,
|
||||
allow_quirks=not logical,
|
||||
servo_restyle_damage = "reflow")}
|
||||
${helpers.predefined_type("max-%s" % size,
|
||||
"LengthOrPercentageOrNone",
|
||||
"computed::LengthOrPercentageOrNone::None",
|
||||
"parse_non_negative",
|
||||
spec=spec % ("min-%s" % size),
|
||||
animation_value_type="ComputedValue",
|
||||
logical=logical,
|
||||
allow_quirks=not logical,
|
||||
servo_restyle_damage = "reflow")}
|
||||
${helpers.predefined_type(
|
||||
size,
|
||||
"LengthOrPercentageOrAuto",
|
||||
"computed::LengthOrPercentageOrAuto::Auto",
|
||||
"parse_non_negative",
|
||||
spec=spec % size,
|
||||
logical_group="size",
|
||||
allow_quirks=not logical,
|
||||
animation_value_type="ComputedValue", logical = logical,
|
||||
servo_restyle_damage = "reflow",
|
||||
)}
|
||||
${helpers.predefined_type(
|
||||
"min-%s" % size,
|
||||
"LengthOrPercentage",
|
||||
"computed::LengthOrPercentage::Length(computed::Length::new(0.))",
|
||||
"parse_non_negative",
|
||||
spec=spec % ("min-%s" % size),
|
||||
logical_group="min-size",
|
||||
animation_value_type="ComputedValue",
|
||||
logical=logical,
|
||||
allow_quirks=not logical,
|
||||
servo_restyle_damage = "reflow",
|
||||
)}
|
||||
${helpers.predefined_type(
|
||||
"max-%s" % size,
|
||||
"LengthOrPercentageOrNone",
|
||||
"computed::LengthOrPercentageOrNone::None",
|
||||
"parse_non_negative",
|
||||
spec=spec % ("max-%s" % size),
|
||||
logical_group="max-size",
|
||||
animation_value_type="ComputedValue",
|
||||
logical=logical,
|
||||
allow_quirks=not logical,
|
||||
servo_restyle_damage = "reflow",
|
||||
)}
|
||||
% endif
|
||||
% endfor
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#[cfg(feature = "servo")]
|
||||
use app_units::Au;
|
||||
use arrayvec::{ArrayVec, Drain as ArrayVecDrain};
|
||||
use dom::TElement;
|
||||
use custom_properties::CustomPropertiesBuilder;
|
||||
use servo_arc::{Arc, UniqueArc};
|
||||
|
@ -56,6 +57,7 @@ use style_adjuster::StyleAdjuster;
|
|||
pub use self::declaration_block::*;
|
||||
|
||||
<%!
|
||||
from collections import defaultdict
|
||||
from data import Method, Keyword, to_rust_ident, to_camel_case, SYSTEM_FONT_LONGHANDS
|
||||
import os.path
|
||||
%>
|
||||
|
@ -841,6 +843,29 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
<%
|
||||
logical_groups = defaultdict(list)
|
||||
for prop in data.longhands:
|
||||
if prop.logical_group:
|
||||
logical_groups[prop.logical_group].append(prop)
|
||||
|
||||
for group, props in logical_groups.iteritems():
|
||||
logical_count = sum(1 for p in props if p.logical)
|
||||
if logical_count * 2 != len(props):
|
||||
raise RuntimeError("Logical group {} has ".format(group) +
|
||||
"unbalanced logical / physical properties")
|
||||
%>
|
||||
|
||||
/// A group for properties which may override each other
|
||||
/// via logical resolution.
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||
pub enum LogicalGroup {
|
||||
% for group in logical_groups.iterkeys():
|
||||
/// ${group}
|
||||
${to_camel_case(group)},
|
||||
% endfor
|
||||
}
|
||||
|
||||
/// An identifier for a given longhand property.
|
||||
#[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq)]
|
||||
#[repr(u16)]
|
||||
|
@ -993,9 +1018,15 @@ impl LonghandId {
|
|||
match *self {
|
||||
% for property in data.longhands:
|
||||
% if property.logical:
|
||||
<% logical_group = property.logical_group %>
|
||||
LonghandId::${property.camel_case} => {
|
||||
<%helpers:logical_setter_helper name="${property.name}">
|
||||
<%def name="inner(physical_ident)">
|
||||
<%
|
||||
physical_name = physical_ident.replace("_", "-")
|
||||
physical_property = data.longhands_by_name[physical_name]
|
||||
assert logical_group == physical_property.logical_group
|
||||
%>
|
||||
LonghandId::${to_camel_case(physical_ident)}
|
||||
</%def>
|
||||
</%helpers:logical_setter_helper>
|
||||
|
@ -1006,6 +1037,20 @@ impl LonghandId {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return the logical group of this longhand property.
|
||||
pub fn logical_group(&self) -> Option<LogicalGroup> {
|
||||
const LOGICAL_GROUPS: [Option<LogicalGroup>; ${len(data.longhands)}] = [
|
||||
% for prop in data.longhands:
|
||||
% if prop.logical_group:
|
||||
Some(LogicalGroup::${to_camel_case(prop.logical_group)}),
|
||||
% else:
|
||||
None,
|
||||
% endif
|
||||
% endfor
|
||||
];
|
||||
LOGICAL_GROUPS[*self as usize]
|
||||
}
|
||||
|
||||
/// Returns PropertyFlags for given longhand property.
|
||||
pub fn flags(&self) -> PropertyFlags {
|
||||
match *self {
|
||||
|
@ -1497,7 +1542,7 @@ impl UnparsedValue {
|
|||
|
||||
/// An identifier for a given property declaration, which can be either a
|
||||
/// longhand or a custom property.
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
||||
pub enum PropertyDeclarationId<'a> {
|
||||
/// A longhand.
|
||||
|
@ -1561,6 +1606,15 @@ impl<'a> PropertyDeclarationId<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns longhand id if it is, None otherwise.
|
||||
#[inline]
|
||||
pub fn as_longhand(&self) -> Option<LonghandId> {
|
||||
match *self {
|
||||
PropertyDeclarationId::Longhand(id) => Some(id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Servo's representation of a CSS property, that is, either a longhand, a
|
||||
|
@ -1903,7 +1957,7 @@ impl PropertyDeclaration {
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
// This is just fine because PropertyDeclarationId and LonghandId
|
||||
// This is just fine because PropertyDeclaration and LonghandId
|
||||
// have corresponding discriminants.
|
||||
let id = unsafe { *(self as *const _ as *const LonghandId) };
|
||||
debug_assert_eq!(id, match *self {
|
||||
|
@ -1915,6 +1969,69 @@ impl PropertyDeclaration {
|
|||
PropertyDeclarationId::Longhand(id)
|
||||
}
|
||||
|
||||
/// Given a declaration, convert it into a declaration for a corresponding
|
||||
/// physical property.
|
||||
#[inline]
|
||||
pub fn to_physical(&self, wm: WritingMode) -> Self {
|
||||
match *self {
|
||||
PropertyDeclaration::WithVariables(VariableDeclaration {
|
||||
id,
|
||||
ref value,
|
||||
}) => {
|
||||
return PropertyDeclaration::WithVariables(VariableDeclaration {
|
||||
id: id.to_physical(wm),
|
||||
value: value.clone(),
|
||||
})
|
||||
}
|
||||
PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
|
||||
id,
|
||||
keyword,
|
||||
}) => {
|
||||
return PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
|
||||
id: id.to_physical(wm),
|
||||
keyword,
|
||||
})
|
||||
}
|
||||
PropertyDeclaration::Custom(..) => return self.clone(),
|
||||
% for prop in data.longhands:
|
||||
PropertyDeclaration::${prop.camel_case}(..) => {},
|
||||
% endfor
|
||||
}
|
||||
|
||||
let mut ret = self.clone();
|
||||
|
||||
% for prop in data.longhands:
|
||||
% if prop.logical:
|
||||
% for physical_property in prop.all_physical_mapped_properties():
|
||||
% if data.longhands_by_name[physical_property].specified_type() != prop.specified_type():
|
||||
<% raise "Logical property %s should share specified value with physical property %s" % \
|
||||
(prop.name, physical_property) %>
|
||||
% endif
|
||||
% endfor
|
||||
% endif
|
||||
% endfor
|
||||
|
||||
unsafe {
|
||||
let longhand_id = *(&mut ret as *mut _ as *mut LonghandId);
|
||||
|
||||
debug_assert_eq!(
|
||||
PropertyDeclarationId::Longhand(longhand_id),
|
||||
ret.id()
|
||||
);
|
||||
|
||||
// This is just fine because PropertyDeclaration and LonghandId
|
||||
// have corresponding discriminants.
|
||||
*(&mut ret as *mut _ as *mut LonghandId) = longhand_id.to_physical(wm);
|
||||
|
||||
debug_assert_eq!(
|
||||
PropertyDeclarationId::Longhand(longhand_id.to_physical(wm)),
|
||||
ret.id()
|
||||
);
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option< &str> {
|
||||
match *self {
|
||||
PropertyDeclaration::WithVariables(ref declaration) => {
|
||||
|
@ -2160,19 +2277,18 @@ impl PropertyDeclaration {
|
|||
}
|
||||
}
|
||||
|
||||
const MAX_SUB_PROPERTIES_PER_SHORTHAND_EXCEPT_ALL: usize =
|
||||
${max(len(s.sub_properties) for s in data.shorthands_except_all())};
|
||||
type SubpropertiesArray<T> =
|
||||
[T; ${max(len(s.sub_properties) for s in data.shorthands_except_all())}];
|
||||
|
||||
type SourcePropertyDeclarationArray =
|
||||
[PropertyDeclaration; MAX_SUB_PROPERTIES_PER_SHORTHAND_EXCEPT_ALL];
|
||||
type SubpropertiesVec<T> = ArrayVec<SubpropertiesArray<T>>;
|
||||
|
||||
/// A stack-allocated vector of `PropertyDeclaration`
|
||||
/// large enough to parse one CSS `key: value` declaration.
|
||||
/// (Shorthands expand to multiple `PropertyDeclaration`s.)
|
||||
pub struct SourcePropertyDeclaration {
|
||||
declarations: ::arrayvec::ArrayVec<SourcePropertyDeclarationArray>,
|
||||
declarations: SubpropertiesVec<PropertyDeclaration>,
|
||||
|
||||
/// Stored separately to keep MAX_SUB_PROPERTIES_PER_SHORTHAND_EXCEPT_ALL smaller.
|
||||
/// Stored separately to keep SubpropertiesVec smaller.
|
||||
all_shorthand: AllShorthand,
|
||||
}
|
||||
|
||||
|
@ -2212,7 +2328,7 @@ impl SourcePropertyDeclaration {
|
|||
|
||||
/// Return type of SourcePropertyDeclaration::drain
|
||||
pub struct SourcePropertyDeclarationDrain<'a> {
|
||||
declarations: ::arrayvec::Drain<'a, SourcePropertyDeclarationArray>,
|
||||
declarations: ArrayVecDrain<'a, SubpropertiesArray<PropertyDeclaration>>,
|
||||
all_shorthand: AllShorthand,
|
||||
}
|
||||
|
||||
|
@ -2630,11 +2746,6 @@ impl ComputedValues {
|
|||
self.get_box().clone_display().is_contents()
|
||||
}
|
||||
|
||||
/// Whether we're a visited style.
|
||||
pub fn is_style_if_visited(&self) -> bool {
|
||||
self.flags.contains(ComputedValueFlags::IS_STYLE_IF_VISITED)
|
||||
}
|
||||
|
||||
/// Gets a reference to the rule node. Panic if no rule node exists.
|
||||
pub fn rules(&self) -> &StrongRuleNode {
|
||||
self.rules.as_ref().unwrap()
|
||||
|
@ -3163,10 +3274,8 @@ impl<'a> StyleBuilder<'a> {
|
|||
parent_style: Option<<&'a ComputedValues>,
|
||||
parent_style_ignoring_first_line: Option<<&'a ComputedValues>,
|
||||
pseudo: Option<<&'a PseudoElement>,
|
||||
cascade_flags: CascadeFlags,
|
||||
rules: Option<StrongRuleNode>,
|
||||
custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
|
||||
visited_style: Option<Arc<ComputedValues>>,
|
||||
) -> Self {
|
||||
debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
|
||||
#[cfg(feature = "gecko")]
|
||||
|
@ -3177,20 +3286,17 @@ impl<'a> StyleBuilder<'a> {
|
|||
let reset_style = device.default_computed_values();
|
||||
let inherited_style = parent_style.unwrap_or(reset_style);
|
||||
let inherited_style_ignoring_first_line = parent_style_ignoring_first_line.unwrap_or(reset_style);
|
||||
// FIXME(bz): INHERIT_ALL seems like a fundamentally broken idea. I'm
|
||||
// FIXME(bz): inherits_all seems like a fundamentally broken idea. I'm
|
||||
// 99% sure it should give incorrect behavior for table anonymous box
|
||||
// backgrounds, for example. This code doesn't attempt to make it play
|
||||
// nice with inherited_style_ignoring_first_line.
|
||||
let reset_style = if cascade_flags.contains(CascadeFlags::INHERIT_ALL) {
|
||||
let reset_style = if pseudo.map_or(false, |p| p.inherits_all()) {
|
||||
inherited_style
|
||||
} else {
|
||||
reset_style
|
||||
};
|
||||
|
||||
let mut flags = inherited_style.flags.inherited();
|
||||
if cascade_flags.contains(CascadeFlags::VISITED_DEPENDENT_ONLY) {
|
||||
flags.insert(ComputedValueFlags::IS_STYLE_IF_VISITED);
|
||||
}
|
||||
let flags = inherited_style.flags.inherited();
|
||||
|
||||
StyleBuilder {
|
||||
device,
|
||||
|
@ -3203,7 +3309,7 @@ impl<'a> StyleBuilder<'a> {
|
|||
custom_properties,
|
||||
writing_mode: inherited_style.writing_mode,
|
||||
flags,
|
||||
visited_style,
|
||||
visited_style: None,
|
||||
% for style_struct in data.active_style_structs():
|
||||
% if style_struct.inherited:
|
||||
${style_struct.ident}: StyleStructRef::Borrowed(inherited_style.${style_struct.name_lower}_arc()),
|
||||
|
@ -3214,11 +3320,6 @@ impl<'a> StyleBuilder<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether we're a visited style.
|
||||
pub fn is_style_if_visited(&self) -> bool {
|
||||
self.flags.contains(ComputedValueFlags::IS_STYLE_IF_VISITED)
|
||||
}
|
||||
|
||||
/// NOTE(emilio): This is done so we can compute relative units with respect
|
||||
/// to the parent style, but all the early properties / writing-mode / etc
|
||||
/// are already set to the right ones on the kid.
|
||||
|
@ -3368,16 +3469,16 @@ impl<'a> StyleBuilder<'a> {
|
|||
).build()
|
||||
})
|
||||
});
|
||||
Self::new(
|
||||
let mut ret = Self::new(
|
||||
device,
|
||||
parent,
|
||||
parent,
|
||||
pseudo,
|
||||
CascadeFlags::empty(),
|
||||
/* rules = */ None,
|
||||
parent.and_then(|p| p.custom_properties().cloned()),
|
||||
visited_style,
|
||||
)
|
||||
);
|
||||
ret.visited_style = visited_style;
|
||||
ret
|
||||
}
|
||||
|
||||
/// Returns whether we have a visited style.
|
||||
|
@ -3588,18 +3689,6 @@ static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [
|
|||
% endfor
|
||||
];
|
||||
|
||||
bitflags! {
|
||||
/// A set of flags to tweak the behavior of the `cascade` function.
|
||||
pub struct CascadeFlags: u8 {
|
||||
/// Whether to inherit all styles from the parent. If this flag is not
|
||||
/// present, non-inherited styles are reset to their initial values.
|
||||
const INHERIT_ALL = 1;
|
||||
|
||||
/// Whether to only cascade properties that are visited dependent.
|
||||
const VISITED_DEPENDENT_ONLY = 1 << 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs the CSS cascade, computing new styles for an element from its parent style.
|
||||
///
|
||||
/// The arguments are:
|
||||
|
@ -3622,9 +3711,43 @@ pub fn cascade<E>(
|
|||
parent_style: Option<<&ComputedValues>,
|
||||
parent_style_ignoring_first_line: Option<<&ComputedValues>,
|
||||
layout_parent_style: Option<<&ComputedValues>,
|
||||
visited_style: Option<Arc<ComputedValues>>,
|
||||
visited_rules: Option<<&StrongRuleNode>,
|
||||
font_metrics_provider: &FontMetricsProvider,
|
||||
flags: CascadeFlags,
|
||||
quirks_mode: QuirksMode,
|
||||
rule_cache: Option<<&RuleCache>,
|
||||
rule_cache_conditions: &mut RuleCacheConditions,
|
||||
element: Option<E>,
|
||||
) -> Arc<ComputedValues>
|
||||
where
|
||||
E: TElement,
|
||||
{
|
||||
cascade_rules(
|
||||
device,
|
||||
pseudo,
|
||||
rule_node,
|
||||
guards,
|
||||
parent_style,
|
||||
parent_style_ignoring_first_line,
|
||||
layout_parent_style,
|
||||
font_metrics_provider,
|
||||
CascadeMode::Unvisited { visited_rules },
|
||||
quirks_mode,
|
||||
rule_cache,
|
||||
rule_cache_conditions,
|
||||
element,
|
||||
)
|
||||
}
|
||||
|
||||
fn cascade_rules<E>(
|
||||
device: &Device,
|
||||
pseudo: Option<<&PseudoElement>,
|
||||
rule_node: &StrongRuleNode,
|
||||
guards: &StylesheetGuards,
|
||||
parent_style: Option<<&ComputedValues>,
|
||||
parent_style_ignoring_first_line: Option<<&ComputedValues>,
|
||||
layout_parent_style: Option<<&ComputedValues>,
|
||||
font_metrics_provider: &FontMetricsProvider,
|
||||
cascade_mode: CascadeMode,
|
||||
quirks_mode: QuirksMode,
|
||||
rule_cache: Option<<&RuleCache>,
|
||||
rule_cache_conditions: &mut RuleCacheConditions,
|
||||
|
@ -3635,33 +3758,29 @@ where
|
|||
{
|
||||
debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
|
||||
let empty = SmallBitVec::new();
|
||||
|
||||
let property_restriction = pseudo.and_then(|p| p.property_restriction());
|
||||
|
||||
let restriction = pseudo.and_then(|p| p.property_restriction());
|
||||
let iter_declarations = || {
|
||||
rule_node.self_and_ancestors().flat_map(|node| {
|
||||
let cascade_level = node.cascade_level();
|
||||
let source = node.style_source();
|
||||
|
||||
let declarations = if source.is_some() {
|
||||
source.as_ref().unwrap().read(cascade_level.guard(guards)).declaration_importance_iter()
|
||||
} else {
|
||||
// The root node has no style source.
|
||||
DeclarationImportanceIterator::new(&[], &empty)
|
||||
};
|
||||
let node_importance = node.importance();
|
||||
let declarations = match node.style_source() {
|
||||
Some(source) => {
|
||||
source.read(cascade_level.guard(guards)).declaration_importance_iter()
|
||||
}
|
||||
None => DeclarationImportanceIterator::new(&[], &empty),
|
||||
};
|
||||
|
||||
declarations
|
||||
// Yield declarations later in source order (with more precedence) first.
|
||||
.rev()
|
||||
.filter_map(move |(declaration, declaration_importance)| {
|
||||
if let Some(property_restriction) = property_restriction {
|
||||
if let Some(restriction) = restriction {
|
||||
// declaration.id() is either a longhand or a custom
|
||||
// property. Custom properties are always allowed, but
|
||||
// longhands are only allowed if they have our
|
||||
// property_restriction flag set.
|
||||
// restriction flag set.
|
||||
if let PropertyDeclarationId::Longhand(id) = declaration.id() {
|
||||
if !id.flags().contains(property_restriction) {
|
||||
if !id.flags().contains(restriction) {
|
||||
return None
|
||||
}
|
||||
}
|
||||
|
@ -3685,9 +3804,8 @@ where
|
|||
parent_style,
|
||||
parent_style_ignoring_first_line,
|
||||
layout_parent_style,
|
||||
visited_style,
|
||||
font_metrics_provider,
|
||||
flags,
|
||||
cascade_mode,
|
||||
quirks_mode,
|
||||
rule_cache,
|
||||
rule_cache_conditions,
|
||||
|
@ -3695,6 +3813,22 @@ where
|
|||
)
|
||||
}
|
||||
|
||||
/// Whether we're cascading for visited or unvisited styles.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CascadeMode<'a> {
|
||||
/// We're cascading for unvisited styles.
|
||||
Unvisited {
|
||||
/// The visited rules that should match the visited style.
|
||||
visited_rules: Option<<&'a StrongRuleNode>,
|
||||
},
|
||||
/// We're cascading for visited styles.
|
||||
Visited {
|
||||
/// The writing mode of our unvisited style, needed to correctly resolve
|
||||
/// logical properties..
|
||||
writing_mode: WritingMode,
|
||||
},
|
||||
}
|
||||
|
||||
/// NOTE: This function expects the declaration with more priority to appear
|
||||
/// first.
|
||||
pub fn apply_declarations<'a, E, F, I>(
|
||||
|
@ -3706,9 +3840,8 @@ pub fn apply_declarations<'a, E, F, I>(
|
|||
parent_style: Option<<&ComputedValues>,
|
||||
parent_style_ignoring_first_line: Option<<&ComputedValues>,
|
||||
layout_parent_style: Option<<&ComputedValues>,
|
||||
visited_style: Option<Arc<ComputedValues>>,
|
||||
font_metrics_provider: &FontMetricsProvider,
|
||||
flags: CascadeFlags,
|
||||
cascade_mode: CascadeMode,
|
||||
quirks_mode: QuirksMode,
|
||||
rule_cache: Option<<&RuleCache>,
|
||||
rule_cache_conditions: &mut RuleCacheConditions,
|
||||
|
@ -3726,16 +3859,8 @@ where
|
|||
::std::ptr::eq(parent_style.unwrap(),
|
||||
parent_style_ignoring_first_line.unwrap()) ||
|
||||
parent_style.unwrap().pseudo() == Some(PseudoElement::FirstLine));
|
||||
let (inherited_style, layout_parent_style) = match parent_style {
|
||||
Some(parent_style) => {
|
||||
(parent_style,
|
||||
layout_parent_style.unwrap_or(parent_style))
|
||||
},
|
||||
None => {
|
||||
(device.default_computed_values(),
|
||||
device.default_computed_values())
|
||||
}
|
||||
};
|
||||
let inherited_style =
|
||||
parent_style.unwrap_or(device.default_computed_values());
|
||||
|
||||
let custom_properties = {
|
||||
let mut builder =
|
||||
|
@ -3760,10 +3885,8 @@ where
|
|||
parent_style,
|
||||
parent_style_ignoring_first_line,
|
||||
pseudo,
|
||||
flags,
|
||||
Some(rules.clone()),
|
||||
custom_properties,
|
||||
visited_style,
|
||||
),
|
||||
cached_system_font: None,
|
||||
in_media_query: false,
|
||||
|
@ -3825,7 +3948,7 @@ where
|
|||
// Only a few properties are allowed to depend on the visited state
|
||||
// of links. When cascading visited styles, we can save time by
|
||||
// only processing these properties.
|
||||
if flags.contains(CascadeFlags::VISITED_DEPENDENT_ONLY) &&
|
||||
if matches!(cascade_mode, CascadeMode::Visited { .. }) &&
|
||||
!physical_longhand_id.is_visited_dependent() {
|
||||
continue
|
||||
}
|
||||
|
@ -3890,9 +4013,47 @@ where
|
|||
(CASCADE_PROPERTY[discriminant])(&*declaration, &mut context);
|
||||
}
|
||||
% if category_to_cascade_now == "early":
|
||||
let writing_mode =
|
||||
WritingMode::new(context.builder.get_inherited_box());
|
||||
let writing_mode = match cascade_mode {
|
||||
CascadeMode::Unvisited { .. } => {
|
||||
WritingMode::new(context.builder.get_inherited_box())
|
||||
}
|
||||
CascadeMode::Visited { writing_mode } => writing_mode,
|
||||
};
|
||||
|
||||
context.builder.writing_mode = writing_mode;
|
||||
if let CascadeMode::Unvisited { visited_rules: Some(visited_rules) } = cascade_mode {
|
||||
let is_link = pseudo.is_none() && element.unwrap().is_link();
|
||||
macro_rules! visited_parent {
|
||||
($parent:expr) => {
|
||||
if is_link {
|
||||
$parent
|
||||
} else {
|
||||
$parent.map(|p| p.visited_style().unwrap_or(p))
|
||||
}
|
||||
}
|
||||
}
|
||||
// We could call apply_declarations directly, but that'd cause
|
||||
// another instantiation of this function which is not great.
|
||||
context.builder.visited_style = Some(cascade_rules(
|
||||
device,
|
||||
pseudo,
|
||||
visited_rules,
|
||||
guards,
|
||||
visited_parent!(parent_style),
|
||||
visited_parent!(parent_style_ignoring_first_line),
|
||||
visited_parent!(layout_parent_style),
|
||||
font_metrics_provider,
|
||||
CascadeMode::Visited { writing_mode },
|
||||
quirks_mode,
|
||||
// The rule cache doesn't care about caching :visited
|
||||
// styles, we cache the unvisited style instead. We still do
|
||||
// need to set the caching dependencies properly if present
|
||||
// though, so the cache conditions need to match.
|
||||
/* rule_cache = */ None,
|
||||
&mut *context.rule_cache_conditions.borrow_mut(),
|
||||
element,
|
||||
));
|
||||
}
|
||||
|
||||
let mut _skip_font_family = false;
|
||||
|
||||
|
@ -4035,11 +4196,12 @@ where
|
|||
|
||||
builder.clear_modified_reset();
|
||||
|
||||
StyleAdjuster::new(&mut builder).adjust(
|
||||
layout_parent_style,
|
||||
element,
|
||||
flags,
|
||||
);
|
||||
if matches!(cascade_mode, CascadeMode::Unvisited { .. }) {
|
||||
StyleAdjuster::new(&mut builder).adjust(
|
||||
layout_parent_style.unwrap_or(inherited_style),
|
||||
element,
|
||||
);
|
||||
}
|
||||
|
||||
if builder.modified_reset() || !apply_reset {
|
||||
// If we adjusted any reset structs, we can't cache this ComputedValues.
|
||||
|
@ -4048,8 +4210,7 @@ where
|
|||
// back again. (Aside from being wasted effort, it will be wrong, since
|
||||
// context.rule_cache_conditions won't be set appropriately if we
|
||||
// didn't compute those reset properties.)
|
||||
context.rule_cache_conditions.borrow_mut()
|
||||
.set_uncacheable();
|
||||
context.rule_cache_conditions.borrow_mut().set_uncacheable();
|
||||
}
|
||||
|
||||
builder.build()
|
||||
|
|
|
@ -124,11 +124,6 @@ impl RuleCache {
|
|||
guards: &StylesheetGuards,
|
||||
builder_with_early_props: &StyleBuilder,
|
||||
) -> Option<&ComputedValues> {
|
||||
if builder_with_early_props.is_style_if_visited() {
|
||||
// FIXME(emilio): We can probably do better, does it matter much?
|
||||
return None;
|
||||
}
|
||||
|
||||
// A pseudo-element with property restrictions can result in different
|
||||
// computed values if it's also used for a non-pseudo.
|
||||
if builder_with_early_props
|
||||
|
@ -166,11 +161,6 @@ impl RuleCache {
|
|||
return false;
|
||||
}
|
||||
|
||||
if style.is_style_if_visited() {
|
||||
// FIXME(emilio): We can probably do better, does it matter much?
|
||||
return false;
|
||||
}
|
||||
|
||||
// A pseudo-element with property restrictions can result in different
|
||||
// computed values if it's also used for a non-pseudo.
|
||||
if pseudo.and_then(|p| p.property_restriction()).is_some() {
|
||||
|
|
|
@ -14,7 +14,7 @@ use element_state::{DocumentState, ElementState};
|
|||
use fnv::FnvHashMap;
|
||||
use invalidation::element::document_state::InvalidationMatchingData;
|
||||
use invalidation::element::element_wrapper::ElementSnapshot;
|
||||
use properties::{CascadeFlags, ComputedValues, PropertyFlags};
|
||||
use properties::{ComputedValues, PropertyFlags};
|
||||
use properties::longhands::display::computed_value::T as Display;
|
||||
use selector_parser::{AttrValue as SelectorAttrValue, PseudoElementCascadeType, SelectorParser};
|
||||
use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
|
||||
|
@ -223,19 +223,19 @@ impl PseudoElement {
|
|||
/// properties... Also, I guess it just could do all: inherit on the
|
||||
/// stylesheet, though chances are that'd be kinda slow if we don't cache
|
||||
/// them...
|
||||
pub fn cascade_flags(&self) -> CascadeFlags {
|
||||
pub fn inherits_all(&self) -> bool {
|
||||
match *self {
|
||||
PseudoElement::After |
|
||||
PseudoElement::Before |
|
||||
PseudoElement::Selection |
|
||||
PseudoElement::DetailsContent |
|
||||
PseudoElement::DetailsSummary => CascadeFlags::empty(),
|
||||
PseudoElement::DetailsSummary |
|
||||
// Anonymous table flows shouldn't inherit their parents properties in order
|
||||
// to avoid doubling up styles such as transformations.
|
||||
PseudoElement::ServoAnonymousTableCell |
|
||||
PseudoElement::ServoAnonymousTableRow |
|
||||
PseudoElement::ServoText |
|
||||
PseudoElement::ServoInputText => CascadeFlags::empty(),
|
||||
PseudoElement::ServoInputText => false,
|
||||
|
||||
// For tables, we do want style to inherit, because TableWrapper is
|
||||
// responsible for handling clipping and scrolling, while Table is
|
||||
|
@ -248,7 +248,7 @@ impl PseudoElement {
|
|||
PseudoElement::ServoTableWrapper |
|
||||
PseudoElement::ServoAnonymousBlock |
|
||||
PseudoElement::ServoInlineBlockWrapper |
|
||||
PseudoElement::ServoInlineAbsolute => CascadeFlags::INHERIT_ALL,
|
||||
PseudoElement::ServoInlineAbsolute => true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
use app_units::Au;
|
||||
use dom::TElement;
|
||||
use properties::{self, CascadeFlags, ComputedValues, StyleBuilder};
|
||||
use properties::{self, ComputedValues, StyleBuilder};
|
||||
use properties::computed_value_flags::ComputedValueFlags;
|
||||
use properties::longhands::display::computed_value::T as Display;
|
||||
use properties::longhands::float::computed_value::T as Float;
|
||||
|
@ -681,7 +681,6 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
|||
&mut self,
|
||||
layout_parent_style: &ComputedValues,
|
||||
element: Option<E>,
|
||||
flags: CascadeFlags,
|
||||
) where
|
||||
E: TElement,
|
||||
{
|
||||
|
@ -705,15 +704,6 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
|||
// "Should always have an element around for non-pseudo styles"
|
||||
// );
|
||||
|
||||
// Don't adjust visited styles, visited-dependent properties aren't
|
||||
// affected by these adjustments and it'd be just wasted work anyway.
|
||||
//
|
||||
// It also doesn't make much sense to adjust them, since we don't
|
||||
// cascade most properties anyway, and they wouldn't be looked up.
|
||||
if flags.contains(CascadeFlags::VISITED_DEPENDENT_ONLY) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.adjust_for_visited(element);
|
||||
#[cfg(feature = "gecko")]
|
||||
{
|
||||
|
|
|
@ -71,9 +71,19 @@ impl DeepCloneWithLock for DocumentRule {
|
|||
}
|
||||
}
|
||||
|
||||
/// A URL matching function for a `@document` rule's condition.
|
||||
/// The kind of media document that the rule will match.
|
||||
#[derive(Clone, Copy, Debug, Parse, PartialEq, ToCss)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum MediaDocumentKind {
|
||||
All,
|
||||
Plugin,
|
||||
Image,
|
||||
Video,
|
||||
}
|
||||
|
||||
/// A matching function for a `@document` rule's condition.
|
||||
#[derive(Clone, Debug, ToCss)]
|
||||
pub enum UrlMatchingFunction {
|
||||
pub enum DocumentMatchingFunction {
|
||||
/// Exact URL matching function. It evaluates to true whenever the
|
||||
/// URL of the document being styled is exactly the URL given.
|
||||
Url(CssUrl),
|
||||
|
@ -97,6 +107,9 @@ pub enum UrlMatchingFunction {
|
|||
/// of the document being styled.
|
||||
#[css(function)]
|
||||
Regexp(String),
|
||||
/// Matching function for a media document.
|
||||
#[css(function)]
|
||||
MediaDocument(MediaDocumentKind)
|
||||
}
|
||||
|
||||
macro_rules! parse_quoted_or_unquoted_string {
|
||||
|
@ -116,60 +129,74 @@ macro_rules! parse_quoted_or_unquoted_string {
|
|||
};
|
||||
}
|
||||
|
||||
impl UrlMatchingFunction {
|
||||
impl DocumentMatchingFunction {
|
||||
/// Parse a URL matching function for a`@document` rule's condition.
|
||||
pub fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
if input
|
||||
.try(|input| input.expect_function_matching("url-prefix"))
|
||||
.is_ok()
|
||||
{
|
||||
return parse_quoted_or_unquoted_string!(input, UrlMatchingFunction::UrlPrefix);
|
||||
if let Ok(url) = input.try(|input| CssUrl::parse(context, input)) {
|
||||
return Ok(DocumentMatchingFunction::Url(url))
|
||||
}
|
||||
|
||||
if input
|
||||
.try(|input| input.expect_function_matching("domain"))
|
||||
.is_ok()
|
||||
{
|
||||
return parse_quoted_or_unquoted_string!(input, UrlMatchingFunction::Domain);
|
||||
}
|
||||
|
||||
if input
|
||||
.try(|input| input.expect_function_matching("regexp"))
|
||||
.is_ok()
|
||||
{
|
||||
return input.parse_nested_block(|input| {
|
||||
Ok(UrlMatchingFunction::Regexp(
|
||||
input.expect_string()?.as_ref().to_owned(),
|
||||
let location = input.current_source_location();
|
||||
let function = input.expect_function()?.clone();
|
||||
match_ignore_ascii_case! { &function,
|
||||
"url-prefix" => {
|
||||
parse_quoted_or_unquoted_string!(input, DocumentMatchingFunction::UrlPrefix)
|
||||
}
|
||||
"domain" => {
|
||||
parse_quoted_or_unquoted_string!(input, DocumentMatchingFunction::Domain)
|
||||
}
|
||||
"regexp" => {
|
||||
input.parse_nested_block(|input| {
|
||||
Ok(DocumentMatchingFunction::Regexp(
|
||||
input.expect_string()?.as_ref().to_owned(),
|
||||
))
|
||||
})
|
||||
}
|
||||
"media-document" => {
|
||||
input.parse_nested_block(|input| {
|
||||
let kind = MediaDocumentKind::parse(input)?;
|
||||
Ok(DocumentMatchingFunction::MediaDocument(kind))
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
Err(location.new_custom_error(
|
||||
StyleParseErrorKind::UnexpectedFunction(function.clone())
|
||||
))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let url = CssUrl::parse(context, input)?;
|
||||
Ok(UrlMatchingFunction::Url(url))
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
/// Evaluate a URL matching function.
|
||||
pub fn evaluate(&self, device: &Device) -> bool {
|
||||
use gecko_bindings::bindings::Gecko_DocumentRule_UseForPresentation;
|
||||
use gecko_bindings::structs::URLMatchingFunction as GeckoUrlMatchingFunction;
|
||||
use gecko_bindings::structs::DocumentMatchingFunction as GeckoDocumentMatchingFunction;
|
||||
use nsstring::nsCStr;
|
||||
|
||||
let func = match *self {
|
||||
UrlMatchingFunction::Url(_) => GeckoUrlMatchingFunction::eURL,
|
||||
UrlMatchingFunction::UrlPrefix(_) => GeckoUrlMatchingFunction::eURLPrefix,
|
||||
UrlMatchingFunction::Domain(_) => GeckoUrlMatchingFunction::eDomain,
|
||||
UrlMatchingFunction::Regexp(_) => GeckoUrlMatchingFunction::eRegExp,
|
||||
DocumentMatchingFunction::Url(_) => GeckoDocumentMatchingFunction::URL,
|
||||
DocumentMatchingFunction::UrlPrefix(_) => GeckoDocumentMatchingFunction::URLPrefix,
|
||||
DocumentMatchingFunction::Domain(_) => GeckoDocumentMatchingFunction::Domain,
|
||||
DocumentMatchingFunction::Regexp(_) => GeckoDocumentMatchingFunction::RegExp,
|
||||
DocumentMatchingFunction::MediaDocument(_) => GeckoDocumentMatchingFunction::MediaDocument,
|
||||
};
|
||||
|
||||
let pattern = nsCStr::from(match *self {
|
||||
UrlMatchingFunction::Url(ref url) => url.as_str(),
|
||||
UrlMatchingFunction::UrlPrefix(ref pat) |
|
||||
UrlMatchingFunction::Domain(ref pat) |
|
||||
UrlMatchingFunction::Regexp(ref pat) => pat,
|
||||
DocumentMatchingFunction::Url(ref url) => url.as_str(),
|
||||
DocumentMatchingFunction::UrlPrefix(ref pat) |
|
||||
DocumentMatchingFunction::Domain(ref pat) |
|
||||
DocumentMatchingFunction::Regexp(ref pat) => pat,
|
||||
DocumentMatchingFunction::MediaDocument(kind) => {
|
||||
match kind {
|
||||
MediaDocumentKind::All => "all",
|
||||
MediaDocumentKind::Image => "image",
|
||||
MediaDocumentKind::Plugin => "plugin",
|
||||
MediaDocumentKind::Video => "video",
|
||||
}
|
||||
}
|
||||
});
|
||||
unsafe { Gecko_DocumentRule_UseForPresentation(device.pres_context(), &*pattern, func) }
|
||||
}
|
||||
|
@ -190,7 +217,7 @@ impl UrlMatchingFunction {
|
|||
/// one of those functions evaluates to true.
|
||||
#[css(comma)]
|
||||
#[derive(Clone, Debug, ToCss)]
|
||||
pub struct DocumentCondition(#[css(iterable)] Vec<UrlMatchingFunction>);
|
||||
pub struct DocumentCondition(#[css(iterable)] Vec<DocumentMatchingFunction>);
|
||||
|
||||
impl DocumentCondition {
|
||||
/// Parse a document condition.
|
||||
|
@ -199,7 +226,7 @@ impl DocumentCondition {
|
|||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
let conditions =
|
||||
input.parse_comma_separated(|input| UrlMatchingFunction::parse(context, input))?;
|
||||
input.parse_comma_separated(|input| DocumentMatchingFunction::parse(context, input))?;
|
||||
|
||||
let condition = DocumentCondition(conditions);
|
||||
if !condition.allowed_in(context) {
|
||||
|
@ -252,7 +279,7 @@ impl DocumentCondition {
|
|||
|
||||
// NOTE(emilio): This technically allows url-prefix("") too, but...
|
||||
match self.0[0] {
|
||||
UrlMatchingFunction::UrlPrefix(ref prefix) => prefix.is_empty(),
|
||||
DocumentMatchingFunction::UrlPrefix(ref prefix) => prefix.is_empty(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use cssparser::{AtRuleParser, CowRcStr, Parser, ParserInput, QualifiedRuleParser
|
|||
use cssparser::{parse_one_rule, DeclarationListParser, DeclarationParser, SourceLocation, Token};
|
||||
use error_reporting::ContextualParseError;
|
||||
use parser::ParserContext;
|
||||
use properties::{DeclarationPushMode, Importance, PropertyDeclaration};
|
||||
use properties::{Importance, PropertyDeclaration};
|
||||
use properties::{LonghandId, PropertyDeclarationBlock, PropertyId};
|
||||
use properties::{PropertyDeclarationId, SourcePropertyDeclaration};
|
||||
use properties::LonghandIdSet;
|
||||
|
@ -554,7 +554,6 @@ impl<'a, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a> {
|
|||
block.extend(
|
||||
iter.parser.declarations.drain(),
|
||||
Importance::Normal,
|
||||
DeclarationPushMode::Parsing,
|
||||
);
|
||||
},
|
||||
Err((error, slice)) => {
|
||||
|
|
|
@ -20,7 +20,7 @@ use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};
|
|||
#[cfg(feature = "gecko")]
|
||||
use malloc_size_of::MallocUnconditionalShallowSizeOf;
|
||||
use media_queries::Device;
|
||||
use properties::{self, CascadeFlags, ComputedValues};
|
||||
use properties::{self, CascadeMode, ComputedValues};
|
||||
use properties::{AnimationRules, PropertyDeclarationBlock};
|
||||
use rule_cache::{RuleCache, RuleCacheConditions};
|
||||
use rule_tree::{CascadeLevel, RuleTree, ShadowCascadeOrder, StrongRuleNode, StyleSource};
|
||||
|
@ -845,55 +845,18 @@ impl Stylist {
|
|||
{
|
||||
debug_assert!(pseudo.is_some() || element.is_some(), "Huh?");
|
||||
|
||||
let cascade_flags = pseudo.map_or(CascadeFlags::empty(), |p| p.cascade_flags());
|
||||
|
||||
// We need to compute visited values if we have visited rules or if our
|
||||
// parent has visited values.
|
||||
let mut visited_values = None;
|
||||
if inputs.visited_rules.is_some() || parent_style.and_then(|s| s.visited_style()).is_some()
|
||||
{
|
||||
// At this point inputs may have visited rules, or rules.
|
||||
let rule_node = match inputs.visited_rules.as_ref() {
|
||||
Some(rules) => rules,
|
||||
None => inputs.rules.as_ref().unwrap_or(self.rule_tree.root()),
|
||||
};
|
||||
|
||||
let inherited_style;
|
||||
let inherited_style_ignoring_first_line;
|
||||
let layout_parent_style_for_visited;
|
||||
if pseudo.is_none() && element.unwrap().is_link() {
|
||||
// We just want to use our parent style as our parent.
|
||||
inherited_style = parent_style;
|
||||
inherited_style_ignoring_first_line = parent_style_ignoring_first_line;
|
||||
layout_parent_style_for_visited = layout_parent_style;
|
||||
} else {
|
||||
// We want to use the visited bits (if any) from our parent
|
||||
// style as our parent.
|
||||
inherited_style = parent_style
|
||||
.map(|parent_style| parent_style.visited_style().unwrap_or(parent_style));
|
||||
inherited_style_ignoring_first_line = parent_style_ignoring_first_line
|
||||
.map(|parent_style| parent_style.visited_style().unwrap_or(parent_style));
|
||||
layout_parent_style_for_visited = layout_parent_style
|
||||
.map(|parent_style| parent_style.visited_style().unwrap_or(parent_style));
|
||||
let visited_rules = match inputs.visited_rules.as_ref() {
|
||||
Some(rules) => Some(rules),
|
||||
None => {
|
||||
if parent_style.and_then(|s| s.visited_style()).is_some() {
|
||||
Some(inputs.rules.as_ref().unwrap_or(self.rule_tree.root()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
visited_values = Some(properties::cascade::<E>(
|
||||
&self.device,
|
||||
pseudo,
|
||||
rule_node,
|
||||
guards,
|
||||
inherited_style,
|
||||
inherited_style_ignoring_first_line,
|
||||
layout_parent_style_for_visited,
|
||||
None,
|
||||
font_metrics,
|
||||
cascade_flags | CascadeFlags::VISITED_DEPENDENT_ONLY,
|
||||
self.quirks_mode,
|
||||
rule_cache,
|
||||
rule_cache_conditions,
|
||||
element,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// Read the comment on `precomputed_values_for_pseudo` to see why it's
|
||||
// difficult to assert that display: contents nodes never arrive here
|
||||
|
@ -909,9 +872,8 @@ impl Stylist {
|
|||
parent_style,
|
||||
parent_style_ignoring_first_line,
|
||||
layout_parent_style,
|
||||
visited_values,
|
||||
visited_rules,
|
||||
font_metrics,
|
||||
cascade_flags,
|
||||
self.quirks_mode,
|
||||
rule_cache,
|
||||
rule_cache_conditions,
|
||||
|
@ -1299,8 +1261,9 @@ impl Stylist {
|
|||
|
||||
if let Some(containing_shadow) = rule_hash_target.containing_shadow() {
|
||||
let cascade_data = containing_shadow.style_data();
|
||||
let host = containing_shadow.host();
|
||||
if let Some(map) = cascade_data.normal_rules(pseudo_element) {
|
||||
context.with_shadow_host(Some(containing_shadow.host()), |context| {
|
||||
context.with_shadow_host(Some(host), |context| {
|
||||
map.get_all_matching_rules(
|
||||
element,
|
||||
rule_hash_target,
|
||||
|
@ -1314,7 +1277,26 @@ impl Stylist {
|
|||
shadow_cascade_order += 1;
|
||||
}
|
||||
|
||||
match_document_author_rules = false;
|
||||
// NOTE(emilio): Hack so <svg:use> matches document rules as
|
||||
// expected.
|
||||
//
|
||||
// This is not a problem for invalidation and that kind of stuff
|
||||
// because they still don't match rules based on elements
|
||||
// outside of the shadow tree, and because the <svg:use> subtree
|
||||
// is immutable and recreated each time the source tree changes.
|
||||
//
|
||||
// See: https://github.com/w3c/svgwg/issues/504
|
||||
//
|
||||
// Note that we always resolve URLs against the document, so we
|
||||
// can't get into a nested shadow situation here.
|
||||
//
|
||||
// See: https://github.com/w3c/svgwg/issues/505
|
||||
//
|
||||
let host_is_svg_use =
|
||||
host.is_svg_element() &&
|
||||
host.local_name() == &*local_name!("use");
|
||||
|
||||
match_document_author_rules = host_is_svg_use;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1581,9 +1563,8 @@ impl Stylist {
|
|||
Some(parent_style),
|
||||
Some(parent_style),
|
||||
Some(parent_style),
|
||||
None,
|
||||
&metrics,
|
||||
CascadeFlags::empty(),
|
||||
CascadeMode::Unvisited { visited_rules: None },
|
||||
self.quirks_mode,
|
||||
/* rule_cache = */ None,
|
||||
&mut Default::default(),
|
||||
|
|
|
@ -569,7 +569,7 @@ pub fn get_normalized_vector_and_angle<T: Zero>(
|
|||
// rotation to not be applied, so we use identity matrix (i.e. rotate3d(0, 0, 1, 0)).
|
||||
(0., 0., 1., T::zero())
|
||||
} else {
|
||||
let vector = vector.normalize();
|
||||
let vector = vector.robust_normalize();
|
||||
(vector.x, vector.y, vector.z, angle)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,17 @@ fn moz_display_values_enabled(context: &ParserContext) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
fn moz_box_display_values_enabled(context: &ParserContext) -> bool {
|
||||
use gecko_bindings::structs;
|
||||
use stylesheets::Origin;
|
||||
context.stylesheet_origin == Origin::UserAgent ||
|
||||
context.chrome_rules_enabled() ||
|
||||
unsafe {
|
||||
structs::StaticPrefs_sVarCache_layout_css_xul_box_display_values_content_enabled
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq,
|
||||
SpecifiedValueInfo, ToComputedValue, ToCss)]
|
||||
|
@ -80,8 +91,10 @@ pub enum Display {
|
|||
#[cfg(feature = "gecko")]
|
||||
WebkitInlineBox,
|
||||
#[cfg(feature = "gecko")]
|
||||
#[parse(condition = "moz_box_display_values_enabled")]
|
||||
MozBox,
|
||||
#[cfg(feature = "gecko")]
|
||||
#[parse(condition = "moz_box_display_values_enabled")]
|
||||
MozInlineBox,
|
||||
#[cfg(feature = "gecko")]
|
||||
#[parse(condition = "moz_display_values_enabled")]
|
||||
|
|
|
@ -53,6 +53,8 @@ files = [
|
|||
"./components/net/tests/parsable_mime/text",
|
||||
# Mako does not lend itself easily to splitting long lines
|
||||
"./components/style/properties/helpers/animated_properties.mako.rs",
|
||||
# Long regexes are long.
|
||||
"./components/style/gecko/regen_atoms.py",
|
||||
# Helper macro where actually a pseudo-element per line makes sense.
|
||||
"./components/style/gecko/non_ts_pseudo_class_list.rs",
|
||||
"./resources/hsts_preload.json",
|
||||
|
|
|
@ -17,7 +17,7 @@ use std::sync::atomic::AtomicBool;
|
|||
use style::context::QuirksMode;
|
||||
use style::error_reporting::{ParseErrorReporter, ContextualParseError};
|
||||
use style::media_queries::MediaList;
|
||||
use style::properties::{CSSWideKeyword, CustomDeclaration, DeclarationPushMode};
|
||||
use style::properties::{CSSWideKeyword, CustomDeclaration};
|
||||
use style::properties::{DeclaredValueOwned, Importance};
|
||||
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock};
|
||||
use style::properties::longhands::{self, animation_timing_function};
|
||||
|
@ -34,7 +34,7 @@ pub fn block_from<I>(iterable: I) -> PropertyDeclarationBlock
|
|||
where I: IntoIterator<Item=(PropertyDeclaration, Importance)> {
|
||||
let mut block = PropertyDeclarationBlock::new();
|
||||
for (d, i) in iterable {
|
||||
block.push(d, i, DeclarationPushMode::Append);
|
||||
block.push(d, i);
|
||||
}
|
||||
block
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue