prepare for media query merging
This commit is contained in:
parent
be3edd8a62
commit
9ed2a8a984
@ -1,3 +1,4 @@
|
||||
#![allow(dead_code)]
|
||||
use std::fmt;
|
||||
|
||||
use crate::{parse::Stmt, selector::Selector};
|
||||
@ -9,7 +10,7 @@ pub(crate) struct MediaRule {
|
||||
pub body: Vec<Stmt>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub(crate) struct MediaQuery {
|
||||
/// The modifier, probably either "not" or "only".
|
||||
///
|
||||
@ -25,7 +26,6 @@ pub(crate) struct MediaQuery {
|
||||
pub features: Vec<String>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl MediaQuery {
|
||||
pub fn is_condition(&self) -> bool {
|
||||
self.modifier.is_none() && self.media_type.is_none()
|
||||
@ -47,9 +47,159 @@ impl MediaQuery {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code, unused_variables)]
|
||||
pub fn merge(other: &Self) -> Self {
|
||||
todo!()
|
||||
fn merge(&self, other: &Self) -> MediaQueryMergeResult {
|
||||
let this_modifier = self.modifier.as_ref().map(|m| m.to_ascii_lowercase());
|
||||
let this_type = self.media_type.as_ref().map(|m| m.to_ascii_lowercase());
|
||||
let other_modifier = other.modifier.as_ref().map(|m| m.to_ascii_lowercase());
|
||||
let other_type = other.media_type.as_ref().map(|m| m.to_ascii_lowercase());
|
||||
|
||||
if this_type.is_none() && other_type.is_none() {
|
||||
return MediaQueryMergeResult::Success(Self::condition(
|
||||
self.features
|
||||
.iter()
|
||||
.chain(&other.features)
|
||||
.cloned()
|
||||
.collect(),
|
||||
));
|
||||
}
|
||||
|
||||
let modifier;
|
||||
let media_type;
|
||||
let features;
|
||||
|
||||
if (this_modifier.as_deref() == Some("not")) != (other_modifier.as_deref() == Some("not")) {
|
||||
if this_modifier == other_modifier {
|
||||
let negative_features = if this_modifier.as_deref() == Some("not") {
|
||||
&self.features
|
||||
} else {
|
||||
&other.features
|
||||
};
|
||||
|
||||
let positive_features = if this_modifier.as_deref() == Some("not") {
|
||||
&other.features
|
||||
} else {
|
||||
&self.features
|
||||
};
|
||||
|
||||
// If the negative features are a subset of the positive features, the
|
||||
// query is empty. For example, `not screen and (color)` has no
|
||||
// intersection with `screen and (color) and (grid)`.
|
||||
//
|
||||
// However, `not screen and (color)` *does* intersect with `screen and
|
||||
// (grid)`, because it means `not (screen and (color))` and so it allows
|
||||
// a screen with no color but with a grid.
|
||||
|
||||
if negative_features
|
||||
.iter()
|
||||
.all(|feat| positive_features.contains(&feat))
|
||||
{
|
||||
return MediaQueryMergeResult::Empty;
|
||||
} else {
|
||||
return MediaQueryMergeResult::Unrepresentable;
|
||||
}
|
||||
} else if self.matches_all_types() || other.matches_all_types() {
|
||||
return MediaQueryMergeResult::Unrepresentable;
|
||||
}
|
||||
|
||||
if this_modifier.as_deref() == Some("not") {
|
||||
modifier = &other_modifier;
|
||||
media_type = &other_type;
|
||||
features = other.features.clone();
|
||||
} else {
|
||||
modifier = &this_modifier;
|
||||
media_type = &this_type;
|
||||
features = self.features.clone();
|
||||
}
|
||||
} else if this_modifier.as_deref() == Some("not") {
|
||||
debug_assert_eq!(other_modifier.as_deref(), Some("not"));
|
||||
|
||||
// CSS has no way of representing "neither screen nor print".
|
||||
if this_type != other_type {
|
||||
return MediaQueryMergeResult::Unrepresentable;
|
||||
}
|
||||
|
||||
let more_features = if self.features.len() > other.features.len() {
|
||||
&self.features
|
||||
} else {
|
||||
&other.features
|
||||
};
|
||||
|
||||
let fewer_features = if self.features.len() > other.features.len() {
|
||||
&other.features
|
||||
} else {
|
||||
&self.features
|
||||
};
|
||||
|
||||
// If one set of features is a superset of the other, use those features
|
||||
// because they're strictly narrower.
|
||||
if fewer_features
|
||||
.iter()
|
||||
.all(|feat| more_features.contains(feat))
|
||||
{
|
||||
modifier = &this_modifier; // "not"
|
||||
media_type = &this_type;
|
||||
features = more_features.clone();
|
||||
} else {
|
||||
// Otherwise, there's no way to represent the intersection.
|
||||
return MediaQueryMergeResult::Unrepresentable;
|
||||
}
|
||||
} else if self.matches_all_types() {
|
||||
modifier = &other_modifier;
|
||||
|
||||
// Omit the type if either input query did, since that indicates that they
|
||||
// aren't targeting a browser that requires "all and".
|
||||
media_type = if other.matches_all_types() && this_type.is_none() {
|
||||
&None
|
||||
} else {
|
||||
&other_type
|
||||
};
|
||||
|
||||
features = self
|
||||
.features
|
||||
.iter()
|
||||
.chain(&other.features)
|
||||
.cloned()
|
||||
.collect();
|
||||
} else if other.matches_all_types() {
|
||||
modifier = &this_modifier;
|
||||
media_type = &this_type;
|
||||
features = self
|
||||
.features
|
||||
.iter()
|
||||
.chain(&other.features)
|
||||
.cloned()
|
||||
.collect();
|
||||
} else if this_type != other_type {
|
||||
return MediaQueryMergeResult::Empty;
|
||||
} else {
|
||||
if this_modifier.is_some() {
|
||||
modifier = &this_modifier
|
||||
} else {
|
||||
modifier = &other_modifier;
|
||||
}
|
||||
|
||||
media_type = &this_type;
|
||||
features = self
|
||||
.features
|
||||
.iter()
|
||||
.chain(&other.features)
|
||||
.cloned()
|
||||
.collect();
|
||||
}
|
||||
|
||||
MediaQueryMergeResult::Success(MediaQuery {
|
||||
media_type: if media_type == &this_type {
|
||||
self.media_type.clone()
|
||||
} else {
|
||||
other.media_type.clone()
|
||||
},
|
||||
modifier: if modifier == &this_modifier {
|
||||
self.modifier.clone()
|
||||
} else {
|
||||
other.modifier.clone()
|
||||
},
|
||||
features,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,3 +217,10 @@ impl fmt::Display for MediaQuery {
|
||||
f.write_str(&self.features.join(" and "))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
enum MediaQueryMergeResult {
|
||||
Empty,
|
||||
Unrepresentable,
|
||||
Success(MediaQuery),
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user