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 std::fmt;
|
||||||
|
|
||||||
use crate::{parse::Stmt, selector::Selector};
|
use crate::{parse::Stmt, selector::Selector};
|
||||||
@ -9,7 +10,7 @@ pub(crate) struct MediaRule {
|
|||||||
pub body: Vec<Stmt>,
|
pub body: Vec<Stmt>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
pub(crate) struct MediaQuery {
|
pub(crate) struct MediaQuery {
|
||||||
/// The modifier, probably either "not" or "only".
|
/// The modifier, probably either "not" or "only".
|
||||||
///
|
///
|
||||||
@ -25,7 +26,6 @@ pub(crate) struct MediaQuery {
|
|||||||
pub features: Vec<String>,
|
pub features: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl MediaQuery {
|
impl MediaQuery {
|
||||||
pub fn is_condition(&self) -> bool {
|
pub fn is_condition(&self) -> bool {
|
||||||
self.modifier.is_none() && self.media_type.is_none()
|
self.modifier.is_none() && self.media_type.is_none()
|
||||||
@ -47,9 +47,159 @@ impl MediaQuery {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code, unused_variables)]
|
fn merge(&self, other: &Self) -> MediaQueryMergeResult {
|
||||||
pub fn merge(other: &Self) -> Self {
|
let this_modifier = self.modifier.as_ref().map(|m| m.to_ascii_lowercase());
|
||||||
todo!()
|
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 "))
|
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