mirror of
https://github.com/servo/servo.git
synced 2025-08-04 05:00:08 +01:00
style: Add new algorithm for setting property to be used in later commit.
Bug: 1473180 Reviewed-by: emilio MozReview-Commit-ID: CdM8hDB6rFj
This commit is contained in:
parent
d245f8b579
commit
b14b2beaad
2 changed files with 219 additions and 11 deletions
|
@ -11,6 +11,7 @@ use cssparser::{DeclarationListParser, parse_important, ParserInput, CowRcStr};
|
||||||
use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter, ParseErrorKind};
|
use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter, ParseErrorKind};
|
||||||
use custom_properties::CustomPropertiesBuilder;
|
use custom_properties::CustomPropertiesBuilder;
|
||||||
use error_reporting::{ParseErrorReporter, ContextualParseError};
|
use error_reporting::{ParseErrorReporter, ContextualParseError};
|
||||||
|
use itertools::Itertools;
|
||||||
use parser::ParserContext;
|
use parser::ParserContext;
|
||||||
use properties::animated_properties::{AnimationValue, AnimationValueMap};
|
use properties::animated_properties::{AnimationValue, AnimationValueMap};
|
||||||
use shared_lock::Locked;
|
use shared_lock::Locked;
|
||||||
|
@ -39,6 +40,30 @@ impl AnimationRules {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
}
|
||||||
|
|
||||||
/// Enum for how a given declaration should be pushed into a declaration block.
|
/// Enum for how a given declaration should be pushed into a declaration block.
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
|
||||||
pub enum DeclarationPushMode {
|
pub enum DeclarationPushMode {
|
||||||
|
@ -440,10 +465,7 @@ impl PropertyDeclarationBlock {
|
||||||
/// and it doesn't exist in the block, and returns false otherwise.
|
/// and it doesn't exist in the block, and returns false otherwise.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_definitely_new(&self, decl: &PropertyDeclaration) -> bool {
|
fn is_definitely_new(&self, decl: &PropertyDeclaration) -> bool {
|
||||||
match decl.id() {
|
decl.id().as_longhand().map_or(false, |id| !self.longhands.contains(id))
|
||||||
PropertyDeclarationId::Longhand(id) => !self.longhands.contains(id),
|
|
||||||
PropertyDeclarationId::Custom(..) => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether calling extend with `DeclarationPushMode::Update`
|
/// Returns whether calling extend with `DeclarationPushMode::Update`
|
||||||
|
@ -596,6 +618,183 @@ impl PropertyDeclarationBlock {
|
||||||
true
|
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
|
/// Returns the first declaration that would be removed by removing
|
||||||
/// `property`.
|
/// `property`.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
|
use arrayvec::{ArrayVec, Drain as ArrayVecDrain};
|
||||||
use dom::TElement;
|
use dom::TElement;
|
||||||
use custom_properties::CustomPropertiesBuilder;
|
use custom_properties::CustomPropertiesBuilder;
|
||||||
use servo_arc::{Arc, UniqueArc};
|
use servo_arc::{Arc, UniqueArc};
|
||||||
|
@ -1605,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
|
/// Servo's representation of a CSS property, that is, either a longhand, a
|
||||||
|
@ -2266,19 +2276,18 @@ impl PropertyDeclaration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_SUB_PROPERTIES_PER_SHORTHAND_EXCEPT_ALL: usize =
|
type SubpropertiesArray<T> =
|
||||||
${max(len(s.sub_properties) for s in data.shorthands_except_all())};
|
[T; ${max(len(s.sub_properties) for s in data.shorthands_except_all())}];
|
||||||
|
|
||||||
type SourcePropertyDeclarationArray =
|
type SubpropertiesVec<T> = ArrayVec<SubpropertiesArray<T>>;
|
||||||
[PropertyDeclaration; MAX_SUB_PROPERTIES_PER_SHORTHAND_EXCEPT_ALL];
|
|
||||||
|
|
||||||
/// A stack-allocated vector of `PropertyDeclaration`
|
/// A stack-allocated vector of `PropertyDeclaration`
|
||||||
/// large enough to parse one CSS `key: value` declaration.
|
/// large enough to parse one CSS `key: value` declaration.
|
||||||
/// (Shorthands expand to multiple `PropertyDeclaration`s.)
|
/// (Shorthands expand to multiple `PropertyDeclaration`s.)
|
||||||
pub struct SourcePropertyDeclaration {
|
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,
|
all_shorthand: AllShorthand,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2318,7 +2327,7 @@ impl SourcePropertyDeclaration {
|
||||||
|
|
||||||
/// Return type of SourcePropertyDeclaration::drain
|
/// Return type of SourcePropertyDeclaration::drain
|
||||||
pub struct SourcePropertyDeclarationDrain<'a> {
|
pub struct SourcePropertyDeclarationDrain<'a> {
|
||||||
declarations: ::arrayvec::Drain<'a, SourcePropertyDeclarationArray>,
|
declarations: ArrayVecDrain<'a, SubpropertiesArray<PropertyDeclaration>>,
|
||||||
all_shorthand: AllShorthand,
|
all_shorthand: AllShorthand,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue