aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGlenn Watson <gw@intuitionlibrary.com>2014-11-04 13:25:07 -0800
committerGlenn Watson <gw@intuitionlibrary.com>2014-11-04 13:25:21 -0800
commit11cf538ff46914f4db50aad908e5246335af9ba1 (patch)
tree8139fd410880fc3b8350f332b612aa91151eb36b
parente483a189a3c24d0fe475cf2a8dedb11821f7ee21 (diff)
downloadservo-11cf538ff46914f4db50aad908e5246335af9ba1.tar.gz
servo-11cf538ff46914f4db50aad908e5246335af9ba1.zip
Make media queries work with resize and page zoom.
-rw-r--r--components/layout/layout_task.rs38
-rw-r--r--components/script/dom/htmlstyleelement.rs4
-rw-r--r--components/style/media_queries.rs6
-rw-r--r--components/style/selector_matching.rs156
-rw-r--r--components/style/stylesheets.rs18
5 files changed, 135 insertions, 87 deletions
diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs
index 9edc1e3f1a5..1ed51197db2 100644
--- a/components/layout/layout_task.rs
+++ b/components/layout/layout_task.rs
@@ -25,6 +25,7 @@ use encoding::all::UTF_8;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
+use geom::scale_factor::ScaleFactor;
use gfx::display_list::{ContentStackingLevel, DisplayItem, DisplayList};
use gfx::display_list::{OpaqueNode};
use gfx::render_task::{RenderInitMsg, RenderChan, RenderLayer};
@@ -93,10 +94,6 @@ pub struct LayoutTaskData {
/// This can be used to easily check for invalid stale data.
pub generation: uint,
- /// True if a style sheet was added since the last reflow. Currently, this causes all nodes to
- /// be dirtied at the next reflow.
- pub stylesheet_dirty: bool,
-
/// A queued response for the union of the content boxes of a node.
pub content_box_response: Rect<Au>,
@@ -147,10 +144,6 @@ pub struct LayoutTask {
///
/// All the other elements of this struct are read-only.
pub rw_data: Arc<Mutex<LayoutTaskData>>,
-
- /// The media queries device state.
- /// TODO: Handle updating this when window size changes etc.
- pub device: Device,
}
struct LayoutImageResponder {
@@ -259,7 +252,7 @@ impl LayoutTask {
let local_image_cache =
Arc::new(Mutex::new(LocalImageCache::new(image_cache_task.clone())));
let screen_size = Size2D(Au(0), Au(0));
- let device = Device::new(Screen, opts::get().initial_window_size.as_f32());
+ let device = Device::new(Screen, opts::get().initial_window_size.as_f32() * ScaleFactor(1.0));
let parallel_traversal = if opts::get().layout_threads != 1 {
Some(WorkQueue::new("LayoutWorker", task_state::Layout,
opts::get().layout_threads, ptr::null()))
@@ -280,17 +273,15 @@ impl LayoutTask {
image_cache_task: image_cache_task.clone(),
font_cache_task: font_cache_task,
first_reflow: Cell::new(true),
- device: device,
rw_data: Arc::new(Mutex::new(
LayoutTaskData {
local_image_cache: local_image_cache,
screen_size: screen_size,
display_list: None,
- stylist: box Stylist::new(&device),
+ stylist: box Stylist::new(device),
parallel_traversal: parallel_traversal,
dirty: Rect::zero(),
generation: 0,
- stylesheet_dirty: false,
content_box_response: Rect::zero(),
content_boxes_response: Vec::new(),
})),
@@ -491,7 +482,8 @@ impl LayoutTask {
let sheet = Stylesheet::from_bytes_iter(iter,
final_url,
protocol_encoding_label,
- Some(environment_encoding));
+ Some(environment_encoding),
+ AuthorOrigin);
self.handle_add_stylesheet(sheet, possibly_locked_rw_data);
}
@@ -501,12 +493,11 @@ impl LayoutTask {
&mut Option<MutexGuard<'a, LayoutTaskData>>) {
// Find all font-face rules and notify the font cache of them.
// GWTODO: Need to handle unloading web fonts (when we handle unloading stylesheets!)
- iter_font_face_rules(&sheet, &self.device, |family, src| {
+ let mut rw_data = self.lock_rw_data(possibly_locked_rw_data);
+ iter_font_face_rules(&sheet, &rw_data.stylist.device, |family, src| {
self.font_cache_task.add_web_font(family.to_string(), (*src).clone());
});
- let mut rw_data = self.lock_rw_data(possibly_locked_rw_data);
- rw_data.stylist.add_stylesheet(sheet, AuthorOrigin, &self.device);
- rw_data.stylesheet_dirty = true;
+ rw_data.stylist.add_stylesheet(sheet);
LayoutTask::return_rw_data(possibly_locked_rw_data, rw_data);
}
@@ -757,12 +748,17 @@ impl LayoutTask {
&data.url);
// Handle conditions where the entire flow tree is invalid.
- let needs_dirtying = rw_data.stylesheet_dirty;
+ let screen_size_changed = current_screen_size != old_screen_size;
- let mut needs_reflow = current_screen_size != old_screen_size;
+ if screen_size_changed {
+ let device = Device::new(Screen, data.window_size.initial_viewport);
+ rw_data.stylist.set_device(device);
+ }
+
+ let needs_dirtying = rw_data.stylist.update();
// If the entire flow tree is invalid, then it will be reflowed anyhow.
- needs_reflow &= !needs_dirtying;
+ let needs_reflow = screen_size_changed && !needs_dirtying;
unsafe {
if needs_dirtying {
@@ -775,8 +771,6 @@ impl LayoutTask {
|mut flow| LayoutTask::reflow_all_nodes(flow.deref_mut()));
}
- rw_data.stylesheet_dirty = false;
-
let mut layout_root = profile(time::LayoutStyleRecalcCategory,
Some((&data.url,
data.iframe,
diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs
index f4d6a315638..a989699e7e4 100644
--- a/components/script/dom/htmlstyleelement.rs
+++ b/components/script/dom/htmlstyleelement.rs
@@ -15,7 +15,7 @@ use dom::node::{Node, NodeHelpers, ElementNodeTypeId, window_from_node};
use dom::virtualmethods::VirtualMethods;
use layout_interface::{AddStylesheetMsg, LayoutChan};
use servo_util::str::DOMString;
-use style::Stylesheet;
+use style::{AuthorOrigin, Stylesheet};
#[dom_struct]
pub struct HTMLStyleElement {
@@ -55,7 +55,7 @@ impl<'a> StyleElementHelpers for JSRef<'a, HTMLStyleElement> {
let url = win.page().get_url();
let data = node.GetTextContent().expect("Element.textContent must be a string");
- let sheet = Stylesheet::from_str(data.as_slice(), url);
+ let sheet = Stylesheet::from_str(data.as_slice(), url, AuthorOrigin);
let LayoutChan(ref layout_chan) = win.page().layout_chan;
layout_chan.send(AddStylesheetMsg(sheet));
}
diff --git a/components/style/media_queries.rs b/components/style/media_queries.rs
index d5e9dad9f12..afa34e1c559 100644
--- a/components/style/media_queries.rs
+++ b/components/style/media_queries.rs
@@ -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,
diff --git a/components/style/selector_matching.rs b/components/style/selector_matching.rs
index 2a6d65f1be7..74d724fe5a5 100644
--- a/components/style/selector_matching.rs
+++ b/components/style/selector_matching.rs
@@ -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;
-
- // 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,
- },
- });
- }
- }
- };
- );
+ 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;
+
+ 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;
+
+ // 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;
+ }
- iter_stylesheet_style_rules(&stylesheet, device, |style_rule| {
- append!(style_rule, normal);
- append!(style_rule, important);
- rules_source_order += 1;
+ 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");
diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs
index db1f13a8240..a30c5762a1a 100644
--- a/components/style/stylesheets.rs
+++ b/components/style/stylesheets.rs
@@ -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 {