aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock29
-rw-r--r--components/canvas/Cargo.toml2
-rw-r--r--components/canvas_traits/Cargo.toml2
-rw-r--r--components/layout/display_list/builder.rs11
-rw-r--r--components/malloc_size_of/Cargo.toml2
-rw-r--r--components/script/Cargo.toml2
-rw-r--r--components/script/dom/htmlimageelement.rs70
-rw-r--r--components/script/dom/webidls/CSSStyleDeclaration.webidl8
-rw-r--r--components/script/test.rs4
-rw-r--r--components/script_layout_interface/Cargo.toml2
-rw-r--r--components/selectors/Cargo.toml2
-rw-r--r--components/selectors/builder.rs49
-rw-r--r--components/selectors/matching.rs2
-rw-r--r--components/selectors/parser.rs129
-rw-r--r--components/style/Cargo.toml2
-rw-r--r--components/style/build_gecko.rs21
-rw-r--r--components/style/element_state.rs3
-rw-r--r--components/style/gecko/media_queries.rs367
-rw-r--r--components/style/gecko/non_ts_pseudo_class_list.rs1
-rw-r--r--components/style/gecko/selector_parser.rs3
-rw-r--r--components/style/gecko/wrapper.rs17
-rw-r--r--components/style/gecko_bindings/sugar/ns_css_shadow_item.rs23
-rw-r--r--components/style/media_queries/media_condition.rs192
-rw-r--r--components/style/media_queries/media_list.rs8
-rw-r--r--components/style/media_queries/media_query.rs79
-rw-r--r--components/style/media_queries/mod.rs6
-rw-r--r--components/style/properties/gecko.mako.rs53
-rw-r--r--components/style/properties/helpers.mako.rs2
-rw-r--r--components/style/properties/longhands/position.mako.rs7
-rw-r--r--components/style/properties/properties.mako.rs8
-rw-r--r--components/style/properties/shorthands/background.mako.rs1
-rw-r--r--components/style/properties/shorthands/box.mako.rs1
-rw-r--r--components/style/properties/shorthands/font.mako.rs1
-rw-r--r--components/style/properties/shorthands/svg.mako.rs2
-rw-r--r--components/style/properties/shorthands/text.mako.rs1
-rw-r--r--components/style/selector_map.rs51
-rw-r--r--components/style/servo/media_queries.rs53
-rw-r--r--components/style/stylesheets/font_feature_values_rule.rs1
-rw-r--r--components/style/stylesheets/keyframes_rule.rs38
-rw-r--r--components/style/stylesheets/rule_parser.rs120
-rw-r--r--components/style/values/animated/color.rs5
-rw-r--r--components/style/values/animated/effects.rs6
-rw-r--r--components/style/values/computed/effects.rs6
-rw-r--r--components/style/values/generics/counters.rs1
-rw-r--r--components/style/values/specified/effects.rs29
-rw-r--r--components/style/values/specified/source_size_list.rs8
-rw-r--r--components/style_traits/Cargo.toml2
-rw-r--r--components/style_traits/lib.rs4
-rw-r--r--tests/unit/script/Cargo.toml1
-rw-r--r--tests/unit/script/htmlimageelement.rs97
-rw-r--r--tests/unit/script/lib.rs1
-rw-r--r--tests/unit/style/Cargo.toml2
-rw-r--r--tests/unit/style/lib.rs14
-rw-r--r--tests/unit/style/media_queries.rs398
54 files changed, 872 insertions, 1077 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c322fc2329f..81d74f5088f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -271,7 +271,7 @@ dependencies = [
"azure 0.29.0 (git+https://github.com/servo/rust-azure)",
"canvas_traits 0.0.1",
"compositing 0.0.1",
- "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"gleam 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -289,7 +289,7 @@ dependencies = [
name = "canvas_traits"
version = "0.0.1"
dependencies = [
- "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
"gleam 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ipc-channel 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -561,7 +561,7 @@ dependencies = [
[[package]]
name = "cssparser"
-version = "0.23.10"
+version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cssparser-macros 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -569,12 +569,12 @@ dependencies = [
"itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"procedural-masquerade 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1610,7 +1610,7 @@ name = "malloc_size_of"
version = "0.0.1"
dependencies = [
"app_units 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
"hashglobe 0.1.0",
"hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2470,7 +2470,7 @@ dependencies = [
"chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"cookie 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
"deny_public_fields 0.0.1",
"devtools_traits 0.0.1",
"dom_struct 0.0.1",
@@ -2547,7 +2547,7 @@ dependencies = [
"app_units 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"canvas_traits 0.0.1",
- "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx_traits 0.0.1",
"html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2590,7 +2590,6 @@ dependencies = [
"msg 0.0.1",
"script 0.0.1",
"servo_url 0.0.1",
- "style 0.0.1",
]
[[package]]
@@ -2629,7 +2628,7 @@ name = "selectors"
version = "0.19.0"
dependencies = [
"bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2967,7 +2966,7 @@ dependencies = [
"bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding_rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fallible 0.0.1",
@@ -3028,7 +3027,7 @@ version = "0.0.1"
dependencies = [
"app_units 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
"html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3050,7 +3049,7 @@ version = "0.0.1"
dependencies = [
"app_units 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
"malloc_size_of 0.0.1",
"malloc_size_of_derive 0.0.1",
@@ -3760,7 +3759,7 @@ dependencies = [
"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
-"checksum cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1c74d99b0f489cc546336b911452562ebfd4aec034b0c526cb77a3a02d3790"
+"checksum cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)" = "495beddc39b1987b8e9f029354eccbd5ef88eb5f1cd24badb764dce338acf2e0"
"checksum cssparser-macros 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f3a5383ae18dbfdeb569ed62019f5bddb2a95cd2d3833313c475a0d014777805"
"checksum darling 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a78af487e4eb8f4421a1770687b328af6bb4494ca93435210678c6eea875c11"
"checksum darling_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b315f49c7b6db3708bca6e6913c194581a44ec619b7a39e131d4dd63733a3698"
diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml
index 2c2a928e233..e0a6db7fb2c 100644
--- a/components/canvas/Cargo.toml
+++ b/components/canvas/Cargo.toml
@@ -13,7 +13,7 @@ path = "lib.rs"
azure = {git = "https://github.com/servo/rust-azure"}
canvas_traits = {path = "../canvas_traits"}
compositing = {path = "../compositing"}
-cssparser = "0.23.0"
+cssparser = "0.24"
euclid = "0.17"
fnv = "1.0"
gleam = "0.5"
diff --git a/components/canvas_traits/Cargo.toml b/components/canvas_traits/Cargo.toml
index 226acf76dff..8ec09cadbdd 100644
--- a/components/canvas_traits/Cargo.toml
+++ b/components/canvas_traits/Cargo.toml
@@ -10,7 +10,7 @@ name = "canvas_traits"
path = "lib.rs"
[dependencies]
-cssparser = "0.23.0"
+cssparser = "0.24.0"
euclid = "0.17"
ipc-channel = "0.10"
gleam = "0.5.1"
diff --git a/components/layout/display_list/builder.rs b/components/layout/display_list/builder.rs
index 3887f141f52..9d0de5c3f41 100644
--- a/components/layout/display_list/builder.rs
+++ b/components/layout/display_list/builder.rs
@@ -1219,11 +1219,7 @@ impl FragmentDisplayListBuilding for Fragment {
state.add_display_item(DisplayItem::BoxShadow(Box::new(BoxShadowDisplayItem {
base: base,
box_bounds: absolute_bounds.to_layout(),
- color: box_shadow
- .base
- .color
- .unwrap_or(style.get_color().color)
- .to_layout(),
+ color: style.resolve_color(box_shadow.base.color).to_layout(),
offset: LayoutVector2D::new(
box_shadow.base.horizontal.px(),
box_shadow.base.vertical.px(),
@@ -2038,10 +2034,7 @@ impl FragmentDisplayListBuilding for Fragment {
base: base.clone(),
shadow: webrender_api::Shadow {
offset: LayoutVector2D::new(shadow.horizontal.px(), shadow.vertical.px()),
- color: shadow
- .color
- .unwrap_or(self.style().get_color().color)
- .to_layout(),
+ color: self.style.resolve_color(shadow.color).to_layout(),
blur_radius: shadow.blur.px(),
},
},
diff --git a/components/malloc_size_of/Cargo.toml b/components/malloc_size_of/Cargo.toml
index 92376a6cd61..67718409311 100644
--- a/components/malloc_size_of/Cargo.toml
+++ b/components/malloc_size_of/Cargo.toml
@@ -24,7 +24,7 @@ servo = [
[dependencies]
app_units = "0.6"
-cssparser = "0.23.0"
+cssparser = "0.24.0"
euclid = "0.17"
hashglobe = { path = "../hashglobe" }
hyper = { version = "0.10", optional = true }
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml
index 40dc8d99be7..7f30c842d34 100644
--- a/components/script/Cargo.toml
+++ b/components/script/Cargo.toml
@@ -37,7 +37,7 @@ canvas_traits = {path = "../canvas_traits"}
caseless = "0.2"
cookie = "0.10"
chrono = "0.4"
-cssparser = "0.23.0"
+cssparser = "0.24"
deny_public_fields = {path = "../deny_public_fields"}
devtools_traits = {path = "../devtools_traits"}
dom_struct = {path = "../dom_struct"}
diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs
index 61acecda71d..b37199ca0db 100644
--- a/components/script/dom/htmlimageelement.rs
+++ b/components/script/dom/htmlimageelement.rs
@@ -3,7 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use app_units::{Au, AU_PER_PX};
-use cssparser::{Parser, ParserInput};
use document_loader::{LoadType, LoadBlocker};
use dom::activation::Activatable;
use dom::attr::Attr;
@@ -58,13 +57,7 @@ use std::default::Default;
use std::i32;
use std::sync::{Arc, Mutex};
use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_double, parse_unsigned_integer};
-use style::context::QuirksMode;
-use style::media_queries::MediaQuery;
-use style::parser::ParserContext;
use style::str::is_ascii_digit;
-use style::values::specified::{Length, ViewportPercentageLength};
-use style::values::specified::length::NoCalcLength;
-use style_traits::ParsingMode;
use task_source::TaskSource;
enum ParseState {
@@ -94,12 +87,6 @@ enum State {
Broken,
}
-#[derive(Debug, PartialEq)]
-pub struct Size {
- pub query: Option<MediaQuery>,
- pub length: Length,
-}
-
#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
enum ImageRequestPhase {
Pending,
@@ -758,63 +745,6 @@ impl LayoutHTMLImageElementHelpers for LayoutDom<HTMLImageElement> {
}
}
-//https://html.spec.whatwg.org/multipage/#parse-a-sizes-attribute
-pub fn parse_a_sizes_attribute(input: DOMString, width: Option<u32>) -> Vec<Size> {
- let mut sizes = Vec::<Size>::new();
- for unparsed_size in input.split(',') {
- let whitespace = unparsed_size.chars().rev().take_while(|c| char::is_whitespace(*c)).count();
- let trimmed: String = unparsed_size.chars().take(unparsed_size.chars().count() - whitespace).collect();
-
- if trimmed.is_empty() {
- continue;
- }
- let mut input = ParserInput::new(&trimmed);
- let url = ServoUrl::parse("about:blank").unwrap();
- let context = ParserContext::new_for_cssom(
- &url,
- None,
- ParsingMode::empty(),
- QuirksMode::NoQuirks,
- None,
- );
- let mut parser = Parser::new(&mut input);
- let length = parser.try(|i| Length::parse_non_negative(&context, i));
- match length {
- Ok(len) => sizes.push(Size {
- length: len,
- query: None
- }),
- Err(_) => {
- let mut media_query_parser = parser;
- let media_query = media_query_parser.try(|i| MediaQuery::parse(&context, i));
- if let Ok(query) = media_query {
- let length = Length::parse_non_negative(&context, &mut media_query_parser);
- if let Ok(length) = length {
- sizes.push(Size {
- length: length,
- query: Some(query)
- })
- }
- }
- },
- }
- }
- if sizes.is_empty() {
- let size = match width {
- Some(w) => Size {
- length: Length::from_px(w as f32),
- query: None
- },
- None => Size {
- length: Length::NoCalc(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vw(100.))),
- query: None
- },
- };
- sizes.push(size);
- }
- sizes
-}
-
impl HTMLImageElementMethods for HTMLImageElement {
// https://html.spec.whatwg.org/multipage/#dom-img-alt
make_getter!(Alt, "alt");
diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl
index 49377cd1044..2841f3e9016 100644
--- a/components/script/dom/webidls/CSSStyleDeclaration.webidl
+++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl
@@ -350,6 +350,14 @@ partial interface CSSStyleDeclaration {
[CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString offsetInlineStart;
[CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString offset-inline-end;
[CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString offsetInlineEnd;
+ [CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString inset-block-start;
+ [CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString insetBlockStart;
+ [CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString inset-block-end;
+ [CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString insetBlockEnd;
+ [CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString inset-inline-start;
+ [CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString insetInlineStart;
+ [CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString inset-inline-end;
+ [CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString insetInlineEnd;
[CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString height;
[CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString minHeight;
diff --git a/components/script/test.rs b/components/script/test.rs
index 351c3447688..b907404da0b 100644
--- a/components/script/test.rs
+++ b/components/script/test.rs
@@ -15,10 +15,6 @@ pub mod area {
pub use dom::htmlareaelement::{Area, Shape};
}
-pub mod sizes {
- pub use dom::htmlimageelement::{parse_a_sizes_attribute, Size};
-}
-
pub mod size_of {
use dom::characterdata::CharacterData;
use dom::element::Element;
diff --git a/components/script_layout_interface/Cargo.toml b/components/script_layout_interface/Cargo.toml
index 4af1f6237c8..f06ed32526e 100644
--- a/components/script_layout_interface/Cargo.toml
+++ b/components/script_layout_interface/Cargo.toml
@@ -13,7 +13,7 @@ path = "lib.rs"
app_units = "0.6"
atomic_refcell = "0.1"
canvas_traits = {path = "../canvas_traits"}
-cssparser = "0.23.0"
+cssparser = "0.24"
euclid = "0.17"
gfx_traits = {path = "../gfx_traits"}
html5ever = "0.22"
diff --git a/components/selectors/Cargo.toml b/components/selectors/Cargo.toml
index ee38f2ef276..19adcdd1578 100644
--- a/components/selectors/Cargo.toml
+++ b/components/selectors/Cargo.toml
@@ -22,7 +22,7 @@ bench = []
[dependencies]
bitflags = "1.0"
matches = "0.1"
-cssparser = "0.23.0"
+cssparser = "0.24.0"
log = "0.4"
fnv = "1.0"
phf = "0.7.18"
diff --git a/components/selectors/builder.rs b/components/selectors/builder.rs
index 0be3eb90e0c..29ac822fa03 100644
--- a/components/selectors/builder.rs
+++ b/components/selectors/builder.rs
@@ -23,7 +23,7 @@ use sink::Push;
use smallvec::{self, SmallVec};
use std::cmp;
use std::iter;
-use std::ops::Add;
+use std::ops::{AddAssign, Add};
use std::ptr;
use std::slice;
@@ -228,6 +228,16 @@ struct Specificity {
element_selectors: u32,
}
+
+impl AddAssign for Specificity {
+ #[inline]
+ fn add_assign(&mut self, rhs: Self) {
+ self.id_selectors += rhs.id_selectors;
+ self.class_like_selectors += rhs.class_like_selectors;
+ self.element_selectors += rhs.element_selectors;
+ }
+}
+
impl Add for Specificity {
type Output = Specificity;
@@ -251,6 +261,7 @@ impl Default for Specificity {
}
impl From<u32> for Specificity {
+ #[inline]
fn from(value: u32) -> Specificity {
assert!(value <= MAX_10BIT << 20 | MAX_10BIT << 10 | MAX_10BIT);
Specificity {
@@ -262,6 +273,7 @@ impl From<u32> for Specificity {
}
impl From<Specificity> for u32 {
+ #[inline]
fn from(specificity: Specificity) -> u32 {
cmp::min(specificity.id_selectors, MAX_10BIT) << 20 |
cmp::min(specificity.class_like_selectors, MAX_10BIT) << 10 |
@@ -298,17 +310,29 @@ where
builder,
);
}
- // FIXME(emilio): Spec doesn't define any particular specificity for
- // ::slotted(), so apply the general rule for pseudos per:
- //
- // https://github.com/w3c/csswg-drafts/issues/1915
- //
- // Though other engines compute it dynamically, so maybe we should
- // do that instead, eventually.
- Component::Slotted(..) | Component::PseudoElement(..) | Component::LocalName(..) => {
+ Component::PseudoElement(..) | Component::LocalName(..) => {
specificity.element_selectors += 1
},
- Component::ID(..) => specificity.id_selectors += 1,
+ Component::Slotted(ref selector) => {
+ specificity.element_selectors += 1;
+ // Note that due to the way ::slotted works we only compete with
+ // other ::slotted rules, so the above rule doesn't really
+ // matter, but we do it still for consistency with other
+ // pseudo-elements.
+ //
+ // See: https://github.com/w3c/csswg-drafts/issues/1915
+ *specificity += Specificity::from(selector.specificity());
+ },
+ Component::Host(ref selector) => {
+ specificity.class_like_selectors += 1;
+ if let Some(ref selector) = *selector {
+ // See: https://github.com/w3c/csswg-drafts/issues/1915
+ *specificity += Specificity::from(selector.specificity());
+ }
+ }
+ Component::ID(..) => {
+ specificity.id_selectors += 1;
+ },
Component::Class(..) |
Component::AttributeInNoNamespace { .. } |
Component::AttributeInNoNamespaceExists { .. } |
@@ -319,7 +343,6 @@ where
Component::Root |
Component::Empty |
Component::Scope |
- Component::Host(..) |
Component::NthChild(..) |
Component::NthLastChild(..) |
Component::NthOfType(..) |
@@ -327,7 +350,9 @@ where
Component::FirstOfType |
Component::LastOfType |
Component::OnlyOfType |
- Component::NonTSPseudoClass(..) => specificity.class_like_selectors += 1,
+ Component::NonTSPseudoClass(..) => {
+ specificity.class_like_selectors += 1;
+ },
Component::ExplicitUniversalType |
Component::ExplicitAnyNamespace |
Component::ExplicitNoNamespace |
diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs
index c3591ff2727..7c4812cb963 100644
--- a/components/selectors/matching.rs
+++ b/components/selectors/matching.rs
@@ -576,7 +576,7 @@ where
_ => {},
}
- if element.is_link() || combinator.is_sibling() {
+ if element.is_link() {
visited_handling = VisitedHandlingMode::AllLinksUnvisited;
}
diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs
index 3210f1085b0..399f21e6361 100644
--- a/components/selectors/parser.rs
+++ b/components/selectors/parser.rs
@@ -995,8 +995,7 @@ impl<Impl: SelectorImpl> ToCss for Selector<Impl> {
let mut combinators = self.iter_raw_match_order()
.rev()
- .filter(|x| x.is_combinator())
- .peekable();
+ .filter_map(|x| x.as_combinator());
let compound_selectors = self.iter_raw_match_order()
.as_slice()
.split(|x| x.is_combinator())
@@ -1007,72 +1006,74 @@ impl<Impl: SelectorImpl> ToCss for Selector<Impl> {
debug_assert!(!combinators_exhausted);
// https://drafts.csswg.org/cssom/#serializing-selectors
+ if compound.is_empty() {
+ continue;
+ }
- if !compound.is_empty() {
- // 1. If there is only one simple selector in the compound selectors
- // which is a universal selector, append the result of
- // serializing the universal selector to s.
- //
- // Check if `!compound.empty()` first--this can happen if we have
- // something like `... > ::before`, because we store `>` and `::`
- // both as combinators internally.
- //
- // If we are in this case, after we have serialized the universal
- // selector, we skip Step 2 and continue with the algorithm.
- let (can_elide_namespace, first_non_namespace) = match &compound[0] {
- &Component::ExplicitAnyNamespace |
- &Component::ExplicitNoNamespace |
- &Component::Namespace(_, _) => (false, 1),
- &Component::DefaultNamespace(_) => (true, 1),
- _ => (true, 0),
- };
- let mut perform_step_2 = true;
- if first_non_namespace == compound.len() - 1 {
- match (combinators.peek(), &compound[first_non_namespace]) {
- // We have to be careful here, because if there is a
- // pseudo element "combinator" there isn't really just
- // the one simple selector. Technically this compound
- // selector contains the pseudo element selector as well
- // -- Combinator::PseudoElement, just like
- // Combinator::SlotAssignment, don't exist in the
- // spec.
- (Some(&&Component::Combinator(Combinator::PseudoElement)), _) |
- (Some(&&Component::Combinator(Combinator::SlotAssignment)), _) => (),
- (_, &Component::ExplicitUniversalType) => {
- // Iterate over everything so we serialize the namespace
- // too.
- for simple in compound.iter() {
- simple.to_css(dest)?;
- }
- // Skip step 2, which is an "otherwise".
- perform_step_2 = false;
- },
- (_, _) => (),
- }
+ // 1. If there is only one simple selector in the compound selectors
+ // which is a universal selector, append the result of
+ // serializing the universal selector to s.
+ //
+ // Check if `!compound.empty()` first--this can happen if we have
+ // something like `... > ::before`, because we store `>` and `::`
+ // both as combinators internally.
+ //
+ // If we are in this case, after we have serialized the universal
+ // selector, we skip Step 2 and continue with the algorithm.
+ let (can_elide_namespace, first_non_namespace) = match compound[0] {
+ Component::ExplicitAnyNamespace |
+ Component::ExplicitNoNamespace |
+ Component::Namespace(..) => (false, 1),
+ Component::DefaultNamespace(..) => (true, 1),
+ _ => (true, 0),
+ };
+ let mut perform_step_2 = true;
+ let next_combinator = combinators.next();
+ if first_non_namespace == compound.len() - 1 {
+ match (next_combinator, &compound[first_non_namespace]) {
+ // We have to be careful here, because if there is a
+ // pseudo element "combinator" there isn't really just
+ // the one simple selector. Technically this compound
+ // selector contains the pseudo element selector as well
+ // -- Combinator::PseudoElement, just like
+ // Combinator::SlotAssignment, don't exist in the
+ // spec.
+ (Some(Combinator::PseudoElement), _) |
+ (Some(Combinator::SlotAssignment), _) => (),
+ (_, &Component::ExplicitUniversalType) => {
+ // Iterate over everything so we serialize the namespace
+ // too.
+ for simple in compound.iter() {
+ simple.to_css(dest)?;
+ }
+ // Skip step 2, which is an "otherwise".
+ perform_step_2 = false;
+ },
+ _ => (),
}
+ }
- // 2. Otherwise, for each simple selector in the compound selectors
- // that is not a universal selector of which the namespace prefix
- // maps to a namespace that is not the default namespace
- // serialize the simple selector and append the result to s.
- //
- // See https://github.com/w3c/csswg-drafts/issues/1606, which is
- // proposing to change this to match up with the behavior asserted
- // in cssom/serialize-namespaced-type-selectors.html, which the
- // following code tries to match.
- if perform_step_2 {
- for simple in compound.iter() {
- if let Component::ExplicitUniversalType = *simple {
- // Can't have a namespace followed by a pseudo-element
- // selector followed by a universal selector in the same
- // compound selector, so we don't have to worry about the
- // real namespace being in a different `compound`.
- if can_elide_namespace {
- continue;
- }
+ // 2. Otherwise, for each simple selector in the compound selectors
+ // that is not a universal selector of which the namespace prefix
+ // maps to a namespace that is not the default namespace
+ // serialize the simple selector and append the result to s.
+ //
+ // See https://github.com/w3c/csswg-drafts/issues/1606, which is
+ // proposing to change this to match up with the behavior asserted
+ // in cssom/serialize-namespaced-type-selectors.html, which the
+ // following code tries to match.
+ if perform_step_2 {
+ for simple in compound.iter() {
+ if let Component::ExplicitUniversalType = *simple {
+ // Can't have a namespace followed by a pseudo-element
+ // selector followed by a universal selector in the same
+ // compound selector, so we don't have to worry about the
+ // real namespace being in a different `compound`.
+ if can_elide_namespace {
+ continue;
}
- simple.to_css(dest)?;
}
+ simple.to_css(dest)?;
}
}
@@ -1081,7 +1082,7 @@ impl<Impl: SelectorImpl> ToCss for Selector<Impl> {
// ">", "+", "~", ">>", "||", as appropriate, followed by another
// single SPACE (U+0020) if the combinator was not whitespace, to
// s.
- match combinators.next() {
+ match next_combinator {
Some(c) => c.to_css(dest)?,
None => combinators_exhausted = true,
};
diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml
index 4fb50d8ad85..b80b7572bba 100644
--- a/components/style/Cargo.toml
+++ b/components/style/Cargo.toml
@@ -31,7 +31,7 @@ atomic_refcell = "0.1"
bitflags = "1.0"
byteorder = "1.0"
cfg-if = "0.1.0"
-cssparser = "0.23.0"
+cssparser = "0.24.0"
new_debug_unreachable = "1.0"
encoding_rs = {version = "0.7", optional = true}
euclid = "0.17"
diff --git a/components/style/build_gecko.rs b/components/style/build_gecko.rs
index 95bb1622942..68d160ab5e0 100644
--- a/components/style/build_gecko.rs
+++ b/components/style/build_gecko.rs
@@ -182,14 +182,21 @@ mod bindings {
// Disable rust unions, because we replace some types inside of
// them.
let mut builder = Builder::default().rust_target(RustTarget::Stable_1_0);
- let rustfmt_path = env::var_os("MOZ_AUTOMATION")
- .and_then(|_| env::var_os("TOOLTOOL_DIR").or_else(|| env::var_os("MOZ_SRC")))
- .map(PathBuf::from);
- builder = match rustfmt_path {
- Some(path) => builder.with_rustfmt(path.join("rustc").join("bin").join("rustfmt")),
- None => builder.rustfmt_bindings(env::var_os("STYLO_RUSTFMT_BINDINGS").is_some()),
- };
+ let rustfmt_path = env::var_os("RUSTFMT")
+ // This can be replaced with
+ // > .filter(|p| !p.is_empty()).map(PathBuf::from)
+ // once we can use 1.27+.
+ .and_then(|p| {
+ if p.is_empty() {
+ None
+ } else {
+ Some(PathBuf::from(p))
+ }
+ });
+ if let Some(path) = rustfmt_path {
+ builder = builder.with_rustfmt(path);
+ }
for dir in SEARCH_PATHS.iter() {
builder = builder.clang_arg("-I").clang_arg(dir.to_str().unwrap());
diff --git a/components/style/element_state.rs b/components/style/element_state.rs
index 0960ca3d4e1..1652dee672f 100644
--- a/components/style/element_state.rs
+++ b/components/style/element_state.rs
@@ -71,7 +71,8 @@ bitflags! {
const IN_OPTIONAL_STATE = 1 << 22;
/// <https://html.spec.whatwg.org/multipage/#selector-read-write>
const IN_READ_WRITE_STATE = 1 << 22;
- /// There is a free bit at 23.
+ /// <https://html.spec.whatwg.org/multipage/#selector-defined>
+ const IN_DEFINED_STATE = 1 << 23;
/// <https://html.spec.whatwg.org/multipage/#selector-visited>
const IN_VISITED_STATE = 1 << 24;
/// <https://html.spec.whatwg.org/multipage/#selector-link>
diff --git a/components/style/gecko/media_queries.rs b/components/style/gecko/media_queries.rs
index 771f4570b47..9c5ef4d71a1 100644
--- a/components/style/gecko/media_queries.rs
+++ b/components/style/gecko/media_queries.rs
@@ -7,7 +7,7 @@
use app_units::AU_PER_PX;
use app_units::Au;
use context::QuirksMode;
-use cssparser::{BasicParseErrorKind, Parser, RGBA};
+use cssparser::{Parser, RGBA, Token};
use euclid::Size2D;
use euclid::TypedScale;
use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
@@ -255,20 +255,53 @@ pub enum Range {
Min,
/// At most the specified value.
Max,
- /// Exactly the specified value.
+}
+
+/// The operator that was specified in this media feature.
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
+enum Operator {
Equal,
+ GreaterThan,
+ GreaterThanEqual,
+ LessThan,
+ LessThanEqual,
+}
+
+impl ToCss for Operator {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ dest.write_str(match *self {
+ Operator::Equal => "=",
+ Operator::LessThan => "<",
+ Operator::LessThanEqual => "<=",
+ Operator::GreaterThan => ">",
+ Operator::GreaterThanEqual => ">=",
+ })
+ }
}
-/// A expression for gecko contains a reference to the media feature, the value
-/// the media query contained, and the range to evaluate.
+/// Either a `Range` or an `Operator`.
+///
+/// Ranged media features are not allowed with operations (that'd make no
+/// sense).
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
+enum RangeOrOperator {
+ Range(Range),
+ Operator(Operator),
+}
+
+/// A feature expression for gecko contains a reference to the media feature,
+/// the value the media query contained, and the range to evaluate.
#[derive(Clone, Debug, MallocSizeOf)]
-pub struct Expression {
+pub struct MediaFeatureExpression {
feature: &'static nsMediaFeature,
value: Option<MediaExpressionValue>,
- range: Range,
+ range_or_operator: Option<RangeOrOperator>,
}
-impl ToCss for Expression {
+impl ToCss for MediaFeatureExpression {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
@@ -279,10 +312,12 @@ impl ToCss for Expression {
{
dest.write_str("-webkit-")?;
}
- match self.range {
- Range::Min => dest.write_str("min-")?,
- Range::Max => dest.write_str("max-")?,
- Range::Equal => {},
+
+ if let Some(RangeOrOperator::Range(range)) = self.range_or_operator {
+ match range {
+ Range::Min => dest.write_str("min-")?,
+ Range::Max => dest.write_str("max-")?,
+ }
}
// NB: CssStringWriter not needed, feature names are under control.
@@ -290,8 +325,15 @@ impl ToCss for Expression {
Atom::from_static(*self.feature.mName)
})?;
- if let Some(ref val) = self.value {
+ if let Some(RangeOrOperator::Operator(op)) = self.range_or_operator {
+ dest.write_char(' ')?;
+ op.to_css(dest)?;
+ dest.write_char(' ')?;
+ } else if self.value.is_some() {
dest.write_str(": ")?;
+ }
+
+ if let Some(ref val) = self.value {
val.to_css(dest, self)?;
}
@@ -299,10 +341,10 @@ impl ToCss for Expression {
}
}
-impl PartialEq for Expression {
- fn eq(&self, other: &Expression) -> bool {
+impl PartialEq for MediaFeatureExpression {
+ fn eq(&self, other: &Self) -> bool {
self.feature.mName == other.feature.mName && self.value == other.value &&
- self.range == other.range
+ self.range_or_operator == other.range_or_operator
}
}
@@ -337,7 +379,10 @@ pub enum MediaExpressionValue {
}
impl MediaExpressionValue {
- fn from_css_value(for_expr: &Expression, css_value: &nsCSSValue) -> Option<Self> {
+ fn from_css_value(
+ for_expr: &MediaFeatureExpression,
+ css_value: &nsCSSValue,
+ ) -> Option<Self> {
// NB: If there's a null value, that means that we don't support the
// feature.
if css_value.mUnit == nsCSSUnit::eCSSUnit_Null {
@@ -395,7 +440,7 @@ impl MediaExpressionValue {
}
impl MediaExpressionValue {
- fn to_css<W>(&self, dest: &mut CssWriter<W>, for_expr: &Expression) -> fmt::Result
+ fn to_css<W>(&self, dest: &mut CssWriter<W>, for_expr: &MediaFeatureExpression) -> fmt::Result
where
W: fmt::Write,
{
@@ -537,17 +582,53 @@ fn parse_feature_value<'i, 't>(
Ok(value)
}
-impl Expression {
+/// Consumes an operation or a colon, or returns an error.
+fn consume_operation_or_colon(
+ input: &mut Parser,
+) -> Result<Option<Operator>, ()> {
+ let first_delim = {
+ let next_token = match input.next() {
+ Ok(t) => t,
+ Err(..) => return Err(()),
+ };
+
+ match *next_token {
+ Token::Colon => return Ok(None),
+ Token::Delim(oper) => oper,
+ _ => return Err(()),
+ }
+ };
+ Ok(Some(match first_delim {
+ '=' => Operator::Equal,
+ '>' => {
+ if input.try(|i| i.expect_delim('=')).is_ok() {
+ Operator::GreaterThanEqual
+ } else {
+ Operator::GreaterThan
+ }
+ }
+ '<' => {
+ if input.try(|i| i.expect_delim('=')).is_ok() {
+ Operator::LessThanEqual
+ } else {
+ Operator::LessThan
+ }
+ }
+ _ => return Err(()),
+ }))
+}
+
+impl MediaFeatureExpression {
/// Trivially construct a new expression.
fn new(
feature: &'static nsMediaFeature,
value: Option<MediaExpressionValue>,
- range: Range,
+ range_or_operator: Option<RangeOrOperator>,
) -> Self {
Self {
feature,
value,
- range,
+ range_or_operator,
}
}
@@ -560,116 +641,145 @@ impl Expression {
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
- input.expect_parenthesis_block().map_err(|err| {
- err.location.new_custom_error(match err.kind {
- BasicParseErrorKind::UnexpectedToken(t) => {
- StyleParseErrorKind::ExpectedIdentifier(t)
- },
- _ => StyleParseErrorKind::UnspecifiedError,
- })
- })?;
-
+ input.expect_parenthesis_block()?;
input.parse_nested_block(|input| {
- // FIXME: remove extra indented block when lifetimes are non-lexical
- let feature;
- let range;
- {
- let location = input.current_source_location();
- let ident = input.expect_ident().map_err(|err| {
- err.location.new_custom_error(match err.kind {
- BasicParseErrorKind::UnexpectedToken(t) => {
- StyleParseErrorKind::ExpectedIdentifier(t)
- },
- _ => StyleParseErrorKind::UnspecifiedError,
- })
- })?;
-
- let mut flags = 0;
-
- if context.chrome_rules_enabled() || context.stylesheet_origin == Origin::UserAgent
- {
- flags |= structs::nsMediaFeature_RequirementFlags_eUserAgentAndChromeOnly;
- }
+ Self::parse_in_parenthesis_block(context, input)
+ })
+ }
- let result = {
- let mut feature_name = &**ident;
-
- if unsafe { structs::StaticPrefs_sVarCache_layout_css_prefixes_webkit } &&
- starts_with_ignore_ascii_case(feature_name, "-webkit-")
- {
- feature_name = &feature_name[8..];
- flags |= structs::nsMediaFeature_RequirementFlags_eHasWebkitPrefix;
- if unsafe {
- structs::StaticPrefs_sVarCache_layout_css_prefixes_device_pixel_ratio_webkit
- } {
- flags |= structs::nsMediaFeature_RequirementFlags_eWebkitDevicePixelRatioPrefEnabled;
- }
- }
+ /// Parse a media feature expression where we've already consumed the
+ /// parenthesis.
+ pub fn parse_in_parenthesis_block<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ // FIXME: remove extra indented block when lifetimes are non-lexical
+ let feature;
+ let range;
+ {
+ let location = input.current_source_location();
+ let ident = input.expect_ident()?;
- let range = if starts_with_ignore_ascii_case(feature_name, "min-") {
- feature_name = &feature_name[4..];
- Range::Min
- } else if starts_with_ignore_ascii_case(feature_name, "max-") {
- feature_name = &feature_name[4..];
- Range::Max
- } else {
- Range::Equal
- };
+ let mut flags = 0;
- let atom = Atom::from(string_as_ascii_lowercase(feature_name));
- match find_feature(|f| atom.as_ptr() == unsafe { *f.mName as *mut _ }) {
- Some(f) => Ok((f, range)),
- None => Err(()),
+ if context.chrome_rules_enabled() || context.stylesheet_origin == Origin::UserAgent
+ {
+ flags |= structs::nsMediaFeature_RequirementFlags_eUserAgentAndChromeOnly;
+ }
+
+ let result = {
+ let mut feature_name = &**ident;
+
+ if unsafe { structs::StaticPrefs_sVarCache_layout_css_prefixes_webkit } &&
+ starts_with_ignore_ascii_case(feature_name, "-webkit-")
+ {
+ feature_name = &feature_name[8..];
+ flags |= structs::nsMediaFeature_RequirementFlags_eHasWebkitPrefix;
+ if unsafe {
+ structs::StaticPrefs_sVarCache_layout_css_prefixes_device_pixel_ratio_webkit
+ } {
+ flags |= structs::nsMediaFeature_RequirementFlags_eWebkitDevicePixelRatioPrefEnabled;
}
+ }
+
+ let range = if starts_with_ignore_ascii_case(feature_name, "min-") {
+ feature_name = &feature_name[4..];
+ Some(Range::Min)
+ } else if starts_with_ignore_ascii_case(feature_name, "max-") {
+ feature_name = &feature_name[4..];
+ Some(Range::Max)
+ } else {
+ None
};
- match result {
- Ok((f, r)) => {
- feature = f;
- range = r;
- },
- Err(()) => {
- return Err(location.new_custom_error(
- StyleParseErrorKind::MediaQueryExpectedFeatureName(ident.clone()),
- ))
- },
+ let atom = Atom::from(string_as_ascii_lowercase(feature_name));
+ match find_feature(|f| atom.as_ptr() == unsafe { *f.mName as *mut _ }) {
+ Some(f) => Ok((f, range)),
+ None => Err(()),
}
+ };
- if (feature.mReqFlags & !flags) != 0 {
+ match result {
+ Ok((f, r)) => {
+ feature = f;
+ range = r;
+ },
+ Err(()) => {
return Err(location.new_custom_error(
StyleParseErrorKind::MediaQueryExpectedFeatureName(ident.clone()),
+ ))
+ },
+ }
+
+ if (feature.mReqFlags & !flags) != 0 {
+ return Err(location.new_custom_error(
+ StyleParseErrorKind::MediaQueryExpectedFeatureName(ident.clone()),
+ ));
+ }
+
+ if range.is_some() &&
+ feature.mRangeType != nsMediaFeature_RangeType::eMinMaxAllowed
+ {
+ return Err(location.new_custom_error(
+ StyleParseErrorKind::MediaQueryExpectedFeatureName(ident.clone()),
+ ));
+ }
+ }
+
+ let feature_allows_ranges =
+ feature.mRangeType == nsMediaFeature_RangeType::eMinMaxAllowed;
+
+ let operator = input.try(consume_operation_or_colon);
+ let operator = match operator {
+ Err(..) => {
+ // If there's no colon, this is a media query of the
+ // form '(<feature>)', that is, there's no value
+ // specified.
+ //
+ // Gecko doesn't allow ranged expressions without a
+ // value, so just reject them here too.
+ if range.is_some() {
+ return Err(input.new_custom_error(
+ StyleParseErrorKind::RangedExpressionWithNoValue
));
}
- if range != Range::Equal &&
- feature.mRangeType != nsMediaFeature_RangeType::eMinMaxAllowed
- {
- return Err(location.new_custom_error(
- StyleParseErrorKind::MediaQueryExpectedFeatureName(ident.clone()),
+ return Ok(Self::new(feature, None, None));
+ }
+ Ok(operator) => operator,
+ };
+
+ let range_or_operator = match range {
+ Some(range) => {
+ if operator.is_some() {
+ return Err(input.new_custom_error(
+ StyleParseErrorKind::MediaQueryUnexpectedOperator
));
}
+ Some(RangeOrOperator::Range(range))
}
-
- // If there's no colon, this is a media query of the form
- // '(<feature>)', that is, there's no value specified.
- //
- // Gecko doesn't allow ranged expressions without a value, so just
- // reject them here too.
- if input.try(|i| i.expect_colon()).is_err() {
- if range != Range::Equal {
- return Err(input.new_custom_error(StyleParseErrorKind::RangedExpressionWithNoValue));
+ None => {
+ match operator {
+ Some(operator) => {
+ if !feature_allows_ranges {
+ return Err(input.new_custom_error(
+ StyleParseErrorKind::MediaQueryUnexpectedOperator
+ ));
+ }
+ Some(RangeOrOperator::Operator(operator))
+ }
+ None => None,
}
- return Ok(Expression::new(feature, None, range));
}
+ };
- let value =
- parse_feature_value(feature, feature.mValueType, context, input).map_err(|err| {
- err.location
- .new_custom_error(StyleParseErrorKind::MediaQueryExpectedFeatureValue)
- })?;
+ let value =
+ parse_feature_value(feature, feature.mValueType, context, input).map_err(|err| {
+ err.location
+ .new_custom_error(StyleParseErrorKind::MediaQueryExpectedFeatureValue)
+ })?;
- Ok(Expression::new(feature, Some(value), range))
- })
+ Ok(Self::new(feature, Some(value), range_or_operator))
}
/// Returns whether this media query evaluates to true for the given device.
@@ -704,8 +814,8 @@ impl Expression {
use std::cmp::Ordering;
debug_assert!(
- self.range == Range::Equal ||
- self.feature.mRangeType == nsMediaFeature_RangeType::eMinMaxAllowed,
+ self.feature.mRangeType == nsMediaFeature_RangeType::eMinMaxAllowed ||
+ self.range_or_operator.is_none(),
"Whoops, wrong range"
);
@@ -730,7 +840,7 @@ impl Expression {
};
// FIXME(emilio): Handle the possible floating point errors?
- let cmp = match (required_value, actual_value) {
+ let cmp = match (actual_value, required_value) {
(&Length(ref one), &Length(ref other)) => {
computed::Context::for_media_query_evaluation(device, quirks_mode, |context| {
one.to_computed_value(&context)
@@ -750,11 +860,11 @@ impl Expression {
if (*device.pres_context).mOverrideDPPX > 0.0 {
self::Resolution::Dppx((*device.pres_context).mOverrideDPPX).to_dpi()
} else {
- other.to_dpi()
+ one.to_dpi()
}
};
- one.to_dpi().partial_cmp(&actual_dpi).unwrap()
+ actual_dpi.partial_cmp(&other.to_dpi()).unwrap()
},
(&Ident(ref one), &Ident(ref other)) => {
debug_assert_ne!(
@@ -773,10 +883,31 @@ impl Expression {
_ => unreachable!(),
};
- cmp == Ordering::Equal || match self.range {
- Range::Min => cmp == Ordering::Less,
- Range::Equal => false,
- Range::Max => cmp == Ordering::Greater,
+ let range_or_op = match self.range_or_operator {
+ Some(r) => r,
+ None => return cmp == Ordering::Equal,
+ };
+
+ match range_or_op {
+ RangeOrOperator::Range(range) => {
+ cmp == Ordering::Equal || match range {
+ Range::Min => cmp == Ordering::Greater,
+ Range::Max => cmp == Ordering::Less,
+ }
+ }
+ RangeOrOperator::Operator(op) => {
+ match op {
+ Operator::Equal => cmp == Ordering::Equal,
+ Operator::GreaterThan => cmp == Ordering::Greater,
+ Operator::GreaterThanEqual => {
+ cmp == Ordering::Equal || cmp == Ordering::Greater
+ }
+ Operator::LessThan => cmp == Ordering::Less,
+ Operator::LessThanEqual => {
+ cmp == Ordering::Equal || cmp == Ordering::Less
+ }
+ }
+ }
}
}
}
diff --git a/components/style/gecko/non_ts_pseudo_class_list.rs b/components/style/gecko/non_ts_pseudo_class_list.rs
index b02e21fbf8a..04f57b73885 100644
--- a/components/style/gecko/non_ts_pseudo_class_list.rs
+++ b/components/style/gecko/non_ts_pseudo_class_list.rs
@@ -47,6 +47,7 @@ macro_rules! apply_non_ts_list {
("visited", Visited, visited, IN_VISITED_STATE, _),
("active", Active, active, IN_ACTIVE_STATE, _),
("checked", Checked, checked, IN_CHECKED_STATE, _),
+ ("defined", Defined, defined, IN_DEFINED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
("disabled", Disabled, disabled, IN_DISABLED_STATE, _),
("enabled", Enabled, enabled, IN_ENABLED_STATE, _),
("focus", Focus, focus, IN_FOCUS_STATE, _),
diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs
index 57a98a89238..7dfc9aa1bbc 100644
--- a/components/style/gecko/selector_parser.rs
+++ b/components/style/gecko/selector_parser.rs
@@ -187,6 +187,9 @@ impl NonTSPseudoClass {
NonTSPseudoClass::Fullscreen => unsafe {
mozilla::StaticPrefs_sVarCache_full_screen_api_unprefix_enabled
},
+ NonTSPseudoClass::Defined => unsafe {
+ structs::nsContentUtils_sIsCustomElementsEnabled
+ },
// Otherwise, a pseudo-class is enabled in content when it
// doesn't have any enabled flag.
_ => !self.has_any_flag(
diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs
index 0ce4a45d175..ee0c134b269 100644
--- a/components/style/gecko/wrapper.rs
+++ b/components/style/gecko/wrapper.rs
@@ -37,6 +37,7 @@ use gecko_bindings::bindings::Gecko_ElementHasAnimations;
use gecko_bindings::bindings::Gecko_ElementHasCSSAnimations;
use gecko_bindings::bindings::Gecko_ElementHasCSSTransitions;
use gecko_bindings::bindings::Gecko_GetActiveLinkAttrDeclarationBlock;
+use gecko_bindings::bindings::Gecko_GetAnimationEffectCount;
use gecko_bindings::bindings::Gecko_GetAnimationRule;
use gecko_bindings::bindings::Gecko_GetExtraContentStyleDeclarations;
use gecko_bindings::bindings::Gecko_GetHTMLPresentationAttrDeclarationBlock;
@@ -650,7 +651,10 @@ impl<'le> GeckoElement<'le> {
#[inline]
fn extended_slots(&self) -> Option<&structs::FragmentOrElement_nsExtendedDOMSlots> {
self.dom_slots().and_then(|s| unsafe {
- (s._base.mExtendedSlots.mPtr as *const structs::FragmentOrElement_nsExtendedDOMSlots)
+ // For the bit usage, see nsContentSlots::GetExtendedSlots.
+ let e_slots = s._base.mExtendedSlots &
+ !structs::nsIContent_nsContentSlots_sNonOwningExtendedSlotsFlag;
+ (e_slots as *const structs::FragmentOrElement_nsExtendedDOMSlots)
.as_ref()
})
}
@@ -948,8 +952,16 @@ fn get_animation_rule(
cascade_level: CascadeLevel,
) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
use gecko_bindings::sugar::ownership::HasSimpleFFI;
+ use properties::longhands::ANIMATABLE_PROPERTY_COUNT;
+
+ // There's a very rough correlation between the number of effects
+ // (animations) on an element and the number of properties it is likely to
+ // animate, so we use that as an initial guess for the size of the
+ // AnimationValueMap in order to reduce the number of re-allocations needed.
+ let effect_count = unsafe { Gecko_GetAnimationEffectCount(element.0) };
// Also, we should try to reuse the PDB, to avoid creating extra rule nodes.
- let mut animation_values = AnimationValueMap::default();
+ let mut animation_values = AnimationValueMap::with_capacity_and_hasher(
+ effect_count.min(ANIMATABLE_PROPERTY_COUNT), Default::default());
if unsafe {
Gecko_GetAnimationRule(
element.0,
@@ -2115,6 +2127,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
{
use selectors::matching::*;
match *pseudo_class {
+ NonTSPseudoClass::Defined |
NonTSPseudoClass::Focus |
NonTSPseudoClass::Enabled |
NonTSPseudoClass::Disabled |
diff --git a/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs b/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs
index 8bdf38d4f84..dfd819bcedb 100644
--- a/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs
+++ b/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs
@@ -5,9 +5,7 @@
//! Rust helpers for Gecko's `nsCSSShadowItem`.
use app_units::Au;
-use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
use gecko_bindings::structs::nsCSSShadowItem;
-use values::computed::RGBAColor;
use values::computed::effects::{BoxShadow, SimpleShadow};
impl nsCSSShadowItem {
@@ -37,31 +35,14 @@ impl nsCSSShadowItem {
self.mRadius = shadow.blur.0.to_i32_au();
self.mSpread = 0;
self.mInset = false;
- if let Some(color) = shadow.color {
- self.mHasColor = true;
- self.mColor = convert_rgba_to_nscolor(&color);
- } else {
- // TODO handle currentColor
- // https://bugzilla.mozilla.org/show_bug.cgi?id=760345
- self.mHasColor = false;
- self.mColor = 0;
- }
- }
-
- #[inline]
- fn extract_color(&self) -> Option<RGBAColor> {
- if self.mHasColor {
- Some(convert_nscolor_to_rgba(self.mColor))
- } else {
- None
- }
+ self.mColor = shadow.color.into();
}
/// Gets a simple shadow from this item.
#[inline]
fn extract_simple_shadow(&self) -> SimpleShadow {
SimpleShadow {
- color: self.extract_color(),
+ color: self.mColor.into(),
horizontal: Au(self.mXOffset).into(),
vertical: Au(self.mYOffset).into(),
blur: Au(self.mRadius).into(),
diff --git a/components/style/media_queries/media_condition.rs b/components/style/media_queries/media_condition.rs
new file mode 100644
index 00000000000..4b80794af39
--- /dev/null
+++ b/components/style/media_queries/media_condition.rs
@@ -0,0 +1,192 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! A media query condition:
+//!
+//! https://drafts.csswg.org/mediaqueries-4/#typedef-media-condition
+
+use context::QuirksMode;
+use cssparser::{Parser, Token};
+use parser::ParserContext;
+use std::fmt::{self, Write};
+use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
+use super::{Device, MediaFeatureExpression};
+
+
+/// A binary `and` or `or` operator.
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
+#[allow(missing_docs)]
+pub enum Operator {
+ And,
+ Or,
+}
+
+/// Whether to allow an `or` condition or not during parsing.
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
+enum AllowOr {
+ Yes,
+ No,
+}
+
+/// Represents a media condition.
+#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
+pub enum MediaCondition {
+ /// A simple media feature expression, implicitly parenthesized.
+ Feature(MediaFeatureExpression),
+ /// A negation of a condition.
+ Not(Box<MediaCondition>),
+ /// A set of joint operations.
+ Operation(Box<[MediaCondition]>, Operator),
+ /// A condition wrapped in parenthesis.
+ InParens(Box<MediaCondition>),
+}
+
+impl ToCss for MediaCondition {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ match *self {
+ // NOTE(emilio): MediaFeatureExpression already includes the
+ // parenthesis.
+ MediaCondition::Feature(ref f) => f.to_css(dest),
+ MediaCondition::Not(ref c) => {
+ dest.write_str("not ")?;
+ c.to_css(dest)
+ }
+ MediaCondition::InParens(ref c) => {
+ dest.write_char('(')?;
+ c.to_css(dest)?;
+ dest.write_char(')')
+ }
+ MediaCondition::Operation(ref list, op) => {
+ let mut iter = list.iter();
+ iter.next().unwrap().to_css(dest)?;
+ for item in iter {
+ dest.write_char(' ')?;
+ op.to_css(dest)?;
+ dest.write_char(' ')?;
+ item.to_css(dest)?;
+ }
+ Ok(())
+ }
+ }
+ }
+}
+
+impl MediaCondition {
+ /// Parse a single media condition.
+ pub fn parse<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ Self::parse_internal(context, input, AllowOr::Yes)
+ }
+
+ /// Parse a single media condition, disallowing `or` expressions.
+ ///
+ /// To be used from the legacy media query syntax.
+ pub fn parse_disallow_or<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ Self::parse_internal(context, input, AllowOr::No)
+ }
+
+ fn parse_internal<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ allow_or: AllowOr,
+ ) -> Result<Self, ParseError<'i>> {
+ let location = input.current_source_location();
+
+ // FIXME(emilio): This can be cleaner with nll.
+ let is_negation = match *input.next()? {
+ Token::ParenthesisBlock => false,
+ Token::Ident(ref ident) if ident.eq_ignore_ascii_case("not") => true,
+ ref t => {
+ return Err(location.new_unexpected_token_error(t.clone()))
+ }
+ };
+
+ if is_negation {
+ let inner_condition = Self::parse_in_parens(context, input)?;
+ return Ok(MediaCondition::Not(Box::new(inner_condition)))
+ }
+
+ // ParenthesisBlock.
+ let first_condition = Self::parse_paren_block(context, input)?;
+ let operator = match input.try(Operator::parse) {
+ Ok(op) => op,
+ Err(..) => return Ok(first_condition),
+ };
+
+ if allow_or == AllowOr::No && operator == Operator::Or {
+ return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
+ }
+
+ let mut conditions = vec![];
+ conditions.push(first_condition);
+ conditions.push(Self::parse_in_parens(context, input)?);
+
+ let delim = match operator {
+ Operator::And => "and",
+ Operator::Or => "or",
+ };
+
+ loop {
+ if input.try(|i| i.expect_ident_matching(delim)).is_err() {
+ return Ok(MediaCondition::Operation(
+ conditions.into_boxed_slice(),
+ operator,
+ ));
+ }
+
+ conditions.push(Self::parse_in_parens(context, input)?);
+ }
+ }
+
+ /// Parse a media condition in parentheses.
+ pub fn parse_in_parens<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ input.expect_parenthesis_block()?;
+ Self::parse_paren_block(context, input)
+ }
+
+ fn parse_paren_block<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ input.parse_nested_block(|input| {
+ // Base case.
+ if let Ok(inner) = input.try(|i| Self::parse(context, i)) {
+ return Ok(MediaCondition::InParens(Box::new(inner)))
+ }
+ let expr = MediaFeatureExpression::parse_in_parenthesis_block(context, input)?;
+ Ok(MediaCondition::Feature(expr))
+ })
+ }
+
+ /// Whether this condition matches the device and quirks mode.
+ pub fn matches(&self, device: &Device, quirks_mode: QuirksMode) -> bool {
+ match *self {
+ MediaCondition::Feature(ref f) => f.matches(device, quirks_mode),
+ MediaCondition::InParens(ref c) => c.matches(device, quirks_mode),
+ MediaCondition::Not(ref c) => !c.matches(device, quirks_mode),
+ MediaCondition::Operation(ref conditions, op) => {
+ let mut iter = conditions.iter();
+ match op {
+ Operator::And => {
+ iter.all(|c| c.matches(device, quirks_mode))
+ }
+ Operator::Or => {
+ iter.any(|c| c.matches(device, quirks_mode))
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/components/style/media_queries/media_list.rs b/components/style/media_queries/media_list.rs
index a5604b6dc19..f8d15df7257 100644
--- a/components/style/media_queries/media_list.rs
+++ b/components/style/media_queries/media_list.rs
@@ -73,16 +73,14 @@ impl MediaList {
/// Evaluate a whole `MediaList` against `Device`.
pub fn evaluate(&self, device: &Device, quirks_mode: QuirksMode) -> bool {
- // Check if it is an empty media query list or any queries match (OR condition)
+ // Check if it is an empty media query list or any queries match.
// https://drafts.csswg.org/mediaqueries-4/#mq-list
self.media_queries.is_empty() || self.media_queries.iter().any(|mq| {
let media_match = mq.media_type.matches(device.media_type());
- // Check if all conditions match (AND condition)
+ // Check if the media condition match.
let query_match = media_match &&
- mq.expressions
- .iter()
- .all(|expression| expression.matches(&device, quirks_mode));
+ mq.condition.as_ref().map_or(true, |c| c.matches(device, quirks_mode));
// Apply the logical NOT qualifier to the result
match mq.qualifier {
diff --git a/components/style/media_queries/media_query.rs b/components/style/media_queries/media_query.rs
index 10fa84bd61a..089fc9412b2 100644
--- a/components/style/media_queries/media_query.rs
+++ b/components/style/media_queries/media_query.rs
@@ -9,13 +9,13 @@
use Atom;
use cssparser::Parser;
use parser::ParserContext;
-use selectors::parser::SelectorParseErrorKind;
use std::fmt::{self, Write};
use str::string_as_ascii_lowercase;
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-use super::Expression;
+use style_traits::{CssWriter, ParseError, ToCss};
+use super::media_condition::MediaCondition;
use values::CustomIdent;
+
/// <https://drafts.csswg.org/mediaqueries/#mq-prefix>
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
pub enum Qualifier {
@@ -65,8 +65,9 @@ pub struct MediaQuery {
pub qualifier: Option<Qualifier>,
/// The media type for this query, that can be known, unknown, or "all".
pub media_type: MediaQueryType,
- /// The set of expressions that this media query contains.
- pub expressions: Vec<Expression>,
+ /// The condition that this media query contains. This cannot have `or`
+ /// in the first level.
+ pub condition: Option<MediaCondition>,
}
impl ToCss for MediaQuery {
@@ -86,28 +87,23 @@ impl ToCss for MediaQuery {
//
// Otherwise, we'd serialize media queries like "(min-width:
// 40px)" in "all (min-width: 40px)", which is unexpected.
- if self.qualifier.is_some() || self.expressions.is_empty() {
+ if self.qualifier.is_some() || self.condition.is_none() {
dest.write_str("all")?;
}
},
MediaQueryType::Concrete(MediaType(ref desc)) => desc.to_css(dest)?,
}
- if self.expressions.is_empty() {
- return Ok(());
- }
+ let condition = match self.condition {
+ Some(ref c) => c,
+ None => return Ok(()),
+ };
if self.media_type != MediaQueryType::All || self.qualifier.is_some() {
dest.write_str(" and ")?;
}
- self.expressions[0].to_css(dest)?;
-
- for expr in self.expressions.iter().skip(1) {
- dest.write_str(" and ")?;
- expr.to_css(dest)?;
- }
- Ok(())
+ condition.to_css(dest)
}
}
@@ -118,7 +114,7 @@ impl MediaQuery {
Self {
qualifier: Some(Qualifier::Not),
media_type: MediaQueryType::All,
- expressions: vec![],
+ condition: None,
}
}
@@ -128,41 +124,24 @@ impl MediaQuery {
pub fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
- ) -> Result<MediaQuery, ParseError<'i>> {
- let mut expressions = vec![];
-
- let qualifier = input.try(Qualifier::parse).ok();
- let media_type = match input.try(|i| i.expect_ident_cloned()) {
- Ok(ident) => MediaQueryType::parse(&*ident).map_err(|()| {
- input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))
- })?,
- Err(_) => {
- // Media type is only optional if qualifier is not specified.
- if qualifier.is_some() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- // Without a media type, require at least one expression.
- expressions.push(Expression::parse(context, input)?);
-
- MediaQueryType::All
- },
+ ) -> Result<Self, ParseError<'i>> {
+ let (qualifier, explicit_media_type) = input.try(|input| -> Result<_, ()> {
+ let qualifier = input.try(Qualifier::parse).ok();
+ let ident = input.expect_ident().map_err(|_| ())?;
+ let media_type = MediaQueryType::parse(&ident)?;
+ Ok((qualifier, Some(media_type)))
+ }).unwrap_or_default();
+
+ let condition = if explicit_media_type.is_none() {
+ Some(MediaCondition::parse(context, input)?)
+ } else if input.try(|i| i.expect_ident_matching("and")).is_ok() {
+ Some(MediaCondition::parse_disallow_or(context, input)?)
+ } else {
+ None
};
- // Parse any subsequent expressions
- loop {
- if input
- .try(|input| input.expect_ident_matching("and"))
- .is_err()
- {
- return Ok(MediaQuery {
- qualifier,
- media_type,
- expressions,
- });
- }
- expressions.push(Expression::parse(context, input)?)
- }
+ let media_type = explicit_media_type.unwrap_or(MediaQueryType::All);
+ Ok(Self { qualifier, media_type, condition })
}
}
diff --git a/components/style/media_queries/mod.rs b/components/style/media_queries/mod.rs
index 8da14fc67e5..d27e33cc64c 100644
--- a/components/style/media_queries/mod.rs
+++ b/components/style/media_queries/mod.rs
@@ -6,13 +6,15 @@
//!
//! [mq]: https://drafts.csswg.org/mediaqueries/
+mod media_condition;
mod media_list;
mod media_query;
+pub use self::media_condition::MediaCondition;
pub use self::media_list::MediaList;
pub use self::media_query::{MediaQuery, MediaQueryType, MediaType, Qualifier};
#[cfg(feature = "servo")]
-pub use servo::media_queries::{Device, Expression};
+pub use servo::media_queries::{Device, MediaFeatureExpression};
#[cfg(feature = "gecko")]
-pub use gecko::media_queries::{Device, Expression};
+pub use gecko::media_queries::{Device, MediaFeatureExpression};
diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs
index f83e0836c91..53d0baf9aba 100644
--- a/components/style/properties/gecko.mako.rs
+++ b/components/style/properties/gecko.mako.rs
@@ -5489,8 +5489,7 @@ clip-path
use values::generics::CounterStyleOrNone;
use gecko_bindings::structs::nsStyleContentData;
use gecko_bindings::structs::nsStyleContentAttr;
- use gecko_bindings::structs::nsStyleContentType;
- use gecko_bindings::structs::nsStyleContentType::*;
+ use gecko_bindings::structs::StyleContentType;
use gecko_bindings::bindings::Gecko_ClearAndResizeStyleContents;
// Converts a string as utf16, and returns an owned, zero-terminated raw buffer.
@@ -5505,19 +5504,19 @@ clip-path
fn set_counter_function(
data: &mut nsStyleContentData,
- content_type: nsStyleContentType,
+ content_type: StyleContentType,
name: &CustomIdent,
sep: &str,
style: CounterStyleOrNone,
device: &Device,
) {
- debug_assert!(content_type == eStyleContentType_Counter ||
- content_type == eStyleContentType_Counters);
+ debug_assert!(content_type == StyleContentType::Counter ||
+ content_type == StyleContentType::Counters);
let counter_func = unsafe {
bindings::Gecko_SetCounterFunction(data, content_type).as_mut().unwrap()
};
counter_func.mIdent.assign(name.0.as_slice());
- if content_type == eStyleContentType_Counters {
+ if content_type == StyleContentType::Counters {
counter_func.mSeparator.assign_utf8(sep);
}
style.to_gecko_value(&mut counter_func.mCounterStyle, device);
@@ -5538,7 +5537,7 @@ clip-path
Gecko_ClearAndResizeStyleContents(&mut self.gecko, 1);
*self.gecko.mContents[0].mContent.mString.as_mut() = ptr::null_mut();
}
- self.gecko.mContents[0].mType = eStyleContentType_AltContent;
+ self.gecko.mContents[0].mType = StyleContentType::AltContent;
},
Content::Items(items) => {
unsafe {
@@ -5554,7 +5553,7 @@ clip-path
}
match *item {
ContentItem::String(ref value) => {
- self.gecko.mContents[i].mType = eStyleContentType_String;
+ self.gecko.mContents[i].mType = StyleContentType::String;
unsafe {
// NB: we share allocators, so doing this is fine.
*self.gecko.mContents[i].mContent.mString.as_mut() =
@@ -5562,7 +5561,7 @@ clip-path
}
}
ContentItem::Attr(ref attr) => {
- self.gecko.mContents[i].mType = eStyleContentType_Attr;
+ self.gecko.mContents[i].mType = StyleContentType::Attr;
unsafe {
// NB: we share allocators, so doing this is fine.
let maybe_ns = attr.namespace.clone();
@@ -5581,17 +5580,17 @@ clip-path
}
}
ContentItem::OpenQuote
- => self.gecko.mContents[i].mType = eStyleContentType_OpenQuote,
+ => self.gecko.mContents[i].mType = StyleContentType::OpenQuote,
ContentItem::CloseQuote
- => self.gecko.mContents[i].mType = eStyleContentType_CloseQuote,
+ => self.gecko.mContents[i].mType = StyleContentType::CloseQuote,
ContentItem::NoOpenQuote
- => self.gecko.mContents[i].mType = eStyleContentType_NoOpenQuote,
+ => self.gecko.mContents[i].mType = StyleContentType::NoOpenQuote,
ContentItem::NoCloseQuote
- => self.gecko.mContents[i].mType = eStyleContentType_NoCloseQuote,
+ => self.gecko.mContents[i].mType = StyleContentType::NoCloseQuote,
ContentItem::Counter(ref name, ref style) => {
set_counter_function(
&mut self.gecko.mContents[i],
- eStyleContentType_Counter,
+ StyleContentType::Counter,
&name,
"",
style.clone(),
@@ -5601,7 +5600,7 @@ clip-path
ContentItem::Counters(ref name, ref sep, ref style) => {
set_counter_function(
&mut self.gecko.mContents[i],
- eStyleContentType_Counters,
+ StyleContentType::Counters,
&name,
&sep,
style.clone(),
@@ -5636,7 +5635,7 @@ clip-path
pub fn clone_content(&self) -> longhands::content::computed_value::T {
use {Atom, Namespace};
use gecko::conversions::string_from_chars_pointer;
- use gecko_bindings::structs::nsStyleContentType::*;
+ use gecko_bindings::structs::StyleContentType;
use values::generics::counters::{Content, ContentItem};
use values::computed::url::ComputedImageUrl;
use values::{CustomIdent, Either};
@@ -5644,27 +5643,27 @@ clip-path
use values::specified::Attr;
if self.gecko.mContents.is_empty() {
- return Content::Normal;
+ return Content::None;
}
if self.gecko.mContents.len() == 1 &&
- self.gecko.mContents[0].mType == eStyleContentType_AltContent {
+ self.gecko.mContents[0].mType == StyleContentType::AltContent {
return Content::MozAltContent;
}
Content::Items(
self.gecko.mContents.iter().map(|gecko_content| {
match gecko_content.mType {
- eStyleContentType_OpenQuote => ContentItem::OpenQuote,
- eStyleContentType_CloseQuote => ContentItem::CloseQuote,
- eStyleContentType_NoOpenQuote => ContentItem::NoOpenQuote,
- eStyleContentType_NoCloseQuote => ContentItem::NoCloseQuote,
- eStyleContentType_String => {
+ StyleContentType::OpenQuote => ContentItem::OpenQuote,
+ StyleContentType::CloseQuote => ContentItem::CloseQuote,
+ StyleContentType::NoOpenQuote => ContentItem::NoOpenQuote,
+ StyleContentType::NoCloseQuote => ContentItem::NoCloseQuote,
+ StyleContentType::String => {
let gecko_chars = unsafe { gecko_content.mContent.mString.as_ref() };
let string = unsafe { string_from_chars_pointer(*gecko_chars) };
ContentItem::String(string.into_boxed_str())
},
- eStyleContentType_Attr => {
+ StyleContentType::Attr => {
let (namespace, attribute) = unsafe {
let s = &**gecko_content.mContent.mAttr.as_ref();
let ns = if s.mNamespaceURL.mRawPtr.is_null() {
@@ -5678,7 +5677,7 @@ clip-path
};
ContentItem::Attr(Attr { namespace, attribute })
},
- eStyleContentType_Counter | eStyleContentType_Counters => {
+ StyleContentType::Counter | StyleContentType::Counters => {
let gecko_function =
unsafe { &**gecko_content.mContent.mCounters.as_ref() };
let ident = CustomIdent(Atom::from(&*gecko_function.mIdent));
@@ -5689,14 +5688,14 @@ clip-path
Either::Second(_) =>
unreachable!("counter function shouldn't have single string type"),
};
- if gecko_content.mType == eStyleContentType_Counter {
+ if gecko_content.mType == StyleContentType::Counter {
ContentItem::Counter(ident, style)
} else {
let separator = gecko_function.mSeparator.to_string();
ContentItem::Counters(ident, separator.into_boxed_str(), style)
}
},
- eStyleContentType_Image => {
+ StyleContentType::Image => {
unsafe {
let gecko_image_request =
&**gecko_content.mContent.mImage.as_ref();
diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs
index 11a1283f1cc..e07c9bd57cb 100644
--- a/components/style/properties/helpers.mako.rs
+++ b/components/style/properties/helpers.mako.rs
@@ -856,7 +856,7 @@
elif len(maybe_size) == 1:
size = maybe_size[0]
def phys_ident(side, phy_side):
- return to_rust_ident(name.replace(side, phy_side).replace("offset-", ""))
+ return to_rust_ident(name.replace(side, phy_side).replace("inset-", ""))
%>
% if side is not None:
use logical_geometry::PhysicalSide;
diff --git a/components/style/properties/longhands/position.mako.rs b/components/style/properties/longhands/position.mako.rs
index a99cebfdba9..3b34047fc5a 100644
--- a/components/style/properties/longhands/position.mako.rs
+++ b/components/style/properties/longhands/position.mako.rs
@@ -21,14 +21,15 @@
servo_restyle_damage="reflow_out_of_flow"
)}
% endfor
-// offset-* logical properties, map to "top" / "left" / "bottom" / "right"
+// inset-* logical properties, map to "top" / "left" / "bottom" / "right"
% for side in LOGICAL_SIDES:
${helpers.predefined_type(
- "offset-%s" % side,
+ "inset-%s" % side,
"LengthOrPercentageOrAuto",
"computed::LengthOrPercentageOrAuto::Auto",
- spec="https://drafts.csswg.org/css-logical-props/#propdef-offset-%s" % side,
+ spec="https://drafts.csswg.org/css-logical-props/#propdef-inset-%s" % side,
flags="GETCS_NEEDS_LAYOUT_FLUSH",
+ alias="offset-%s:layout.css.offset-logical-properties.enabled" % side,
animation_value_type="ComputedValue",
logical=True,
)}
diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs
index b6a8879e106..3e51c1a6c71 100644
--- a/components/style/properties/properties.mako.rs
+++ b/components/style/properties/properties.mako.rs
@@ -100,6 +100,7 @@ pub mod longhands {
% for style_struct in data.style_structs:
include!("${repr(os.path.join(OUT_DIR, 'longhands/{}.rs'.format(style_struct.name_lower)))[1:-1]}");
% endfor
+ pub const ANIMATABLE_PROPERTY_COUNT: usize = ${sum(1 for prop in data.longhands if prop.animatable)};
}
macro_rules! unwrap_or_initial {
@@ -177,7 +178,6 @@ pub mod shorthands {
data.declare_shorthand(
"all",
logical_longhands + other_longhands,
- gecko_pref="layout.css.all-shorthand.enabled",
spec="https://drafts.csswg.org/css-cascade-3/#all-shorthand"
)
%>
@@ -410,6 +410,7 @@ pub struct NonCustomPropertyId(usize);
impl NonCustomPropertyId {
#[cfg(feature = "gecko")]
+ #[inline]
fn to_nscsspropertyid(self) -> nsCSSPropertyID {
static MAP: [nsCSSPropertyID; ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())}] = [
% for property in data.longhands + data.shorthands + data.all_aliases():
@@ -835,6 +836,8 @@ bitflags! {
* they can be checked in the C++ side via ServoCSSPropList.h. */
/// This property can be animated on the compositor.
const CAN_ANIMATE_ON_COMPOSITOR = 0;
+ /// This shorthand property is accessible from getComputedStyle.
+ const SHORTHAND_IN_GETCS = 0;
}
}
@@ -1708,6 +1711,9 @@ impl PropertyId {
}
/// Returns a property id from Gecko's nsCSSPropertyID.
+ ///
+ /// TODO(emilio): We should be able to make this a single integer cast to
+ /// `NonCustomPropertyId`.
#[cfg(feature = "gecko")]
#[allow(non_upper_case_globals)]
pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()> {
diff --git a/components/style/properties/shorthands/background.mako.rs b/components/style/properties/shorthands/background.mako.rs
index b16c9918a24..dff9319cb30 100644
--- a/components/style/properties/shorthands/background.mako.rs
+++ b/components/style/properties/shorthands/background.mako.rs
@@ -196,6 +196,7 @@
</%helpers:shorthand>
<%helpers:shorthand name="background-position"
+ flags="SHORTHAND_IN_GETCS"
sub_properties="background-position-x background-position-y"
spec="https://drafts.csswg.org/css-backgrounds-4/#the-background-position">
use properties::longhands::{background_position_x, background_position_y};
diff --git a/components/style/properties/shorthands/box.mako.rs b/components/style/properties/shorthands/box.mako.rs
index 842dfbd7229..1425f012854 100644
--- a/components/style/properties/shorthands/box.mako.rs
+++ b/components/style/properties/shorthands/box.mako.rs
@@ -6,6 +6,7 @@
<%helpers:shorthand
name="overflow"
+ flags="SHORTHAND_IN_GETCS"
sub_properties="overflow-x overflow-y"
spec="https://drafts.csswg.org/css-overflow/#propdef-overflow"
>
diff --git a/components/style/properties/shorthands/font.mako.rs b/components/style/properties/shorthands/font.mako.rs
index a53f899851c..75095bcebc2 100644
--- a/components/style/properties/shorthands/font.mako.rs
+++ b/components/style/properties/shorthands/font.mako.rs
@@ -285,6 +285,7 @@
</%helpers:shorthand>
<%helpers:shorthand name="font-variant"
+ flags="SHORTHAND_IN_GETCS"
sub_properties="font-variant-caps
${'font-variant-alternates' if product == 'gecko' else ''}
${'font-variant-east-asian' if product == 'gecko' else ''}
diff --git a/components/style/properties/shorthands/svg.mako.rs b/components/style/properties/shorthands/svg.mako.rs
index aa73c77e46b..03027359dd4 100644
--- a/components/style/properties/shorthands/svg.mako.rs
+++ b/components/style/properties/shorthands/svg.mako.rs
@@ -5,6 +5,7 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
<%helpers:shorthand name="mask" products="gecko" extra_prefixes="webkit"
+ flags="SHORTHAND_IN_GETCS"
sub_properties="mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x
mask-position-y mask-size mask-image"
spec="https://drafts.fxtf.org/css-masking/#propdef-mask">
@@ -182,6 +183,7 @@
</%helpers:shorthand>
<%helpers:shorthand name="mask-position" products="gecko" extra_prefixes="webkit"
+ flags="SHORTHAND_IN_GETCS"
sub_properties="mask-position-x mask-position-y"
spec="https://drafts.csswg.org/css-masks-4/#the-mask-position">
use properties::longhands::{mask_position_x,mask_position_y};
diff --git a/components/style/properties/shorthands/text.mako.rs b/components/style/properties/shorthands/text.mako.rs
index 18a0786494d..e83d0b67544 100644
--- a/components/style/properties/shorthands/text.mako.rs
+++ b/components/style/properties/shorthands/text.mako.rs
@@ -5,6 +5,7 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
<%helpers:shorthand name="text-decoration"
+ flags="SHORTHAND_IN_GETCS"
sub_properties="text-decoration-line
${' text-decoration-style text-decoration-color' if product == 'gecko' else ''}"
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration">
diff --git a/components/style/selector_map.rs b/components/style/selector_map.rs
index 3b9b6f20184..3b0dcca9525 100644
--- a/components/style/selector_map.rs
+++ b/components/style/selector_map.rs
@@ -5,7 +5,7 @@
//! A data structure to efficiently index structs containing selectors by local
//! name, ids and hash.
-use {Atom, LocalName, WeakAtom};
+use {Atom, LocalName, Namespace, WeakAtom};
use applicable_declarations::ApplicableDeclarationList;
use context::QuirksMode;
use dom::TElement;
@@ -102,6 +102,8 @@ pub struct SelectorMap<T: 'static> {
pub class_hash: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[T; 1]>>,
/// A hash from local name to rules which contain that local name selector.
pub local_name_hash: PrecomputedHashMap<LocalName, SmallVec<[T; 1]>>,
+ /// A hash from namespace to rules which contain that namespace selector.
+ pub namespace_hash: PrecomputedHashMap<Namespace, SmallVec<[T; 1]>>,
/// Rules that don't have ID, class, or element selectors.
pub other: SmallVec<[T; 1]>,
/// The number of entries in this map.
@@ -125,6 +127,7 @@ impl<T: 'static> SelectorMap<T> {
id_hash: MaybeCaseInsensitiveHashMap::new(),
class_hash: MaybeCaseInsensitiveHashMap::new(),
local_name_hash: HashMap::default(),
+ namespace_hash: HashMap::default(),
other: SmallVec::new(),
count: 0,
}
@@ -135,6 +138,7 @@ impl<T: 'static> SelectorMap<T> {
self.id_hash.clear();
self.class_hash.clear();
self.local_name_hash.clear();
+ self.namespace_hash.clear();
self.other.clear();
self.count = 0;
}
@@ -217,6 +221,18 @@ impl SelectorMap<Rule> {
)
}
+ if let Some(rules) = self.namespace_hash.get(rule_hash_target.namespace()) {
+ SelectorMap::get_matching_rules(
+ element,
+ rules,
+ matching_rules_list,
+ context,
+ flags_setter,
+ cascade_level,
+ shadow_cascade_order,
+ )
+ }
+
SelectorMap::get_matching_rules(
element,
&self.other,
@@ -261,7 +277,8 @@ impl SelectorMap<Rule> {
}
impl<T: SelectorMapEntry> SelectorMap<T> {
- /// Inserts into the correct hash, trying id, class, and localname.
+ /// Inserts into the correct hash, trying id, class, localname and
+ /// namespace.
pub fn insert(
&mut self,
entry: T,
@@ -298,13 +315,17 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
.try_entry(name.clone())?
.or_insert_with(SmallVec::new)
},
+ Bucket::Namespace(url) => self.namespace_hash
+ .try_entry(url.clone())?
+ .or_insert_with(SmallVec::new),
Bucket::Universal => &mut self.other,
};
vector.try_push(entry)
}
- /// Looks up entries by id, class, local name, and other (in order).
+ /// Looks up entries by id, class, local name, namespace, and other (in
+ /// order).
///
/// Each entry is passed to the callback, which returns true to continue
/// iterating entries, or false to terminate the lookup.
@@ -319,7 +340,6 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
E: TElement,
F: FnMut(&'a T) -> bool,
{
- // Id.
if let Some(id) = element.id() {
if let Some(v) = self.id_hash.get(id, quirks_mode) {
for entry in v.iter() {
@@ -330,7 +350,6 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
}
}
- // Class.
let mut done = false;
element.each_class(|class| {
if !done {
@@ -348,7 +367,6 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
return false;
}
- // Local name.
if let Some(v) = self.local_name_hash.get(element.local_name()) {
for entry in v.iter() {
if !f(&entry) {
@@ -357,7 +375,14 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
}
}
- // Other.
+ if let Some(v) = self.namespace_hash.get(element.namespace()) {
+ for entry in v.iter() {
+ if !f(&entry) {
+ return false;
+ }
+ }
+ }
+
for entry in self.other.iter() {
if !f(&entry) {
return false;
@@ -425,6 +450,7 @@ enum Bucket<'a> {
name: &'a LocalName,
lower_name: &'a LocalName,
},
+ Namespace(&'a Namespace),
Universal,
}
@@ -436,6 +462,8 @@ fn specific_bucket_for<'a>(component: &'a Component<SelectorImpl>) -> Bucket<'a>
name: &selector.name,
lower_name: &selector.lower_name,
},
+ Component::Namespace(_, ref url) |
+ Component::DefaultNamespace(ref url) => Bucket::Namespace(url),
// ::slotted(..) isn't a normal pseudo-element, so we can insert it on
// the rule hash normally without much problem. For example, in a
// selector like:
@@ -470,7 +498,7 @@ fn find_bucket<'a>(mut iter: SelectorIter<'a, SelectorImpl>) -> Bucket<'a> {
// We basically want to find the most specific bucket,
// where:
//
- // id > class > local name > universal.
+ // id > class > local name > namespace > universal.
//
for ss in &mut iter {
let new_bucket = specific_bucket_for(ss);
@@ -480,10 +508,15 @@ fn find_bucket<'a>(mut iter: SelectorIter<'a, SelectorImpl>) -> Bucket<'a> {
current_bucket = new_bucket;
},
Bucket::LocalName { .. } => {
- if matches!(current_bucket, Bucket::Universal) {
+ if matches!(current_bucket, Bucket::Universal | Bucket::Namespace(..)) {
current_bucket = new_bucket;
}
},
+ Bucket::Namespace(..) => {
+ if matches!(current_bucket, Bucket::Universal) {
+ current_bucket = new_bucket;
+ }
+ }
Bucket::Universal => {},
}
}
diff --git a/components/style/servo/media_queries.rs b/components/style/servo/media_queries.rs
index 336971f98df..ba31cfcaf4b 100644
--- a/components/style/servo/media_queries.rs
+++ b/components/style/servo/media_queries.rs
@@ -170,9 +170,9 @@ pub enum ExpressionKind {
/// <http://dev.w3.org/csswg/mediaqueries-3/#media1>
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct Expression(pub ExpressionKind);
+pub struct MediaFeatureExpression(pub ExpressionKind);
-impl Expression {
+impl MediaFeatureExpression {
/// The kind of expression we're, just for unit testing.
///
/// Eventually this will become servo-only.
@@ -183,34 +183,43 @@ impl Expression {
/// Parse a media expression of the form:
///
/// ```
- /// (media-feature: media-value)
+ /// media-feature: media-value
/// ```
///
- /// Only supports width and width ranges for now.
+ /// Only supports width ranges for now.
pub fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
input.expect_parenthesis_block()?;
input.parse_nested_block(|input| {
- let name = input.expect_ident_cloned()?;
- input.expect_colon()?;
- // TODO: Handle other media features
- Ok(Expression(match_ignore_ascii_case! { &name,
- "min-width" => {
- ExpressionKind::Width(Range::Min(specified::Length::parse_non_negative(context, input)?))
- },
- "max-width" => {
- ExpressionKind::Width(Range::Max(specified::Length::parse_non_negative(context, input)?))
- },
- "width" => {
- ExpressionKind::Width(Range::Eq(specified::Length::parse_non_negative(context, input)?))
- },
- _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
- }))
+ Self::parse_in_parenthesis_block(context, input)
})
}
+ /// Parse a media range expression where we've already consumed the
+ /// parenthesis.
+ pub fn parse_in_parenthesis_block<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ let name = input.expect_ident_cloned()?;
+ input.expect_colon()?;
+ // TODO: Handle other media features
+ Ok(MediaFeatureExpression(match_ignore_ascii_case! { &name,
+ "min-width" => {
+ ExpressionKind::Width(Range::Min(specified::Length::parse_non_negative(context, input)?))
+ },
+ "max-width" => {
+ ExpressionKind::Width(Range::Max(specified::Length::parse_non_negative(context, input)?))
+ },
+ "width" => {
+ ExpressionKind::Width(Range::Eq(specified::Length::parse_non_negative(context, input)?))
+ },
+ _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
+ }))
+ }
+
/// Evaluate this expression and return whether it matches the current
/// device.
pub fn matches(&self, device: &Device, quirks_mode: QuirksMode) -> bool {
@@ -228,7 +237,7 @@ impl Expression {
}
}
-impl ToCss for Expression {
+impl ToCss for MediaFeatureExpression {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
@@ -246,8 +255,8 @@ impl ToCss for Expression {
/// An enumeration that represents a ranged value.
///
-/// Only public for testing, implementation details of `Expression` may change
-/// for Stylo.
+/// Only public for testing, implementation details of `MediaFeatureExpression`
+/// may change for Stylo.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
pub enum Range<T> {
diff --git a/components/style/stylesheets/font_feature_values_rule.rs b/components/style/stylesheets/font_feature_values_rule.rs
index 8ce16e2ccf9..c70a46c1c9d 100644
--- a/components/style/stylesheets/font_feature_values_rule.rs
+++ b/components/style/stylesheets/font_feature_values_rule.rs
@@ -428,6 +428,7 @@ macro_rules! font_feature_values_blocks {
fn parse_block<'t>(
&mut self,
prelude: BlockType,
+ _location: SourceLocation,
input: &mut Parser<'i, 't>
) -> Result<Self::AtRule, ParseError<'i>> {
debug_assert_eq!(self.context.rule_type(), CssRuleType::FontFeatureValues);
diff --git a/components/style/stylesheets/keyframes_rule.rs b/components/style/stylesheets/keyframes_rule.rs
index a0cb5fa0b1d..85dc00bb9af 100644
--- a/components/style/stylesheets/keyframes_rule.rs
+++ b/components/style/stylesheets/keyframes_rule.rs
@@ -509,14 +509,8 @@ impl<'a, 'i> AtRuleParser<'i> for KeyframeListParser<'a> {
type Error = StyleParseErrorKind<'i>;
}
-/// A wrapper to wraps the KeyframeSelector with its source location
-struct KeyframeSelectorParserPrelude {
- selector: KeyframeSelector,
- source_location: SourceLocation,
-}
-
impl<'a, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a> {
- type Prelude = KeyframeSelectorParserPrelude;
+ type Prelude = KeyframeSelector;
type QualifiedRule = Arc<Locked<Keyframe>>;
type Error = StyleParseErrorKind<'i>;
@@ -525,27 +519,21 @@ impl<'a, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a> {
input: &mut Parser<'i, 't>,
) -> Result<Self::Prelude, ParseError<'i>> {
let start_position = input.position();
- let start_location = input.current_source_location();
- match KeyframeSelector::parse(input) {
- Ok(sel) => Ok(KeyframeSelectorParserPrelude {
- selector: sel,
- source_location: start_location,
- }),
- Err(e) => {
- let location = e.location;
- let error = ContextualParseError::InvalidKeyframeRule(
- input.slice_from(start_position),
- e.clone(),
+ KeyframeSelector::parse(input).map_err(|e| {
+ let location = e.location;
+ let error = ContextualParseError::InvalidKeyframeRule(
+ input.slice_from(start_position),
+ e.clone(),
);
- self.context.log_css_error(location, error);
- Err(e)
- },
- }
+ self.context.log_css_error(location, error);
+ e
+ })
}
fn parse_block<'t>(
&mut self,
- prelude: Self::Prelude,
+ selector: Self::Prelude,
+ source_location: SourceLocation,
input: &mut Parser<'i, 't>,
) -> Result<Self::QualifiedRule, ParseError<'i>> {
let context = ParserContext::new_with_rule_type(
@@ -580,9 +568,9 @@ impl<'a, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a> {
// `parse_important` is not called here, `!important` is not allowed in keyframe blocks.
}
Ok(Arc::new(self.shared_lock.wrap(Keyframe {
- selector: prelude.selector,
+ selector,
block: Arc::new(self.shared_lock.wrap(block)),
- source_location: prelude.source_location,
+ source_location,
})))
}
}
diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs
index 41d87aa07a4..d2f9b691ad4 100644
--- a/components/style/stylesheets/rule_parser.rs
+++ b/components/style/stylesheets/rule_parser.rs
@@ -146,31 +146,31 @@ pub enum VendorPrefix {
/// A rule prelude for at-rule with block.
pub enum AtRuleBlockPrelude {
/// A @font-face rule prelude.
- FontFace(SourceLocation),
+ FontFace,
/// A @font-feature-values rule prelude, with its FamilyName list.
- FontFeatureValues(Vec<FamilyName>, SourceLocation),
+ FontFeatureValues(Vec<FamilyName>),
/// A @counter-style rule prelude, with its counter style name.
- CounterStyle(CustomIdent, SourceLocation),
+ CounterStyle(CustomIdent),
/// A @media rule prelude, with its media queries.
- Media(Arc<Locked<MediaList>>, SourceLocation),
+ Media(Arc<Locked<MediaList>>),
/// An @supports rule, with its conditional
- Supports(SupportsCondition, SourceLocation),
+ Supports(SupportsCondition),
/// A @viewport rule prelude.
Viewport,
/// A @keyframes rule, with its animation name and vendor prefix if exists.
- Keyframes(KeyframesName, Option<VendorPrefix>, SourceLocation),
+ Keyframes(KeyframesName, Option<VendorPrefix>),
/// A @page rule prelude.
- Page(SourceLocation),
+ Page,
/// A @document rule, with its conditional.
- Document(DocumentCondition, SourceLocation),
+ Document(DocumentCondition),
}
/// A rule prelude for at-rule without block.
pub enum AtRuleNonBlockPrelude {
/// A @import rule prelude.
- Import(CssUrl, Arc<Locked<MediaList>>, SourceLocation),
+ Import(CssUrl, Arc<Locked<MediaList>>),
/// A @namespace rule prelude.
- Namespace(Option<Prefix>, Namespace, SourceLocation),
+ Namespace(Option<Prefix>, Namespace),
}
impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
@@ -184,7 +184,6 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>,
) -> Result<AtRuleType<AtRuleNonBlockPrelude, AtRuleBlockPrelude>, ParseError<'i>> {
- let location = input.current_source_location();
match_ignore_ascii_case! { &*name,
"import" => {
if !self.check_state(State::Imports) {
@@ -197,7 +196,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
let media = MediaList::parse(&self.context, input);
let media = Arc::new(self.shared_lock.wrap(media));
- let prelude = AtRuleNonBlockPrelude::Import(url, media, location);
+ let prelude = AtRuleNonBlockPrelude::Import(url, media);
return Ok(AtRuleType::WithoutBlock(prelude));
},
"namespace" => {
@@ -215,7 +214,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
Err(e) => return Err(e.into()),
};
let url = Namespace::from(maybe_namespace.as_ref());
- let prelude = AtRuleNonBlockPrelude::Namespace(prefix, url, location);
+ let prelude = AtRuleNonBlockPrelude::Namespace(prefix, url);
return Ok(AtRuleType::WithoutBlock(prelude));
},
// @charset is removed by rust-cssparser if it’s the first rule in the stylesheet
@@ -238,24 +237,29 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
fn parse_block<'t>(
&mut self,
prelude: AtRuleBlockPrelude,
+ location: SourceLocation,
input: &mut Parser<'i, 't>,
) -> Result<CssRule, ParseError<'i>> {
- AtRuleParser::parse_block(&mut self.nested(), prelude, input).map(|rule| {
+ AtRuleParser::parse_block(&mut self.nested(), prelude, location, input).map(|rule| {
self.state = State::Body;
rule
})
}
#[inline]
- fn rule_without_block(&mut self, prelude: AtRuleNonBlockPrelude) -> CssRule {
+ fn rule_without_block(
+ &mut self,
+ prelude: AtRuleNonBlockPrelude,
+ source_location: SourceLocation,
+ ) -> CssRule {
match prelude {
- AtRuleNonBlockPrelude::Import(url, media, location) => {
+ AtRuleNonBlockPrelude::Import(url, media) => {
let loader = self.loader
.expect("Expected a stylesheet loader for @import");
let import_rule = loader.request_stylesheet(
url,
- location,
+ source_location,
&self.context,
&self.shared_lock,
media,
@@ -264,7 +268,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
self.state = State::Imports;
CssRule::Import(import_rule)
},
- AtRuleNonBlockPrelude::Namespace(prefix, url, source_location) => {
+ AtRuleNonBlockPrelude::Namespace(prefix, url) => {
let prefix = if let Some(prefix) = prefix {
self.namespaces.prefixes.insert(prefix.clone(), url.clone());
Some(prefix)
@@ -284,13 +288,8 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
}
}
-pub struct QualifiedRuleParserPrelude {
- selectors: SelectorList<SelectorImpl>,
- source_location: SourceLocation,
-}
-
impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a> {
- type Prelude = QualifiedRuleParserPrelude;
+ type Prelude = SelectorList<SelectorImpl>;
type QualifiedRule = CssRule;
type Error = StyleParseErrorKind<'i>;
@@ -298,7 +297,7 @@ impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a> {
fn parse_prelude<'t>(
&mut self,
input: &mut Parser<'i, 't>,
- ) -> Result<QualifiedRuleParserPrelude, ParseError<'i>> {
+ ) -> Result<Self::Prelude, ParseError<'i>> {
if !self.check_state(State::Body) {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
@@ -309,10 +308,16 @@ impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a> {
#[inline]
fn parse_block<'t>(
&mut self,
- prelude: QualifiedRuleParserPrelude,
+ prelude: Self::Prelude,
+ location: SourceLocation,
input: &mut Parser<'i, 't>,
) -> Result<CssRule, ParseError<'i>> {
- QualifiedRuleParser::parse_block(&mut self.nested(), prelude, input).map(|result| {
+ QualifiedRuleParser::parse_block(
+ &mut self.nested(),
+ prelude,
+ location,
+ input,
+ ).map(|result| {
self.state = State::Body;
result
})
@@ -373,20 +378,18 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>,
) -> Result<AtRuleType<AtRuleNonBlockPrelude, AtRuleBlockPrelude>, ParseError<'i>> {
- let location = input.current_source_location();
-
match_ignore_ascii_case! { &*name,
"media" => {
let media_queries = MediaList::parse(self.context, input);
let arc = Arc::new(self.shared_lock.wrap(media_queries));
- Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Media(arc, location)))
+ Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Media(arc)))
},
"supports" => {
let cond = SupportsCondition::parse(input)?;
- Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Supports(cond, location)))
+ Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Supports(cond)))
},
"font-face" => {
- Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::FontFace(location)))
+ Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::FontFace))
},
"font-feature-values" => {
if !cfg!(feature = "gecko") {
@@ -394,7 +397,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
return Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone())))
}
let family_names = parse_family_name_list(self.context, input)?;
- Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::FontFeatureValues(family_names, location)))
+ Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::FontFeatureValues(family_names)))
},
"counter-style" => {
if !cfg!(feature = "gecko") {
@@ -402,7 +405,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
return Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone())))
}
let name = parse_counter_style_name_definition(input)?;
- Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::CounterStyle(name, location)))
+ Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::CounterStyle(name)))
},
"viewport" => {
if viewport_rule::enabled() {
@@ -426,11 +429,11 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
}
let name = KeyframesName::parse(self.context, input)?;
- Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Keyframes(name, prefix, location)))
+ Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Keyframes(name, prefix)))
},
"page" => {
if cfg!(feature = "gecko") {
- Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Page(location)))
+ Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Page))
} else {
Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone())))
}
@@ -443,7 +446,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
}
let cond = DocumentCondition::parse(self.context, input)?;
- Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Document(cond, location)))
+ Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Document(cond)))
},
_ => Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone())))
}
@@ -452,10 +455,11 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
fn parse_block<'t>(
&mut self,
prelude: AtRuleBlockPrelude,
+ source_location: SourceLocation,
input: &mut Parser<'i, 't>,
) -> Result<CssRule, ParseError<'i>> {
match prelude {
- AtRuleBlockPrelude::FontFace(location) => {
+ AtRuleBlockPrelude::FontFace => {
let context = ParserContext::new_with_rule_type(
self.context,
CssRuleType::FontFace,
@@ -463,10 +467,10 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
);
Ok(CssRule::FontFace(Arc::new(self.shared_lock.wrap(
- parse_font_face_block(&context, input, location).into(),
+ parse_font_face_block(&context, input, source_location).into(),
))))
},
- AtRuleBlockPrelude::FontFeatureValues(family_names, location) => {
+ AtRuleBlockPrelude::FontFeatureValues(family_names) => {
let context = ParserContext::new_with_rule_type(
self.context,
CssRuleType::FontFeatureValues,
@@ -478,11 +482,11 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
&context,
input,
family_names,
- location,
+ source_location,
),
))))
},
- AtRuleBlockPrelude::CounterStyle(name, location) => {
+ AtRuleBlockPrelude::CounterStyle(name) => {
let context = ParserContext::new_with_rule_type(
self.context,
CssRuleType::CounterStyle,
@@ -495,19 +499,19 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
name,
&context,
input,
- location,
+ source_location,
)?.into(),
),
)))
},
- AtRuleBlockPrelude::Media(media_queries, source_location) => {
+ AtRuleBlockPrelude::Media(media_queries) => {
Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
media_queries,
rules: self.parse_nested_rules(input, CssRuleType::Media),
source_location,
}))))
},
- AtRuleBlockPrelude::Supports(condition, source_location) => {
+ AtRuleBlockPrelude::Supports(condition) => {
let eval_context = ParserContext::new_with_rule_type(
self.context,
CssRuleType::Style,
@@ -535,7 +539,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
ViewportRule::parse(&context, input)?,
))))
},
- AtRuleBlockPrelude::Keyframes(name, vendor_prefix, source_location) => {
+ AtRuleBlockPrelude::Keyframes(name, vendor_prefix) => {
let context = ParserContext::new_with_rule_type(
self.context,
CssRuleType::Keyframes,
@@ -555,7 +559,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
},
))))
},
- AtRuleBlockPrelude::Page(source_location) => {
+ AtRuleBlockPrelude::Page => {
let context = ParserContext::new_with_rule_type(
self.context,
CssRuleType::Page,
@@ -569,7 +573,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
source_location,
}))))
},
- AtRuleBlockPrelude::Document(condition, source_location) => {
+ AtRuleBlockPrelude::Document(condition) => {
if !cfg!(feature = "gecko") {
unreachable!()
}
@@ -586,39 +590,37 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
}
impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> {
- type Prelude = QualifiedRuleParserPrelude;
+ type Prelude = SelectorList<SelectorImpl>;
type QualifiedRule = CssRule;
type Error = StyleParseErrorKind<'i>;
fn parse_prelude<'t>(
&mut self,
input: &mut Parser<'i, 't>,
- ) -> Result<QualifiedRuleParserPrelude, ParseError<'i>> {
+ ) -> Result<Self::Prelude, ParseError<'i>> {
let selector_parser = SelectorParser {
stylesheet_origin: self.stylesheet_origin,
namespaces: self.namespaces,
url_data: Some(self.context.url_data),
};
-
- let source_location = input.current_source_location();
- let selectors = SelectorList::parse(&selector_parser, input)?;
-
- Ok(QualifiedRuleParserPrelude { selectors, source_location, })
+ SelectorList::parse(&selector_parser, input)
}
fn parse_block<'t>(
&mut self,
- prelude: QualifiedRuleParserPrelude,
+ selectors: Self::Prelude,
+ source_location: SourceLocation,
input: &mut Parser<'i, 't>,
) -> Result<CssRule, ParseError<'i>> {
let context =
ParserContext::new_with_rule_type(self.context, CssRuleType::Style, self.namespaces);
let declarations = parse_property_declaration_list(&context, input);
+ let block = Arc::new(self.shared_lock.wrap(declarations));
Ok(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule {
- selectors: prelude.selectors,
- block: Arc::new(self.shared_lock.wrap(declarations)),
- source_location: prelude.source_location,
+ selectors,
+ block,
+ source_location,
}))))
}
}
diff --git a/components/style/values/animated/color.rs b/components/style/values/animated/color.rs
index 1b01a86457a..7f6b4cd9d43 100644
--- a/components/style/values/animated/color.rs
+++ b/components/style/values/animated/color.rs
@@ -186,7 +186,7 @@ impl ComputeSquaredDistance for Color {
(Foreground, Foreground) => SquaredDistance::from_sqrt(0.),
(Numeric(c1), Numeric(c2)) => c1.compute_squared_distance(&c2)?,
(Foreground, Numeric(color)) | (Numeric(color), Foreground) => {
- // `computed_squared_distance` is symmetic.
+ // `computed_squared_distance` is symmetric.
color.compute_squared_distance(&RGBA::transparent())?
+ SquaredDistance::from_sqrt(1.)
}
@@ -207,7 +207,6 @@ impl ComputeSquaredDistance for Color {
impl ToAnimatedZero for Color {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
- // FIXME(nox): This does not look correct to me.
- Err(())
+ Ok(RGBA::transparent().into())
}
}
diff --git a/components/style/values/animated/effects.rs b/components/style/values/animated/effects.rs
index d0463365fc2..a201ec0549e 100644
--- a/components/style/values/animated/effects.rs
+++ b/components/style/values/animated/effects.rs
@@ -6,7 +6,7 @@
#[cfg(not(feature = "gecko"))]
use values::Impossible;
-use values::animated::color::RGBA;
+use values::animated::color::Color;
use values::computed::{Angle, Number};
use values::computed::length::Length;
#[cfg(feature = "gecko")]
@@ -17,7 +17,7 @@ use values::generics::effects::Filter as GenericFilter;
use values::generics::effects::SimpleShadow as GenericSimpleShadow;
/// An animated value for a single `box-shadow`.
-pub type BoxShadow = GenericBoxShadow<Option<RGBA>, Length, Length, Length>;
+pub type BoxShadow = GenericBoxShadow<Color, Length, Length, Length>;
/// An animated value for a single `filter`.
#[cfg(feature = "gecko")]
@@ -28,7 +28,7 @@ pub type Filter = GenericFilter<Angle, Number, Length, SimpleShadow, ComputedUrl
pub type Filter = GenericFilter<Angle, Number, Length, Impossible, Impossible>;
/// An animated value for the `drop-shadow()` filter.
-pub type SimpleShadow = GenericSimpleShadow<Option<RGBA>, Length, Length>;
+pub type SimpleShadow = GenericSimpleShadow<Color, Length, Length>;
impl ComputeSquaredDistance for BoxShadow {
#[inline]
diff --git a/components/style/values/computed/effects.rs b/components/style/values/computed/effects.rs
index 3d804d42605..07ac6441b6c 100644
--- a/components/style/values/computed/effects.rs
+++ b/components/style/values/computed/effects.rs
@@ -7,7 +7,7 @@
#[cfg(not(feature = "gecko"))]
use values::Impossible;
use values::computed::{Angle, NonNegativeNumber};
-use values::computed::color::RGBAColor;
+use values::computed::color::Color;
use values::computed::length::{Length, NonNegativeLength};
#[cfg(feature = "gecko")]
use values::computed::url::ComputedUrl;
@@ -16,7 +16,7 @@ use values::generics::effects::Filter as GenericFilter;
use values::generics::effects::SimpleShadow as GenericSimpleShadow;
/// A computed value for a single shadow of the `box-shadow` property.
-pub type BoxShadow = GenericBoxShadow<Option<RGBAColor>, Length, NonNegativeLength, Length>;
+pub type BoxShadow = GenericBoxShadow<Color, Length, NonNegativeLength, Length>;
/// A computed value for a single `filter`.
#[cfg(feature = "gecko")]
@@ -27,4 +27,4 @@ pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Sim
pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Impossible, Impossible>;
/// A computed value for the `drop-shadow()` filter.
-pub type SimpleShadow = GenericSimpleShadow<Option<RGBAColor>, Length, NonNegativeLength>;
+pub type SimpleShadow = GenericSimpleShadow<Color, Length, NonNegativeLength>;
diff --git a/components/style/values/generics/counters.rs b/components/style/values/generics/counters.rs
index 66da2625ffd..779d56d65ee 100644
--- a/components/style/values/generics/counters.rs
+++ b/components/style/values/generics/counters.rs
@@ -122,7 +122,6 @@ impl<ImageUrl> Content<ImageUrl> {
pub fn normal() -> Self {
Content::Normal
}
-
}
/// Items for the `content` property.
diff --git a/components/style/values/specified/effects.rs b/components/style/values/specified/effects.rs
index f10b53fa788..c3304b05af0 100644
--- a/components/style/values/specified/effects.rs
+++ b/components/style/values/specified/effects.rs
@@ -17,14 +17,14 @@ use values::generics::effects::BoxShadow as GenericBoxShadow;
use values::generics::effects::Filter as GenericFilter;
use values::generics::effects::SimpleShadow as GenericSimpleShadow;
use values::specified::{Angle, NumberOrPercentage};
-use values::specified::color::RGBAColor;
+use values::specified::color::Color;
use values::specified::length::{Length, NonNegativeLength};
#[cfg(feature = "gecko")]
use values::specified::url::SpecifiedUrl;
/// A specified value for a single shadow of the `box-shadow` property.
pub type BoxShadow =
- GenericBoxShadow<Option<RGBAColor>, Length, Option<NonNegativeLength>, Option<Length>>;
+ GenericBoxShadow<Option<Color>, Length, Option<NonNegativeLength>, Option<Length>>;
/// A specified value for a single `filter`.
#[cfg(feature = "gecko")]
@@ -93,7 +93,7 @@ impl ToComputedValue for Factor {
}
/// A specified value for the `drop-shadow()` filter.
-pub type SimpleShadow = GenericSimpleShadow<Option<RGBAColor>, Length, Option<NonNegativeLength>>;
+pub type SimpleShadow = GenericSimpleShadow<Option<Color>, Length, Option<NonNegativeLength>>;
impl Parse for BoxShadow {
fn parse<'i, 't>(
@@ -135,7 +135,7 @@ impl Parse for BoxShadow {
}
}
if color.is_none() {
- if let Ok(value) = input.try(|i| RGBAColor::parse(context, i)) {
+ if let Ok(value) = input.try(|i| Color::parse(context, i)) {
color = Some(value);
continue;
}
@@ -249,16 +249,18 @@ impl Parse for SimpleShadow {
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
- let color = input.try(|i| RGBAColor::parse(context, i)).ok();
+ let color = input.try(|i| Color::parse(context, i)).ok();
let horizontal = Length::parse(context, input)?;
let vertical = Length::parse(context, input)?;
let blur = input.try(|i| Length::parse_non_negative(context, i)).ok();
- let color = color.or_else(|| input.try(|i| RGBAColor::parse(context, i)).ok());
+ let blur = blur.map(NonNegative::<Length>);
+ let color = color.or_else(|| input.try(|i| Color::parse(context, i)).ok());
+
Ok(SimpleShadow {
- color: color,
- horizontal: horizontal,
- vertical: vertical,
- blur: blur.map(NonNegative::<Length>),
+ color,
+ horizontal,
+ vertical,
+ blur,
})
}
}
@@ -269,7 +271,10 @@ impl ToComputedValue for SimpleShadow {
#[inline]
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
ComputedSimpleShadow {
- color: self.color.to_computed_value(context),
+ color: self.color
+ .as_ref()
+ .unwrap_or(&Color::currentcolor())
+ .to_computed_value(context),
horizontal: self.horizontal.to_computed_value(context),
vertical: self.vertical.to_computed_value(context),
blur: self.blur
@@ -282,7 +287,7 @@ impl ToComputedValue for SimpleShadow {
#[inline]
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
SimpleShadow {
- color: ToComputedValue::from_computed_value(&computed.color),
+ color: Some(ToComputedValue::from_computed_value(&computed.color)),
horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
vertical: ToComputedValue::from_computed_value(&computed.vertical),
blur: Some(ToComputedValue::from_computed_value(&computed.blur)),
diff --git a/components/style/values/specified/source_size_list.rs b/components/style/values/specified/source_size_list.rs
index 596c2f73951..d41fc283bcc 100644
--- a/components/style/values/specified/source_size_list.rs
+++ b/components/style/values/specified/source_size_list.rs
@@ -8,7 +8,7 @@ use app_units::Au;
use cssparser::{Delimiter, Parser, Token};
#[cfg(feature = "gecko")]
use gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
-use media_queries::{Device, Expression as MediaExpression};
+use media_queries::{Device, MediaCondition};
use parser::{Parse, ParserContext};
use selectors::context::QuirksMode;
use style_traits::ParseError;
@@ -19,9 +19,7 @@ use values::specified::{Length, NoCalcLength, ViewportPercentageLength};
///
/// https://html.spec.whatwg.org/multipage/#source-size
pub struct SourceSize {
- // FIXME(emilio): This should be a `MediaCondition`, and support `and` and
- // `or`.
- condition: MediaExpression,
+ condition: MediaCondition,
value: Length,
}
@@ -30,7 +28,7 @@ impl Parse for SourceSize {
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
- let condition = MediaExpression::parse(context, input)?;
+ let condition = MediaCondition::parse(context, input)?;
let value = Length::parse_non_negative(context, input)?;
Ok(Self { condition, value })
diff --git a/components/style_traits/Cargo.toml b/components/style_traits/Cargo.toml
index 67a6c851dce..9b273472774 100644
--- a/components/style_traits/Cargo.toml
+++ b/components/style_traits/Cargo.toml
@@ -15,7 +15,7 @@ gecko = []
[dependencies]
app_units = "0.6"
-cssparser = "0.23.0"
+cssparser = "0.24.0"
bitflags = "1.0"
euclid = "0.17"
malloc_size_of = { path = "../malloc_size_of" }
diff --git a/components/style_traits/lib.rs b/components/style_traits/lib.rs
index b1eaf4e086f..737450b5e05 100644
--- a/components/style_traits/lib.rs
+++ b/components/style_traits/lib.rs
@@ -106,12 +106,12 @@ pub enum StyleParseErrorKind<'i> {
PropertyDeclarationValueNotExhausted,
/// An unexpected dimension token was encountered.
UnexpectedDimension(CowRcStr<'i>),
- /// Expected identifier not found.
- ExpectedIdentifier(Token<'i>),
/// Missing or invalid media feature name.
MediaQueryExpectedFeatureName(CowRcStr<'i>),
/// Missing or invalid media feature value.
MediaQueryExpectedFeatureValue,
+ /// A media feature range operator was not expected.
+ MediaQueryUnexpectedOperator,
/// min- or max- properties must have a value.
RangedExpressionWithNoValue,
/// A function was encountered that was not expected.
diff --git a/tests/unit/script/Cargo.toml b/tests/unit/script/Cargo.toml
index f6743a7dba6..dc78113136f 100644
--- a/tests/unit/script/Cargo.toml
+++ b/tests/unit/script/Cargo.toml
@@ -13,4 +13,3 @@ euclid = "0.17"
msg = {path = "../../../components/msg"}
script = {path = "../../../components/script"}
servo_url = {path = "../../../components/url"}
-style = {path = "../../../components/style"}
diff --git a/tests/unit/script/htmlimageelement.rs b/tests/unit/script/htmlimageelement.rs
index 6d62f25a500..796e63c2e2b 100644
--- a/tests/unit/script/htmlimageelement.rs
+++ b/tests/unit/script/htmlimageelement.rs
@@ -2,104 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use script::test::DOMString;
-use script::test::sizes::{parse_a_sizes_attribute, Size};
use script::test::srcset::{Descriptor, ImageSource, parse_a_srcset_attribute};
-use style::media_queries::{MediaQuery, MediaQueryType};
-use style::media_queries::Expression;
-use style::servo::media_queries::{ExpressionKind, Range};
-use style::values::specified::{Length, NoCalcLength, AbsoluteLength, ViewportPercentageLength};
-
-pub fn test_length_for_no_default_provided(len: f32) -> Length {
- let length = Length::NoCalc(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vw(len)));
- return length;
-}
-
-#[test]
-fn no_default_provided() {
- let mut a = vec![];
- let length = test_length_for_no_default_provided(100f32);
- let size = Size { query: None, length: length };
- a.push(size);
- assert_eq!(parse_a_sizes_attribute(DOMString::new(), None), a);
-}
-
-pub fn test_length_for_default_provided(len: f32) -> Length {
- let length = Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(len)));
- return length;
-}
-
-#[test]
-fn default_provided() {
- let mut a = vec![];
- let length = test_length_for_default_provided(2f32);
- let size = Size { query: None, length: length };
- a.push(size);
- assert_eq!(parse_a_sizes_attribute(DOMString::new(), Some(2)), a);
-}
-
-pub fn test_media_query(len: f32) -> MediaQuery {
- let length = Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(len)));
- let expr = Expression(ExpressionKind::Width(Range::Max(length)));
- let media_query = MediaQuery {
- qualifier: None,
- media_type: MediaQueryType::All,
- expressions: vec![expr]
- };
- media_query
-}
-
-pub fn test_length(len: f32) -> Length {
- let length = Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(len)));
- return length;
-}
-
-#[test]
-fn one_value() {
- let mut a = vec![];
- let media_query = test_media_query(200f32);
- let length = test_length(545f32);
- let size = Size { query: Some(media_query), length: length };
- a.push(size);
- assert_eq!(parse_a_sizes_attribute(DOMString::from("(max-width: 200px) 545px"), None), a);
-}
-
-#[test]
-fn more_then_one_value() {
- let media_query = test_media_query(900f32);
- let length = test_length(1000f32);
- let size = Size { query: Some(media_query), length: length };
- let media_query1 = test_media_query(900f32);
- let length1 = test_length(50f32);
- let size1 = Size { query: Some(media_query1), length: length1 };
- let a = &[size, size1];
- assert_eq!(parse_a_sizes_attribute(DOMString::from("(max-width: 900px) 1000px, (max-width: 900px) 50px"),
- None), a);
-}
-
-#[test]
-fn no_extra_whitespace() {
- let mut a = vec![];
- let media_query = test_media_query(200f32);
- let length = test_length(545f32);
- let size = Size { query: Some(media_query), length: length };
- a.push(size);
- assert_eq!(parse_a_sizes_attribute(DOMString::from("(max-width: 200px) 545px"), None), a);
-}
-
-#[test]
-fn extra_whitespace() {
- let media_query = test_media_query(900f32);
- let length = test_length(1000f32);
- let size = Size { query: Some(media_query), length: length };
- let media_query1 = test_media_query(900f32);
- let length1 = test_length(50f32);
- let size1 = Size { query: Some(media_query1), length: length1 };
- let a = &[size, size1];
- assert_eq!(parse_a_sizes_attribute(
- DOMString::from("(max-width: 900px) 1000px, (max-width: 900px) 50px"),
- None), a);
-}
#[test]
fn no_value() {
diff --git a/tests/unit/script/lib.rs b/tests/unit/script/lib.rs
index 6d724db5358..069417a5911 100644
--- a/tests/unit/script/lib.rs
+++ b/tests/unit/script/lib.rs
@@ -6,7 +6,6 @@
#[cfg(test)] extern crate msg;
#[cfg(test)] extern crate script;
#[cfg(test)] extern crate servo_url;
-#[cfg(test)] extern crate style;
#[cfg(test)] mod origin;
#[cfg(all(test, target_pointer_width = "64"))] mod size_of;
diff --git a/tests/unit/style/Cargo.toml b/tests/unit/style/Cargo.toml
index 23778c73f93..77b0ac34fd1 100644
--- a/tests/unit/style/Cargo.toml
+++ b/tests/unit/style/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
[dependencies]
byteorder = "1.0"
app_units = "0.6"
-cssparser = "0.23.0"
+cssparser = "0.24.0"
euclid = "0.17"
html5ever = "0.22"
parking_lot = "0.5"
diff --git a/tests/unit/style/lib.rs b/tests/unit/style/lib.rs
index 2c46bb65063..faa15845195 100644
--- a/tests/unit/style/lib.rs
+++ b/tests/unit/style/lib.rs
@@ -27,7 +27,6 @@ mod attr;
mod custom_properties;
mod keyframes;
mod logical_geometry;
-mod media_queries;
mod parsing;
mod properties;
mod rule_tree;
@@ -37,16 +36,3 @@ mod str;
mod stylesheets;
mod stylist;
mod viewport;
-
-mod writing_modes {
- use style::logical_geometry::WritingMode;
- use style::properties::INITIAL_SERVO_VALUES;
-
- #[test]
- fn initial_writing_mode_is_empty() {
- assert_eq!(
- WritingMode::new(INITIAL_SERVO_VALUES.get_inherited_box()),
- WritingMode::empty(),
- )
- }
-}
diff --git a/tests/unit/style/media_queries.rs b/tests/unit/style/media_queries.rs
deleted file mode 100644
index 044dee3db72..00000000000
--- a/tests/unit/style/media_queries.rs
+++ /dev/null
@@ -1,398 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use euclid::TypedScale;
-use euclid::TypedSize2D;
-use servo_arc::Arc;
-use servo_url::ServoUrl;
-use std::borrow::ToOwned;
-use style::Atom;
-use style::context::QuirksMode;
-use style::media_queries::*;
-use style::servo::media_queries::*;
-use style::shared_lock::SharedRwLock;
-use style::stylesheets::{AllRules, Stylesheet, StylesheetInDocument, Origin, CssRule};
-use style::values::{CustomIdent, specified};
-use style_traits::ToCss;
-
-fn test_media_rule<F>(css: &str, callback: F)
-where
- F: Fn(&MediaList, &str),
-{
- let url = ServoUrl::parse("http://localhost").unwrap();
- let css_str = css.to_owned();
- let lock = SharedRwLock::new();
- let media_list = Arc::new(lock.wrap(MediaList::empty()));
- let stylesheet = Stylesheet::from_str(
- css, url, Origin::Author, media_list, lock,
- None, None, QuirksMode::NoQuirks, 0);
- let dummy = Device::new(MediaType::screen(), TypedSize2D::new(200.0, 100.0), TypedScale::new(1.0));
- let mut rule_count = 0;
- let guard = stylesheet.shared_lock.read();
- for rule in stylesheet.iter_rules::<AllRules>(&dummy, &guard) {
- if let CssRule::Media(ref lock) = *rule {
- rule_count += 1;
- callback(&lock.read_with(&guard).media_queries.read_with(&guard), css);
- }
- }
- assert!(rule_count > 0, css_str);
-}
-
-fn media_query_test(device: &Device, css: &str, expected_rule_count: usize) {
- let url = ServoUrl::parse("http://localhost").unwrap();
- let lock = SharedRwLock::new();
- let media_list = Arc::new(lock.wrap(MediaList::empty()));
- let ss = Stylesheet::from_str(
- css, url, Origin::Author, media_list, lock,
- None, None, QuirksMode::NoQuirks, 0);
- let mut rule_count = 0;
- ss.effective_style_rules(device, &ss.shared_lock.read(), |_| rule_count += 1);
- assert!(rule_count == expected_rule_count, css.to_owned());
-}
-
-#[test]
-fn test_mq_empty() {
- test_media_rule("@media { }", |list, css| {
- assert!(list.media_queries.len() == 0, css.to_owned());
- });
-}
-
-#[test]
-fn test_mq_screen() {
- test_media_rule("@media screen { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == None, css.to_owned());
- assert!(q.media_type == MediaQueryType::Concrete(MediaType::screen()), css.to_owned());
- assert!(q.expressions.len() == 0, css.to_owned());
- });
-
- test_media_rule("@media only screen { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
- assert!(q.media_type == MediaQueryType::Concrete(MediaType::screen()), css.to_owned());
- assert!(q.expressions.len() == 0, css.to_owned());
- });
-
- test_media_rule("@media not screen { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
- assert!(q.media_type == MediaQueryType::Concrete(MediaType::screen()), css.to_owned());
- assert!(q.expressions.len() == 0, css.to_owned());
- });
-}
-
-#[test]
-fn test_mq_print() {
- test_media_rule("@media print { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == None, css.to_owned());
- assert!(q.media_type == MediaQueryType::Concrete(MediaType::print()), css.to_owned());
- assert!(q.expressions.len() == 0, css.to_owned());
- });
-
- test_media_rule("@media only print { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
- assert!(q.media_type == MediaQueryType::Concrete(MediaType::print()), css.to_owned());
- assert!(q.expressions.len() == 0, css.to_owned());
- });
-
- test_media_rule("@media not print { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
- assert!(q.media_type == MediaQueryType::Concrete(MediaType::print()), css.to_owned());
- assert!(q.expressions.len() == 0, css.to_owned());
- });
-}
-
-#[test]
-fn test_mq_unknown() {
- test_media_rule("@media fridge { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == None, css.to_owned());
- assert!(q.media_type == MediaQueryType::Concrete(
- MediaType(CustomIdent(Atom::from("fridge")))), css.to_owned());
- assert!(q.expressions.len() == 0, css.to_owned());
- });
-
- test_media_rule("@media only glass { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
- assert!(q.media_type == MediaQueryType::Concrete(
- MediaType(CustomIdent(Atom::from("glass")))), css.to_owned());
- assert!(q.expressions.len() == 0, css.to_owned());
- });
-
- test_media_rule("@media not wood { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
- assert!(q.media_type == MediaQueryType::Concrete(
- MediaType(CustomIdent(Atom::from("wood")))), css.to_owned());
- assert!(q.expressions.len() == 0, css.to_owned());
- });
-}
-
-#[test]
-fn test_mq_all() {
- test_media_rule("@media all { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == None, css.to_owned());
- assert!(q.media_type == MediaQueryType::All, css.to_owned());
- assert!(q.expressions.len() == 0, css.to_owned());
- });
-
- test_media_rule("@media only all { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
- assert!(q.media_type == MediaQueryType::All, css.to_owned());
- assert!(q.expressions.len() == 0, css.to_owned());
- });
-
- test_media_rule("@media not all { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
- assert!(q.media_type == MediaQueryType::All, css.to_owned());
- assert!(q.expressions.len() == 0, css.to_owned());
- });
-}
-
-#[test]
-fn test_mq_or() {
- test_media_rule("@media screen, print { }", |list, css| {
- assert!(list.media_queries.len() == 2, css.to_owned());
- let q0 = &list.media_queries[0];
- assert!(q0.qualifier == None, css.to_owned());
- assert!(q0.media_type == MediaQueryType::Concrete(MediaType::screen()), css.to_owned());
- assert!(q0.expressions.len() == 0, css.to_owned());
-
- let q1 = &list.media_queries[1];
- assert!(q1.qualifier == None, css.to_owned());
- assert!(q1.media_type == MediaQueryType::Concrete(MediaType::print()), css.to_owned());
- assert!(q1.expressions.len() == 0, css.to_owned());
- });
-}
-
-#[test]
-fn test_mq_default_expressions() {
- test_media_rule("@media (min-width: 100px) { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == None, css.to_owned());
- assert!(q.media_type == MediaQueryType::All, css.to_owned());
- assert!(q.expressions.len() == 1, css.to_owned());
- match *q.expressions[0].kind_for_testing() {
- ExpressionKind::Width(Range::Min(ref w)) => assert!(*w == specified::Length::from_px(100.)),
- _ => panic!("wrong expression type"),
- }
- });
-
- test_media_rule("@media (max-width: 43px) { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == None, css.to_owned());
- assert!(q.media_type == MediaQueryType::All, css.to_owned());
- assert!(q.expressions.len() == 1, css.to_owned());
- match *q.expressions[0].kind_for_testing() {
- ExpressionKind::Width(Range::Max(ref w)) => assert!(*w == specified::Length::from_px(43.)),
- _ => panic!("wrong expression type"),
- }
- });
-}
-
-#[test]
-fn test_mq_expressions() {
- test_media_rule("@media screen and (min-width: 100px) { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == None, css.to_owned());
- assert!(q.media_type == MediaQueryType::Concrete(MediaType::screen()), css.to_owned());
- assert!(q.expressions.len() == 1, css.to_owned());
- match *q.expressions[0].kind_for_testing() {
- ExpressionKind::Width(Range::Min(ref w)) => assert!(*w == specified::Length::from_px(100.)),
- _ => panic!("wrong expression type"),
- }
- });
-
- test_media_rule("@media print and (max-width: 43px) { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == None, css.to_owned());
- assert!(q.media_type == MediaQueryType::Concrete(MediaType::print()), css.to_owned());
- assert!(q.expressions.len() == 1, css.to_owned());
- match *q.expressions[0].kind_for_testing() {
- ExpressionKind::Width(Range::Max(ref w)) => assert!(*w == specified::Length::from_px(43.)),
- _ => panic!("wrong expression type"),
- }
- });
-
- test_media_rule("@media print and (width: 43px) { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == None, css.to_owned());
- assert!(q.media_type == MediaQueryType::Concrete(MediaType::print()), css.to_owned());
- assert!(q.expressions.len() == 1, css.to_owned());
- match *q.expressions[0].kind_for_testing() {
- ExpressionKind::Width(Range::Eq(ref w)) => assert!(*w == specified::Length::from_px(43.)),
- _ => panic!("wrong expression type"),
- }
- });
-
- test_media_rule("@media fridge and (max-width: 52px) { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == None, css.to_owned());
- assert!(q.media_type == MediaQueryType::Concrete(
- MediaType(CustomIdent(Atom::from("fridge")))), css.to_owned());
- assert!(q.expressions.len() == 1, css.to_owned());
- match *q.expressions[0].kind_for_testing() {
- ExpressionKind::Width(Range::Max(ref w)) => assert!(*w == specified::Length::from_px(52.)),
- _ => panic!("wrong expression type"),
- }
- });
-}
-
-#[test]
-fn test_to_css() {
- test_media_rule("@media print and (width: 43px) { }", |list, _| {
- let q = &list.media_queries[0];
- let dest = q.to_css_string();
- assert_eq!(dest, "print and (width: 43px)");
- });
-}
-
-#[test]
-fn test_mq_multiple_expressions() {
- test_media_rule("@media (min-width: 100px) and (max-width: 200px) { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == None, css.to_owned());
- assert!(q.media_type == MediaQueryType::All, css.to_owned());
- assert!(q.expressions.len() == 2, css.to_owned());
- match *q.expressions[0].kind_for_testing() {
- ExpressionKind::Width(Range::Min(ref w)) => assert!(*w == specified::Length::from_px(100.)),
- _ => panic!("wrong expression type"),
- }
- match *q.expressions[1].kind_for_testing() {
- ExpressionKind::Width(Range::Max(ref w)) => assert!(*w == specified::Length::from_px(200.)),
- _ => panic!("wrong expression type"),
- }
- });
-
- test_media_rule("@media not screen and (min-width: 100px) and (max-width: 200px) { }", |list, css| {
- assert!(list.media_queries.len() == 1, css.to_owned());
- let q = &list.media_queries[0];
- assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
- assert!(q.media_type == MediaQueryType::Concrete(MediaType::screen()), css.to_owned());
- assert!(q.expressions.len() == 2, css.to_owned());
- match *q.expressions[0].kind_for_testing() {
- ExpressionKind::Width(Range::Min(ref w)) => assert!(*w == specified::Length::from_px(100.)),
- _ => panic!("wrong expression type"),
- }
- match *q.expressions[1].kind_for_testing() {
- ExpressionKind::Width(Range::Max(ref w)) => assert!(*w == specified::Length::from_px(200.)),
- _ => panic!("wrong expression type"),
- }
- });
-}
-
-#[test]
-fn test_mq_malformed_expressions() {
- fn check_malformed_expr(list: &MediaList, css: &str) {
- assert!(!list.media_queries.is_empty(), css.to_owned());
- for mq in &list.media_queries {
- assert!(mq.qualifier == Some(Qualifier::Not), css.to_owned());
- assert!(mq.media_type == MediaQueryType::All, css.to_owned());
- assert!(mq.expressions.is_empty(), css.to_owned());
- }
- }
-
- for rule in &[
- "@media (min-width: 100blah) and (max-width: 200px) { }",
- "@media screen and (height: 200px) { }",
- "@media (min-width: 30em foo bar) {}",
- "@media not {}",
- "@media not (min-width: 300px) {}",
- "@media , {}",
- ] {
- test_media_rule(rule, check_malformed_expr);
- }
-}
-
-#[test]
-fn test_matching_simple() {
- let device = Device::new(MediaType::screen(), TypedSize2D::new(200.0, 100.0), TypedScale::new(1.0));
-
- media_query_test(&device, "@media not all { a { color: red; } }", 0);
- media_query_test(&device, "@media not screen { a { color: red; } }", 0);
- media_query_test(&device, "@media not print { a { color: red; } }", 1);
-
- media_query_test(&device, "@media unknown { a { color: red; } }", 0);
- media_query_test(&device, "@media not unknown { a { color: red; } }", 1);
-
- media_query_test(&device, "@media { a { color: red; } }", 1);
- media_query_test(&device, "@media screen { a { color: red; } }", 1);
- media_query_test(&device, "@media print { a { color: red; } }", 0);
-}
-
-#[test]
-fn test_matching_width() {
- let device = Device::new(MediaType::screen(), TypedSize2D::new(200.0, 100.0), TypedScale::new(1.0));
-
- media_query_test(&device, "@media { a { color: red; } }", 1);
-
- media_query_test(&device, "@media (min-width: 50px) { a { color: red; } }", 1);
- media_query_test(&device, "@media (min-width: 150px) { a { color: red; } }", 1);
- media_query_test(&device, "@media (min-width: 300px) { a { color: red; } }", 0);
-
- media_query_test(&device, "@media screen and (min-width: 50px) { a { color: red; } }", 1);
- media_query_test(&device, "@media screen and (min-width: 150px) { a { color: red; } }", 1);
- media_query_test(&device, "@media screen and (min-width: 300px) { a { color: red; } }", 0);
-
- media_query_test(&device, "@media not screen and (min-width: 50px) { a { color: red; } }", 0);
- media_query_test(&device, "@media not screen and (min-width: 150px) { a { color: red; } }", 0);
- media_query_test(&device, "@media not screen and (min-width: 300px) { a { color: red; } }", 1);
-
- media_query_test(&device, "@media (max-width: 50px) { a { color: red; } }", 0);
- media_query_test(&device, "@media (max-width: 150px) { a { color: red; } }", 0);
- media_query_test(&device, "@media (max-width: 300px) { a { color: red; } }", 1);
-
- media_query_test(&device, "@media screen and (min-width: 50px) and (max-width: 100px) { a { color: red; } }", 0);
- media_query_test(&device, "@media screen and (min-width: 250px) and (max-width: 300px) { a { color: red; } }", 0);
- media_query_test(&device, "@media screen and (min-width: 50px) and (max-width: 250px) { a { color: red; } }", 1);
-
- media_query_test(
- &device, "@media not screen and (min-width: 50px) and (max-width: 100px) { a { color: red; } }", 1);
- media_query_test(
- &device, "@media not screen and (min-width: 250px) and (max-width: 300px) { a { color: red; } }", 1);
- media_query_test(
- &device, "@media not screen and (min-width: 50px) and (max-width: 250px) { a { color: red; } }", 0);
-
- media_query_test(
- &device, "@media not screen and (min-width: 3.1em) and (max-width: 6em) { a { color: red; } }", 1);
- media_query_test(
- &device, "@media not screen and (min-width: 16em) and (max-width: 19.75em) { a { color: red; } }", 1);
- media_query_test(
- &device, "@media not screen and (min-width: 3em) and (max-width: 250px) { a { color: red; } }", 0);
-}
-
-#[test]
-fn test_matching_invalid() {
- let device = Device::new(MediaType::screen(), TypedSize2D::new(200.0, 100.0), TypedScale::new(1.0));
-
- media_query_test(&device, "@media fridge { a { color: red; } }", 0);
- media_query_test(&device, "@media screen and (height: 100px) { a { color: red; } }", 0);
- media_query_test(&device, "@media not print and (width: 100) { a { color: red; } }", 0);
-}