mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +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 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,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.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
|
||||
pub enum DeclarationPushMode {
|
||||
|
@ -440,10 +465,7 @@ 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,
|
||||
}
|
||||
decl.id().as_longhand().map_or(false, |id| !self.longhands.contains(id))
|
||||
}
|
||||
|
||||
/// Returns whether calling extend with `DeclarationPushMode::Update`
|
||||
|
@ -596,6 +618,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]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue