Make media queries work with resize and page zoom.

This commit is contained in:
Glenn Watson 2014-11-04 13:25:07 -08:00
parent e483a189a3
commit 11cf538ff4
5 changed files with 134 additions and 86 deletions

View file

@ -13,7 +13,7 @@ use namespaces::NamespaceMap;
use parsing_utils::{BufferedIter, ParserIter};
use properties::common_types::*;
use properties::longhands;
use servo_util::geometry::ScreenPx;
use servo_util::geometry::ViewportPx;
use url::Url;
pub struct MediaRule {
@ -83,11 +83,11 @@ pub enum MediaType {
pub struct Device {
pub media_type: MediaType,
pub viewport_size: TypedSize2D<ScreenPx, f32>,
pub viewport_size: TypedSize2D<ViewportPx, f32>,
}
impl Device {
pub fn new(media_type: MediaType, viewport_size: TypedSize2D<ScreenPx, f32>) -> Device {
pub fn new(media_type: MediaType, viewport_size: TypedSize2D<ViewportPx, f32>) -> Device {
Device {
media_type: media_type,
viewport_size: viewport_size,

View file

@ -24,7 +24,7 @@ use node::{TElement, TElementAttributes, TNode};
use properties::{PropertyDeclaration, PropertyDeclarationBlock, SpecifiedValue, WidthDeclaration};
use properties::{specified};
use selectors::*;
use stylesheets::{Stylesheet, iter_stylesheet_style_rules};
use stylesheets::{Stylesheet, iter_stylesheet_media_rules, iter_stylesheet_style_rules};
pub enum StylesheetOrigin {
UserAgentOrigin,
@ -264,6 +264,18 @@ impl SelectorMap {
pub static RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE: uint = 4096;
pub struct Stylist {
// List of stylesheets (including all media rules)
stylesheets: Vec<Stylesheet>,
// Device that the stylist is currently evaluating against.
pub device: Device,
// If true, a stylesheet has been added or the device has
// changed, and the stylist needs to be updated.
is_dirty: bool,
// The current selector maps, after evaluating media
// rules against the current device.
element_map: PerPseudoElementSelectorMap,
before_map: PerPseudoElementSelectorMap,
after_map: PerPseudoElementSelectorMap,
@ -272,8 +284,12 @@ pub struct Stylist {
impl Stylist {
#[inline]
pub fn new(device: &Device) -> Stylist {
pub fn new(device: Device) -> Stylist {
let mut stylist = Stylist {
stylesheets: vec!(),
device: device,
is_dirty: true,
element_map: PerPseudoElementSelectorMap::new(),
before_map: PerPseudoElementSelectorMap::new(),
after_map: PerPseudoElementSelectorMap::new(),
@ -288,63 +304,96 @@ impl Stylist {
read_resource_file([filename]).unwrap().as_slice(),
Url::parse(format!("chrome:///{}", filename).as_slice()).unwrap(),
None,
None);
stylist.add_stylesheet(ua_stylesheet, UserAgentOrigin, device);
None,
UserAgentOrigin);
stylist.add_stylesheet(ua_stylesheet);
}
stylist
}
pub fn add_stylesheet(&mut self, stylesheet: Stylesheet, origin: StylesheetOrigin,
device: &Device) {
let (mut element_map, mut before_map, mut after_map) = match origin {
UserAgentOrigin => (
&mut self.element_map.user_agent,
&mut self.before_map.user_agent,
&mut self.after_map.user_agent,
),
AuthorOrigin => (
&mut self.element_map.author,
&mut self.before_map.author,
&mut self.after_map.author,
),
UserOrigin => (
&mut self.element_map.user,
&mut self.before_map.user,
&mut self.after_map.user,
),
};
let mut rules_source_order = self.rules_source_order;
pub fn update(&mut self) -> bool {
if self.is_dirty {
self.element_map = PerPseudoElementSelectorMap::new();
self.before_map = PerPseudoElementSelectorMap::new();
self.after_map = PerPseudoElementSelectorMap::new();
self.rules_source_order = 0;
// Take apart the StyleRule into individual Rules and insert
// them into the SelectorMap of that priority.
macro_rules! append(
($style_rule: ident, $priority: ident) => {
if $style_rule.declarations.$priority.len() > 0 {
for selector in $style_rule.selectors.iter() {
let map = match selector.pseudo_element {
None => &mut element_map,
Some(Before) => &mut before_map,
Some(After) => &mut after_map,
};
map.$priority.insert(Rule {
selector: selector.compound_selectors.clone(),
declarations: DeclarationBlock {
specificity: selector.specificity,
declarations: $style_rule.declarations.$priority.clone(),
source_order: rules_source_order,
},
});
}
}
};
);
for stylesheet in self.stylesheets.iter() {
let (mut element_map, mut before_map, mut after_map) = match stylesheet.origin {
UserAgentOrigin => (
&mut self.element_map.user_agent,
&mut self.before_map.user_agent,
&mut self.after_map.user_agent,
),
AuthorOrigin => (
&mut self.element_map.author,
&mut self.before_map.author,
&mut self.after_map.author,
),
UserOrigin => (
&mut self.element_map.user,
&mut self.before_map.user,
&mut self.after_map.user,
),
};
let mut rules_source_order = self.rules_source_order;
iter_stylesheet_style_rules(&stylesheet, device, |style_rule| {
append!(style_rule, normal);
append!(style_rule, important);
rules_source_order += 1;
// Take apart the StyleRule into individual Rules and insert
// them into the SelectorMap of that priority.
macro_rules! append(
($style_rule: ident, $priority: ident) => {
if $style_rule.declarations.$priority.len() > 0 {
for selector in $style_rule.selectors.iter() {
let map = match selector.pseudo_element {
None => &mut element_map,
Some(Before) => &mut before_map,
Some(After) => &mut after_map,
};
map.$priority.insert(Rule {
selector: selector.compound_selectors.clone(),
declarations: DeclarationBlock {
specificity: selector.specificity,
declarations: $style_rule.declarations.$priority.clone(),
source_order: rules_source_order,
},
});
}
}
};
);
iter_stylesheet_style_rules(stylesheet, &self.device, |style_rule| {
append!(style_rule, normal);
append!(style_rule, important);
rules_source_order += 1;
});
self.rules_source_order = rules_source_order;
}
self.is_dirty = false;
return true;
}
false
}
pub fn set_device(&mut self, device: Device) {
let is_dirty = self.stylesheets.iter().any(|stylesheet| {
let mut stylesheet_dirty = false;
iter_stylesheet_media_rules(stylesheet, |rule| {
stylesheet_dirty |= rule.media_queries.evaluate(&self.device) !=
rule.media_queries.evaluate(&device);
});
stylesheet_dirty
});
self.rules_source_order = rules_source_order;
self.device = device;
self.is_dirty |= is_dirty;
}
pub fn add_stylesheet(&mut self, stylesheet: Stylesheet) {
self.stylesheets.push(stylesheet);
self.is_dirty = true;
}
/// Returns the applicable CSS declarations for the given element. This corresponds to
@ -364,6 +413,7 @@ impl Stylist {
where E: TElement<'a> + TElementAttributes,
N: TNode<'a,E>,
V: VecLike<DeclarationBlock> {
assert!(!self.is_dirty);
assert!(element.is_element());
assert!(style_attribute.is_none() || pseudo_element.is_none(),
"Style attributes do not apply to pseudo-elements");

View file

@ -17,12 +17,14 @@ use namespaces::{NamespaceMap, parse_namespace_rule};
use media_queries::{Device, MediaRule, parse_media_rule};
use media_queries;
use font_face::{FontFaceRule, Source, parse_font_face_rule, iter_font_face_rules_inner};
use selector_matching::StylesheetOrigin;
pub struct Stylesheet {
/// List of rules in the order they were found (important for
/// cascading order)
rules: Vec<CSSRule>,
pub origin: StylesheetOrigin,
}
@ -42,25 +44,25 @@ pub struct StyleRule {
impl Stylesheet {
pub fn from_bytes_iter<I: Iterator<Vec<u8>>>(
mut input: I, base_url: Url, protocol_encoding_label: Option<&str>,
environment_encoding: Option<EncodingRef>) -> Stylesheet {
environment_encoding: Option<EncodingRef>, origin: StylesheetOrigin) -> Stylesheet {
let mut bytes = vec!();
// TODO: incremental decoding and tokinization/parsing
for chunk in input {
bytes.push_all(chunk.as_slice())
}
Stylesheet::from_bytes(bytes.as_slice(), base_url, protocol_encoding_label, environment_encoding)
Stylesheet::from_bytes(bytes.as_slice(), base_url, protocol_encoding_label, environment_encoding, origin)
}
pub fn from_bytes(
bytes: &[u8], base_url: Url, protocol_encoding_label: Option<&str>,
environment_encoding: Option<EncodingRef>) -> Stylesheet {
environment_encoding: Option<EncodingRef>, origin: StylesheetOrigin) -> Stylesheet {
// TODO: bytes.as_slice could be bytes.container_as_bytes()
let (string, _) = decode_stylesheet_bytes(
bytes.as_slice(), protocol_encoding_label, environment_encoding);
Stylesheet::from_str(string.as_slice(), base_url)
Stylesheet::from_str(string.as_slice(), base_url, origin)
}
pub fn from_str(css: &str, base_url: Url) -> Stylesheet {
pub fn from_str(css: &str, base_url: Url, origin: StylesheetOrigin) -> Stylesheet {
static STATE_CHARSET: uint = 1;
static STATE_IMPORTS: uint = 2;
static STATE_NAMESPACES: uint = 3;
@ -119,7 +121,10 @@ impl Stylesheet {
}
state = next_state;
}
Stylesheet{ rules: rules }
Stylesheet {
rules: rules,
origin: origin,
}
}
}
@ -165,7 +170,6 @@ pub fn iter_style_rules<'a>(rules: &[CSSRule], device: &media_queries::Device,
}
}
#[cfg(test)]
pub fn iter_stylesheet_media_rules(stylesheet: &Stylesheet, callback: |&MediaRule|) {
for rule in stylesheet.rules.iter() {
match *rule {