mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
Auto merge of #21109 - emilio:gecko-sync, r=SimonSapin
style: sync changes from mozilla-central. See each individual commit for details.. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21109) <!-- Reviewable:end -->
This commit is contained in:
commit
c71c55e542
54 changed files with 872 additions and 1077 deletions
29
Cargo.lock
generated
29
Cargo.lock
generated
|
@ -271,7 +271,7 @@ dependencies = [
|
||||||
"azure 0.29.0 (git+https://github.com/servo/rust-azure)",
|
"azure 0.29.0 (git+https://github.com/servo/rust-azure)",
|
||||||
"canvas_traits 0.0.1",
|
"canvas_traits 0.0.1",
|
||||||
"compositing 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)",
|
"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)",
|
"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)",
|
"gleam 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -289,7 +289,7 @@ dependencies = [
|
||||||
name = "canvas_traits"
|
name = "canvas_traits"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
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)",
|
"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)",
|
"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)",
|
"ipc-channel 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -561,7 +561,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cssparser"
|
name = "cssparser"
|
||||||
version = "0.23.10"
|
version = "0.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cssparser-macros 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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]]
|
[[package]]
|
||||||
|
@ -1610,7 +1610,7 @@ name = "malloc_size_of"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hashglobe 0.1.0",
|
"hashglobe 0.1.0",
|
||||||
"hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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",
|
"deny_public_fields 0.0.1",
|
||||||
"devtools_traits 0.0.1",
|
"devtools_traits 0.0.1",
|
||||||
"dom_struct 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)",
|
"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)",
|
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"canvas_traits 0.0.1",
|
"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)",
|
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gfx_traits 0.0.1",
|
"gfx_traits 0.0.1",
|
||||||
"html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -2590,7 +2590,6 @@ dependencies = [
|
||||||
"msg 0.0.1",
|
"msg 0.0.1",
|
||||||
"script 0.0.1",
|
"script 0.0.1",
|
||||||
"servo_url 0.0.1",
|
"servo_url 0.0.1",
|
||||||
"style 0.0.1",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2629,7 +2628,7 @@ name = "selectors"
|
||||||
version = "0.19.0"
|
version = "0.19.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.0 (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)",
|
||||||
"fnv 1.0.5 (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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"fallible 0.0.1",
|
"fallible 0.0.1",
|
||||||
|
@ -3028,7 +3027,7 @@ version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -3050,7 +3049,7 @@ version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"malloc_size_of 0.0.1",
|
"malloc_size_of 0.0.1",
|
||||||
"malloc_size_of_derive 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-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-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 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 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 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"
|
"checksum darling_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b315f49c7b6db3708bca6e6913c194581a44ec619b7a39e131d4dd63733a3698"
|
||||||
|
|
|
@ -13,7 +13,7 @@ path = "lib.rs"
|
||||||
azure = {git = "https://github.com/servo/rust-azure"}
|
azure = {git = "https://github.com/servo/rust-azure"}
|
||||||
canvas_traits = {path = "../canvas_traits"}
|
canvas_traits = {path = "../canvas_traits"}
|
||||||
compositing = {path = "../compositing"}
|
compositing = {path = "../compositing"}
|
||||||
cssparser = "0.23.0"
|
cssparser = "0.24"
|
||||||
euclid = "0.17"
|
euclid = "0.17"
|
||||||
fnv = "1.0"
|
fnv = "1.0"
|
||||||
gleam = "0.5"
|
gleam = "0.5"
|
||||||
|
|
|
@ -10,7 +10,7 @@ name = "canvas_traits"
|
||||||
path = "lib.rs"
|
path = "lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cssparser = "0.23.0"
|
cssparser = "0.24.0"
|
||||||
euclid = "0.17"
|
euclid = "0.17"
|
||||||
ipc-channel = "0.10"
|
ipc-channel = "0.10"
|
||||||
gleam = "0.5.1"
|
gleam = "0.5.1"
|
||||||
|
|
|
@ -1219,11 +1219,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
state.add_display_item(DisplayItem::BoxShadow(Box::new(BoxShadowDisplayItem {
|
state.add_display_item(DisplayItem::BoxShadow(Box::new(BoxShadowDisplayItem {
|
||||||
base: base,
|
base: base,
|
||||||
box_bounds: absolute_bounds.to_layout(),
|
box_bounds: absolute_bounds.to_layout(),
|
||||||
color: box_shadow
|
color: style.resolve_color(box_shadow.base.color).to_layout(),
|
||||||
.base
|
|
||||||
.color
|
|
||||||
.unwrap_or(style.get_color().color)
|
|
||||||
.to_layout(),
|
|
||||||
offset: LayoutVector2D::new(
|
offset: LayoutVector2D::new(
|
||||||
box_shadow.base.horizontal.px(),
|
box_shadow.base.horizontal.px(),
|
||||||
box_shadow.base.vertical.px(),
|
box_shadow.base.vertical.px(),
|
||||||
|
@ -2038,10 +2034,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
base: base.clone(),
|
base: base.clone(),
|
||||||
shadow: webrender_api::Shadow {
|
shadow: webrender_api::Shadow {
|
||||||
offset: LayoutVector2D::new(shadow.horizontal.px(), shadow.vertical.px()),
|
offset: LayoutVector2D::new(shadow.horizontal.px(), shadow.vertical.px()),
|
||||||
color: shadow
|
color: self.style.resolve_color(shadow.color).to_layout(),
|
||||||
.color
|
|
||||||
.unwrap_or(self.style().get_color().color)
|
|
||||||
.to_layout(),
|
|
||||||
blur_radius: shadow.blur.px(),
|
blur_radius: shadow.blur.px(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -24,7 +24,7 @@ servo = [
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
app_units = "0.6"
|
app_units = "0.6"
|
||||||
cssparser = "0.23.0"
|
cssparser = "0.24.0"
|
||||||
euclid = "0.17"
|
euclid = "0.17"
|
||||||
hashglobe = { path = "../hashglobe" }
|
hashglobe = { path = "../hashglobe" }
|
||||||
hyper = { version = "0.10", optional = true }
|
hyper = { version = "0.10", optional = true }
|
||||||
|
|
|
@ -37,7 +37,7 @@ canvas_traits = {path = "../canvas_traits"}
|
||||||
caseless = "0.2"
|
caseless = "0.2"
|
||||||
cookie = "0.10"
|
cookie = "0.10"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
cssparser = "0.23.0"
|
cssparser = "0.24"
|
||||||
deny_public_fields = {path = "../deny_public_fields"}
|
deny_public_fields = {path = "../deny_public_fields"}
|
||||||
devtools_traits = {path = "../devtools_traits"}
|
devtools_traits = {path = "../devtools_traits"}
|
||||||
dom_struct = {path = "../dom_struct"}
|
dom_struct = {path = "../dom_struct"}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use app_units::{Au, AU_PER_PX};
|
use app_units::{Au, AU_PER_PX};
|
||||||
use cssparser::{Parser, ParserInput};
|
|
||||||
use document_loader::{LoadType, LoadBlocker};
|
use document_loader::{LoadType, LoadBlocker};
|
||||||
use dom::activation::Activatable;
|
use dom::activation::Activatable;
|
||||||
use dom::attr::Attr;
|
use dom::attr::Attr;
|
||||||
|
@ -58,13 +57,7 @@ use std::default::Default;
|
||||||
use std::i32;
|
use std::i32;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_double, parse_unsigned_integer};
|
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::str::is_ascii_digit;
|
||||||
use style::values::specified::{Length, ViewportPercentageLength};
|
|
||||||
use style::values::specified::length::NoCalcLength;
|
|
||||||
use style_traits::ParsingMode;
|
|
||||||
use task_source::TaskSource;
|
use task_source::TaskSource;
|
||||||
|
|
||||||
enum ParseState {
|
enum ParseState {
|
||||||
|
@ -94,12 +87,6 @@ enum State {
|
||||||
Broken,
|
Broken,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub struct Size {
|
|
||||||
pub query: Option<MediaQuery>,
|
|
||||||
pub length: Length,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
|
#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
|
||||||
enum ImageRequestPhase {
|
enum ImageRequestPhase {
|
||||||
Pending,
|
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 {
|
impl HTMLImageElementMethods for HTMLImageElement {
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-img-alt
|
// https://html.spec.whatwg.org/multipage/#dom-img-alt
|
||||||
make_getter!(Alt, "alt");
|
make_getter!(Alt, "alt");
|
||||||
|
|
|
@ -350,6 +350,14 @@ partial interface CSSStyleDeclaration {
|
||||||
[CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString offsetInlineStart;
|
[CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString offsetInlineStart;
|
||||||
[CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString offset-inline-end;
|
[CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString offset-inline-end;
|
||||||
[CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString offsetInlineEnd;
|
[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 height;
|
||||||
[CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString minHeight;
|
[CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString minHeight;
|
||||||
|
|
|
@ -15,10 +15,6 @@ pub mod area {
|
||||||
pub use dom::htmlareaelement::{Area, Shape};
|
pub use dom::htmlareaelement::{Area, Shape};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod sizes {
|
|
||||||
pub use dom::htmlimageelement::{parse_a_sizes_attribute, Size};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod size_of {
|
pub mod size_of {
|
||||||
use dom::characterdata::CharacterData;
|
use dom::characterdata::CharacterData;
|
||||||
use dom::element::Element;
|
use dom::element::Element;
|
||||||
|
|
|
@ -13,7 +13,7 @@ path = "lib.rs"
|
||||||
app_units = "0.6"
|
app_units = "0.6"
|
||||||
atomic_refcell = "0.1"
|
atomic_refcell = "0.1"
|
||||||
canvas_traits = {path = "../canvas_traits"}
|
canvas_traits = {path = "../canvas_traits"}
|
||||||
cssparser = "0.23.0"
|
cssparser = "0.24"
|
||||||
euclid = "0.17"
|
euclid = "0.17"
|
||||||
gfx_traits = {path = "../gfx_traits"}
|
gfx_traits = {path = "../gfx_traits"}
|
||||||
html5ever = "0.22"
|
html5ever = "0.22"
|
||||||
|
|
|
@ -22,7 +22,7 @@ bench = []
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
matches = "0.1"
|
matches = "0.1"
|
||||||
cssparser = "0.23.0"
|
cssparser = "0.24.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
fnv = "1.0"
|
fnv = "1.0"
|
||||||
phf = "0.7.18"
|
phf = "0.7.18"
|
||||||
|
|
|
@ -23,7 +23,7 @@ use sink::Push;
|
||||||
use smallvec::{self, SmallVec};
|
use smallvec::{self, SmallVec};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::ops::Add;
|
use std::ops::{AddAssign, Add};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
|
@ -228,6 +228,16 @@ struct Specificity {
|
||||||
element_selectors: u32,
|
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 {
|
impl Add for Specificity {
|
||||||
type Output = Specificity;
|
type Output = Specificity;
|
||||||
|
|
||||||
|
@ -251,6 +261,7 @@ impl Default for Specificity {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u32> for Specificity {
|
impl From<u32> for Specificity {
|
||||||
|
#[inline]
|
||||||
fn from(value: u32) -> Specificity {
|
fn from(value: u32) -> Specificity {
|
||||||
assert!(value <= MAX_10BIT << 20 | MAX_10BIT << 10 | MAX_10BIT);
|
assert!(value <= MAX_10BIT << 20 | MAX_10BIT << 10 | MAX_10BIT);
|
||||||
Specificity {
|
Specificity {
|
||||||
|
@ -262,6 +273,7 @@ impl From<u32> for Specificity {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Specificity> for u32 {
|
impl From<Specificity> for u32 {
|
||||||
|
#[inline]
|
||||||
fn from(specificity: Specificity) -> u32 {
|
fn from(specificity: Specificity) -> u32 {
|
||||||
cmp::min(specificity.id_selectors, MAX_10BIT) << 20 |
|
cmp::min(specificity.id_selectors, MAX_10BIT) << 20 |
|
||||||
cmp::min(specificity.class_like_selectors, MAX_10BIT) << 10 |
|
cmp::min(specificity.class_like_selectors, MAX_10BIT) << 10 |
|
||||||
|
@ -298,17 +310,29 @@ where
|
||||||
builder,
|
builder,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// FIXME(emilio): Spec doesn't define any particular specificity for
|
Component::PseudoElement(..) | Component::LocalName(..) => {
|
||||||
// ::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(..) => {
|
|
||||||
specificity.element_selectors += 1
|
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::Class(..) |
|
||||||
Component::AttributeInNoNamespace { .. } |
|
Component::AttributeInNoNamespace { .. } |
|
||||||
Component::AttributeInNoNamespaceExists { .. } |
|
Component::AttributeInNoNamespaceExists { .. } |
|
||||||
|
@ -319,7 +343,6 @@ where
|
||||||
Component::Root |
|
Component::Root |
|
||||||
Component::Empty |
|
Component::Empty |
|
||||||
Component::Scope |
|
Component::Scope |
|
||||||
Component::Host(..) |
|
|
||||||
Component::NthChild(..) |
|
Component::NthChild(..) |
|
||||||
Component::NthLastChild(..) |
|
Component::NthLastChild(..) |
|
||||||
Component::NthOfType(..) |
|
Component::NthOfType(..) |
|
||||||
|
@ -327,7 +350,9 @@ where
|
||||||
Component::FirstOfType |
|
Component::FirstOfType |
|
||||||
Component::LastOfType |
|
Component::LastOfType |
|
||||||
Component::OnlyOfType |
|
Component::OnlyOfType |
|
||||||
Component::NonTSPseudoClass(..) => specificity.class_like_selectors += 1,
|
Component::NonTSPseudoClass(..) => {
|
||||||
|
specificity.class_like_selectors += 1;
|
||||||
|
},
|
||||||
Component::ExplicitUniversalType |
|
Component::ExplicitUniversalType |
|
||||||
Component::ExplicitAnyNamespace |
|
Component::ExplicitAnyNamespace |
|
||||||
Component::ExplicitNoNamespace |
|
Component::ExplicitNoNamespace |
|
||||||
|
|
|
@ -576,7 +576,7 @@ where
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
if element.is_link() || combinator.is_sibling() {
|
if element.is_link() {
|
||||||
visited_handling = VisitedHandlingMode::AllLinksUnvisited;
|
visited_handling = VisitedHandlingMode::AllLinksUnvisited;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -995,8 +995,7 @@ impl<Impl: SelectorImpl> ToCss for Selector<Impl> {
|
||||||
|
|
||||||
let mut combinators = self.iter_raw_match_order()
|
let mut combinators = self.iter_raw_match_order()
|
||||||
.rev()
|
.rev()
|
||||||
.filter(|x| x.is_combinator())
|
.filter_map(|x| x.as_combinator());
|
||||||
.peekable();
|
|
||||||
let compound_selectors = self.iter_raw_match_order()
|
let compound_selectors = self.iter_raw_match_order()
|
||||||
.as_slice()
|
.as_slice()
|
||||||
.split(|x| x.is_combinator())
|
.split(|x| x.is_combinator())
|
||||||
|
@ -1007,72 +1006,74 @@ impl<Impl: SelectorImpl> ToCss for Selector<Impl> {
|
||||||
debug_assert!(!combinators_exhausted);
|
debug_assert!(!combinators_exhausted);
|
||||||
|
|
||||||
// https://drafts.csswg.org/cssom/#serializing-selectors
|
// 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
|
||||||
// 1. If there is only one simple selector in the compound selectors
|
// which is a universal selector, append the result of
|
||||||
// which is a universal selector, append the result of
|
// serializing the universal selector to s.
|
||||||
// serializing the universal selector to s.
|
//
|
||||||
//
|
// Check if `!compound.empty()` first--this can happen if we have
|
||||||
// Check if `!compound.empty()` first--this can happen if we have
|
// something like `... > ::before`, because we store `>` and `::`
|
||||||
// something like `... > ::before`, because we store `>` and `::`
|
// both as combinators internally.
|
||||||
// both as combinators internally.
|
//
|
||||||
//
|
// If we are in this case, after we have serialized the universal
|
||||||
// If we are in this case, after we have serialized the universal
|
// selector, we skip Step 2 and continue with the algorithm.
|
||||||
// selector, we skip Step 2 and continue with the algorithm.
|
let (can_elide_namespace, first_non_namespace) = match compound[0] {
|
||||||
let (can_elide_namespace, first_non_namespace) = match &compound[0] {
|
Component::ExplicitAnyNamespace |
|
||||||
&Component::ExplicitAnyNamespace |
|
Component::ExplicitNoNamespace |
|
||||||
&Component::ExplicitNoNamespace |
|
Component::Namespace(..) => (false, 1),
|
||||||
&Component::Namespace(_, _) => (false, 1),
|
Component::DefaultNamespace(..) => (true, 1),
|
||||||
&Component::DefaultNamespace(_) => (true, 1),
|
_ => (true, 0),
|
||||||
_ => (true, 0),
|
};
|
||||||
};
|
let mut perform_step_2 = true;
|
||||||
let mut perform_step_2 = true;
|
let next_combinator = combinators.next();
|
||||||
if first_non_namespace == compound.len() - 1 {
|
if first_non_namespace == compound.len() - 1 {
|
||||||
match (combinators.peek(), &compound[first_non_namespace]) {
|
match (next_combinator, &compound[first_non_namespace]) {
|
||||||
// We have to be careful here, because if there is a
|
// We have to be careful here, because if there is a
|
||||||
// pseudo element "combinator" there isn't really just
|
// pseudo element "combinator" there isn't really just
|
||||||
// the one simple selector. Technically this compound
|
// the one simple selector. Technically this compound
|
||||||
// selector contains the pseudo element selector as well
|
// selector contains the pseudo element selector as well
|
||||||
// -- Combinator::PseudoElement, just like
|
// -- Combinator::PseudoElement, just like
|
||||||
// Combinator::SlotAssignment, don't exist in the
|
// Combinator::SlotAssignment, don't exist in the
|
||||||
// spec.
|
// spec.
|
||||||
(Some(&&Component::Combinator(Combinator::PseudoElement)), _) |
|
(Some(Combinator::PseudoElement), _) |
|
||||||
(Some(&&Component::Combinator(Combinator::SlotAssignment)), _) => (),
|
(Some(Combinator::SlotAssignment), _) => (),
|
||||||
(_, &Component::ExplicitUniversalType) => {
|
(_, &Component::ExplicitUniversalType) => {
|
||||||
// Iterate over everything so we serialize the namespace
|
// Iterate over everything so we serialize the namespace
|
||||||
// too.
|
// too.
|
||||||
for simple in compound.iter() {
|
for simple in compound.iter() {
|
||||||
simple.to_css(dest)?;
|
simple.to_css(dest)?;
|
||||||
}
|
}
|
||||||
// Skip step 2, which is an "otherwise".
|
// Skip step 2, which is an "otherwise".
|
||||||
perform_step_2 = false;
|
perform_step_2 = false;
|
||||||
},
|
},
|
||||||
(_, _) => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Otherwise, for each simple selector in the compound selectors
|
// 2. Otherwise, for each simple selector in the compound selectors
|
||||||
// that is not a universal selector of which the namespace prefix
|
// that is not a universal selector of which the namespace prefix
|
||||||
// maps to a namespace that is not the default namespace
|
// maps to a namespace that is not the default namespace
|
||||||
// serialize the simple selector and append the result to s.
|
// serialize the simple selector and append the result to s.
|
||||||
//
|
//
|
||||||
// See https://github.com/w3c/csswg-drafts/issues/1606, which is
|
// See https://github.com/w3c/csswg-drafts/issues/1606, which is
|
||||||
// proposing to change this to match up with the behavior asserted
|
// proposing to change this to match up with the behavior asserted
|
||||||
// in cssom/serialize-namespaced-type-selectors.html, which the
|
// in cssom/serialize-namespaced-type-selectors.html, which the
|
||||||
// following code tries to match.
|
// following code tries to match.
|
||||||
if perform_step_2 {
|
if perform_step_2 {
|
||||||
for simple in compound.iter() {
|
for simple in compound.iter() {
|
||||||
if let Component::ExplicitUniversalType = *simple {
|
if let Component::ExplicitUniversalType = *simple {
|
||||||
// Can't have a namespace followed by a pseudo-element
|
// Can't have a namespace followed by a pseudo-element
|
||||||
// selector followed by a universal selector in the same
|
// selector followed by a universal selector in the same
|
||||||
// compound selector, so we don't have to worry about the
|
// compound selector, so we don't have to worry about the
|
||||||
// real namespace being in a different `compound`.
|
// real namespace being in a different `compound`.
|
||||||
if can_elide_namespace {
|
if can_elide_namespace {
|
||||||
continue;
|
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
|
// ">", "+", "~", ">>", "||", as appropriate, followed by another
|
||||||
// single SPACE (U+0020) if the combinator was not whitespace, to
|
// single SPACE (U+0020) if the combinator was not whitespace, to
|
||||||
// s.
|
// s.
|
||||||
match combinators.next() {
|
match next_combinator {
|
||||||
Some(c) => c.to_css(dest)?,
|
Some(c) => c.to_css(dest)?,
|
||||||
None => combinators_exhausted = true,
|
None => combinators_exhausted = true,
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,7 +31,7 @@ atomic_refcell = "0.1"
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
byteorder = "1.0"
|
byteorder = "1.0"
|
||||||
cfg-if = "0.1.0"
|
cfg-if = "0.1.0"
|
||||||
cssparser = "0.23.0"
|
cssparser = "0.24.0"
|
||||||
new_debug_unreachable = "1.0"
|
new_debug_unreachable = "1.0"
|
||||||
encoding_rs = {version = "0.7", optional = true}
|
encoding_rs = {version = "0.7", optional = true}
|
||||||
euclid = "0.17"
|
euclid = "0.17"
|
||||||
|
|
|
@ -182,14 +182,21 @@ mod bindings {
|
||||||
// Disable rust unions, because we replace some types inside of
|
// Disable rust unions, because we replace some types inside of
|
||||||
// them.
|
// them.
|
||||||
let mut builder = Builder::default().rust_target(RustTarget::Stable_1_0);
|
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 {
|
let rustfmt_path = env::var_os("RUSTFMT")
|
||||||
Some(path) => builder.with_rustfmt(path.join("rustc").join("bin").join("rustfmt")),
|
// This can be replaced with
|
||||||
None => builder.rustfmt_bindings(env::var_os("STYLO_RUSTFMT_BINDINGS").is_some()),
|
// > .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() {
|
for dir in SEARCH_PATHS.iter() {
|
||||||
builder = builder.clang_arg("-I").clang_arg(dir.to_str().unwrap());
|
builder = builder.clang_arg("-I").clang_arg(dir.to_str().unwrap());
|
||||||
|
|
|
@ -71,7 +71,8 @@ bitflags! {
|
||||||
const IN_OPTIONAL_STATE = 1 << 22;
|
const IN_OPTIONAL_STATE = 1 << 22;
|
||||||
/// <https://html.spec.whatwg.org/multipage/#selector-read-write>
|
/// <https://html.spec.whatwg.org/multipage/#selector-read-write>
|
||||||
const IN_READ_WRITE_STATE = 1 << 22;
|
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>
|
/// <https://html.spec.whatwg.org/multipage/#selector-visited>
|
||||||
const IN_VISITED_STATE = 1 << 24;
|
const IN_VISITED_STATE = 1 << 24;
|
||||||
/// <https://html.spec.whatwg.org/multipage/#selector-link>
|
/// <https://html.spec.whatwg.org/multipage/#selector-link>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
use app_units::AU_PER_PX;
|
use app_units::AU_PER_PX;
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use context::QuirksMode;
|
use context::QuirksMode;
|
||||||
use cssparser::{BasicParseErrorKind, Parser, RGBA};
|
use cssparser::{Parser, RGBA, Token};
|
||||||
use euclid::Size2D;
|
use euclid::Size2D;
|
||||||
use euclid::TypedScale;
|
use euclid::TypedScale;
|
||||||
use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
|
use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
|
||||||
|
@ -255,20 +255,53 @@ pub enum Range {
|
||||||
Min,
|
Min,
|
||||||
/// At most the specified value.
|
/// At most the specified value.
|
||||||
Max,
|
Max,
|
||||||
/// Exactly the specified value.
|
|
||||||
Equal,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A expression for gecko contains a reference to the media feature, the value
|
/// The operator that was specified in this media feature.
|
||||||
/// the media query contained, and the range to evaluate.
|
#[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 => ">=",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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)]
|
#[derive(Clone, Debug, MallocSizeOf)]
|
||||||
pub struct Expression {
|
pub struct MediaFeatureExpression {
|
||||||
feature: &'static nsMediaFeature,
|
feature: &'static nsMediaFeature,
|
||||||
value: Option<MediaExpressionValue>,
|
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
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
where
|
where
|
||||||
W: fmt::Write,
|
W: fmt::Write,
|
||||||
|
@ -279,10 +312,12 @@ impl ToCss for Expression {
|
||||||
{
|
{
|
||||||
dest.write_str("-webkit-")?;
|
dest.write_str("-webkit-")?;
|
||||||
}
|
}
|
||||||
match self.range {
|
|
||||||
Range::Min => dest.write_str("min-")?,
|
if let Some(RangeOrOperator::Range(range)) = self.range_or_operator {
|
||||||
Range::Max => dest.write_str("max-")?,
|
match range {
|
||||||
Range::Equal => {},
|
Range::Min => dest.write_str("min-")?,
|
||||||
|
Range::Max => dest.write_str("max-")?,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: CssStringWriter not needed, feature names are under control.
|
// NB: CssStringWriter not needed, feature names are under control.
|
||||||
|
@ -290,8 +325,15 @@ impl ToCss for Expression {
|
||||||
Atom::from_static(*self.feature.mName)
|
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(": ")?;
|
dest.write_str(": ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref val) = self.value {
|
||||||
val.to_css(dest, self)?;
|
val.to_css(dest, self)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,10 +341,10 @@ impl ToCss for Expression {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Expression {
|
impl PartialEq for MediaFeatureExpression {
|
||||||
fn eq(&self, other: &Expression) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.feature.mName == other.feature.mName && self.value == other.value &&
|
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 {
|
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
|
// NB: If there's a null value, that means that we don't support the
|
||||||
// feature.
|
// feature.
|
||||||
if css_value.mUnit == nsCSSUnit::eCSSUnit_Null {
|
if css_value.mUnit == nsCSSUnit::eCSSUnit_Null {
|
||||||
|
@ -395,7 +440,7 @@ impl MediaExpressionValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
where
|
||||||
W: fmt::Write,
|
W: fmt::Write,
|
||||||
{
|
{
|
||||||
|
@ -537,17 +582,53 @@ fn parse_feature_value<'i, 't>(
|
||||||
Ok(value)
|
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.
|
/// Trivially construct a new expression.
|
||||||
fn new(
|
fn new(
|
||||||
feature: &'static nsMediaFeature,
|
feature: &'static nsMediaFeature,
|
||||||
value: Option<MediaExpressionValue>,
|
value: Option<MediaExpressionValue>,
|
||||||
range: Range,
|
range_or_operator: Option<RangeOrOperator>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
feature,
|
feature,
|
||||||
value,
|
value,
|
||||||
range,
|
range_or_operator,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -560,116 +641,145 @@ impl Expression {
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
input.expect_parenthesis_block().map_err(|err| {
|
input.expect_parenthesis_block()?;
|
||||||
err.location.new_custom_error(match err.kind {
|
|
||||||
BasicParseErrorKind::UnexpectedToken(t) => {
|
|
||||||
StyleParseErrorKind::ExpectedIdentifier(t)
|
|
||||||
},
|
|
||||||
_ => StyleParseErrorKind::UnspecifiedError,
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
input.parse_nested_block(|input| {
|
input.parse_nested_block(|input| {
|
||||||
// FIXME: remove extra indented block when lifetimes are non-lexical
|
Self::parse_in_parenthesis_block(context, input)
|
||||||
let feature;
|
})
|
||||||
let range;
|
}
|
||||||
|
|
||||||
|
/// 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 mut flags = 0;
|
||||||
|
|
||||||
|
if context.chrome_rules_enabled() || context.stylesheet_origin == Origin::UserAgent
|
||||||
{
|
{
|
||||||
let location = input.current_source_location();
|
flags |= structs::nsMediaFeature_RequirementFlags_eUserAgentAndChromeOnly;
|
||||||
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;
|
let result = {
|
||||||
|
let mut feature_name = &**ident;
|
||||||
|
|
||||||
if context.chrome_rules_enabled() || context.stylesheet_origin == Origin::UserAgent
|
if unsafe { structs::StaticPrefs_sVarCache_layout_css_prefixes_webkit } &&
|
||||||
|
starts_with_ignore_ascii_case(feature_name, "-webkit-")
|
||||||
{
|
{
|
||||||
flags |= structs::nsMediaFeature_RequirementFlags_eUserAgentAndChromeOnly;
|
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 result = {
|
let range = if starts_with_ignore_ascii_case(feature_name, "min-") {
|
||||||
let mut feature_name = &**ident;
|
feature_name = &feature_name[4..];
|
||||||
|
Some(Range::Min)
|
||||||
if unsafe { structs::StaticPrefs_sVarCache_layout_css_prefixes_webkit } &&
|
} else if starts_with_ignore_ascii_case(feature_name, "max-") {
|
||||||
starts_with_ignore_ascii_case(feature_name, "-webkit-")
|
feature_name = &feature_name[4..];
|
||||||
{
|
Some(Range::Max)
|
||||||
feature_name = &feature_name[8..];
|
} else {
|
||||||
flags |= structs::nsMediaFeature_RequirementFlags_eHasWebkitPrefix;
|
None
|
||||||
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..];
|
|
||||||
Range::Min
|
|
||||||
} else if starts_with_ignore_ascii_case(feature_name, "max-") {
|
|
||||||
feature_name = &feature_name[4..];
|
|
||||||
Range::Max
|
|
||||||
} else {
|
|
||||||
Range::Equal
|
|
||||||
};
|
|
||||||
|
|
||||||
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(()),
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match result {
|
let atom = Atom::from(string_as_ascii_lowercase(feature_name));
|
||||||
Ok((f, r)) => {
|
match find_feature(|f| atom.as_ptr() == unsafe { *f.mName as *mut _ }) {
|
||||||
feature = f;
|
Some(f) => Ok((f, range)),
|
||||||
range = r;
|
None => Err(()),
|
||||||
},
|
|
||||||
Err(()) => {
|
|
||||||
return Err(location.new_custom_error(
|
|
||||||
StyleParseErrorKind::MediaQueryExpectedFeatureName(ident.clone()),
|
|
||||||
))
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (feature.mReqFlags & !flags) != 0 {
|
match result {
|
||||||
|
Ok((f, r)) => {
|
||||||
|
feature = f;
|
||||||
|
range = r;
|
||||||
|
},
|
||||||
|
Err(()) => {
|
||||||
return Err(location.new_custom_error(
|
return Err(location.new_custom_error(
|
||||||
StyleParseErrorKind::MediaQueryExpectedFeatureName(ident.clone()),
|
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 &&
|
return Ok(Self::new(feature, None, None));
|
||||||
feature.mRangeType != nsMediaFeature_RangeType::eMinMaxAllowed
|
}
|
||||||
{
|
Ok(operator) => operator,
|
||||||
return Err(location.new_custom_error(
|
};
|
||||||
StyleParseErrorKind::MediaQueryExpectedFeatureName(ident.clone()),
|
|
||||||
|
let range_or_operator = match range {
|
||||||
|
Some(range) => {
|
||||||
|
if operator.is_some() {
|
||||||
|
return Err(input.new_custom_error(
|
||||||
|
StyleParseErrorKind::MediaQueryUnexpectedOperator
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
Some(RangeOrOperator::Range(range))
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
// If there's no colon, this is a media query of the form
|
match operator {
|
||||||
// '(<feature>)', that is, there's no value specified.
|
Some(operator) => {
|
||||||
//
|
if !feature_allows_ranges {
|
||||||
// Gecko doesn't allow ranged expressions without a value, so just
|
return Err(input.new_custom_error(
|
||||||
// reject them here too.
|
StyleParseErrorKind::MediaQueryUnexpectedOperator
|
||||||
if input.try(|i| i.expect_colon()).is_err() {
|
));
|
||||||
if range != Range::Equal {
|
}
|
||||||
return Err(input.new_custom_error(StyleParseErrorKind::RangedExpressionWithNoValue));
|
Some(RangeOrOperator::Operator(operator))
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
}
|
}
|
||||||
return Ok(Expression::new(feature, None, range));
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let value =
|
let value =
|
||||||
parse_feature_value(feature, feature.mValueType, context, input).map_err(|err| {
|
parse_feature_value(feature, feature.mValueType, context, input).map_err(|err| {
|
||||||
err.location
|
err.location
|
||||||
.new_custom_error(StyleParseErrorKind::MediaQueryExpectedFeatureValue)
|
.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.
|
/// Returns whether this media query evaluates to true for the given device.
|
||||||
|
@ -704,8 +814,8 @@ impl Expression {
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
debug_assert!(
|
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"
|
"Whoops, wrong range"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -730,7 +840,7 @@ impl Expression {
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME(emilio): Handle the possible floating point errors?
|
// 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)) => {
|
(&Length(ref one), &Length(ref other)) => {
|
||||||
computed::Context::for_media_query_evaluation(device, quirks_mode, |context| {
|
computed::Context::for_media_query_evaluation(device, quirks_mode, |context| {
|
||||||
one.to_computed_value(&context)
|
one.to_computed_value(&context)
|
||||||
|
@ -750,11 +860,11 @@ impl Expression {
|
||||||
if (*device.pres_context).mOverrideDPPX > 0.0 {
|
if (*device.pres_context).mOverrideDPPX > 0.0 {
|
||||||
self::Resolution::Dppx((*device.pres_context).mOverrideDPPX).to_dpi()
|
self::Resolution::Dppx((*device.pres_context).mOverrideDPPX).to_dpi()
|
||||||
} else {
|
} 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)) => {
|
(&Ident(ref one), &Ident(ref other)) => {
|
||||||
debug_assert_ne!(
|
debug_assert_ne!(
|
||||||
|
@ -773,10 +883,31 @@ impl Expression {
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
cmp == Ordering::Equal || match self.range {
|
let range_or_op = match self.range_or_operator {
|
||||||
Range::Min => cmp == Ordering::Less,
|
Some(r) => r,
|
||||||
Range::Equal => false,
|
None => return cmp == Ordering::Equal,
|
||||||
Range::Max => cmp == Ordering::Greater,
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ macro_rules! apply_non_ts_list {
|
||||||
("visited", Visited, visited, IN_VISITED_STATE, _),
|
("visited", Visited, visited, IN_VISITED_STATE, _),
|
||||||
("active", Active, active, IN_ACTIVE_STATE, _),
|
("active", Active, active, IN_ACTIVE_STATE, _),
|
||||||
("checked", Checked, checked, IN_CHECKED_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, _),
|
("disabled", Disabled, disabled, IN_DISABLED_STATE, _),
|
||||||
("enabled", Enabled, enabled, IN_ENABLED_STATE, _),
|
("enabled", Enabled, enabled, IN_ENABLED_STATE, _),
|
||||||
("focus", Focus, focus, IN_FOCUS_STATE, _),
|
("focus", Focus, focus, IN_FOCUS_STATE, _),
|
||||||
|
|
|
@ -187,6 +187,9 @@ impl NonTSPseudoClass {
|
||||||
NonTSPseudoClass::Fullscreen => unsafe {
|
NonTSPseudoClass::Fullscreen => unsafe {
|
||||||
mozilla::StaticPrefs_sVarCache_full_screen_api_unprefix_enabled
|
mozilla::StaticPrefs_sVarCache_full_screen_api_unprefix_enabled
|
||||||
},
|
},
|
||||||
|
NonTSPseudoClass::Defined => unsafe {
|
||||||
|
structs::nsContentUtils_sIsCustomElementsEnabled
|
||||||
|
},
|
||||||
// Otherwise, a pseudo-class is enabled in content when it
|
// Otherwise, a pseudo-class is enabled in content when it
|
||||||
// doesn't have any enabled flag.
|
// doesn't have any enabled flag.
|
||||||
_ => !self.has_any_flag(
|
_ => !self.has_any_flag(
|
||||||
|
|
|
@ -37,6 +37,7 @@ use gecko_bindings::bindings::Gecko_ElementHasAnimations;
|
||||||
use gecko_bindings::bindings::Gecko_ElementHasCSSAnimations;
|
use gecko_bindings::bindings::Gecko_ElementHasCSSAnimations;
|
||||||
use gecko_bindings::bindings::Gecko_ElementHasCSSTransitions;
|
use gecko_bindings::bindings::Gecko_ElementHasCSSTransitions;
|
||||||
use gecko_bindings::bindings::Gecko_GetActiveLinkAttrDeclarationBlock;
|
use gecko_bindings::bindings::Gecko_GetActiveLinkAttrDeclarationBlock;
|
||||||
|
use gecko_bindings::bindings::Gecko_GetAnimationEffectCount;
|
||||||
use gecko_bindings::bindings::Gecko_GetAnimationRule;
|
use gecko_bindings::bindings::Gecko_GetAnimationRule;
|
||||||
use gecko_bindings::bindings::Gecko_GetExtraContentStyleDeclarations;
|
use gecko_bindings::bindings::Gecko_GetExtraContentStyleDeclarations;
|
||||||
use gecko_bindings::bindings::Gecko_GetHTMLPresentationAttrDeclarationBlock;
|
use gecko_bindings::bindings::Gecko_GetHTMLPresentationAttrDeclarationBlock;
|
||||||
|
@ -650,7 +651,10 @@ impl<'le> GeckoElement<'le> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extended_slots(&self) -> Option<&structs::FragmentOrElement_nsExtendedDOMSlots> {
|
fn extended_slots(&self) -> Option<&structs::FragmentOrElement_nsExtendedDOMSlots> {
|
||||||
self.dom_slots().and_then(|s| unsafe {
|
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()
|
.as_ref()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -948,8 +952,16 @@ fn get_animation_rule(
|
||||||
cascade_level: CascadeLevel,
|
cascade_level: CascadeLevel,
|
||||||
) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
||||||
use gecko_bindings::sugar::ownership::HasSimpleFFI;
|
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.
|
// 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 {
|
if unsafe {
|
||||||
Gecko_GetAnimationRule(
|
Gecko_GetAnimationRule(
|
||||||
element.0,
|
element.0,
|
||||||
|
@ -2115,6 +2127,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
||||||
{
|
{
|
||||||
use selectors::matching::*;
|
use selectors::matching::*;
|
||||||
match *pseudo_class {
|
match *pseudo_class {
|
||||||
|
NonTSPseudoClass::Defined |
|
||||||
NonTSPseudoClass::Focus |
|
NonTSPseudoClass::Focus |
|
||||||
NonTSPseudoClass::Enabled |
|
NonTSPseudoClass::Enabled |
|
||||||
NonTSPseudoClass::Disabled |
|
NonTSPseudoClass::Disabled |
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
//! Rust helpers for Gecko's `nsCSSShadowItem`.
|
//! Rust helpers for Gecko's `nsCSSShadowItem`.
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
|
|
||||||
use gecko_bindings::structs::nsCSSShadowItem;
|
use gecko_bindings::structs::nsCSSShadowItem;
|
||||||
use values::computed::RGBAColor;
|
|
||||||
use values::computed::effects::{BoxShadow, SimpleShadow};
|
use values::computed::effects::{BoxShadow, SimpleShadow};
|
||||||
|
|
||||||
impl nsCSSShadowItem {
|
impl nsCSSShadowItem {
|
||||||
|
@ -37,31 +35,14 @@ impl nsCSSShadowItem {
|
||||||
self.mRadius = shadow.blur.0.to_i32_au();
|
self.mRadius = shadow.blur.0.to_i32_au();
|
||||||
self.mSpread = 0;
|
self.mSpread = 0;
|
||||||
self.mInset = false;
|
self.mInset = false;
|
||||||
if let Some(color) = shadow.color {
|
self.mColor = shadow.color.into();
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a simple shadow from this item.
|
/// Gets a simple shadow from this item.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extract_simple_shadow(&self) -> SimpleShadow {
|
fn extract_simple_shadow(&self) -> SimpleShadow {
|
||||||
SimpleShadow {
|
SimpleShadow {
|
||||||
color: self.extract_color(),
|
color: self.mColor.into(),
|
||||||
horizontal: Au(self.mXOffset).into(),
|
horizontal: Au(self.mXOffset).into(),
|
||||||
vertical: Au(self.mYOffset).into(),
|
vertical: Au(self.mYOffset).into(),
|
||||||
blur: Au(self.mRadius).into(),
|
blur: Au(self.mRadius).into(),
|
||||||
|
|
192
components/style/media_queries/media_condition.rs
Normal file
192
components/style/media_queries/media_condition.rs
Normal file
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -73,16 +73,14 @@ impl MediaList {
|
||||||
|
|
||||||
/// Evaluate a whole `MediaList` against `Device`.
|
/// Evaluate a whole `MediaList` against `Device`.
|
||||||
pub fn evaluate(&self, device: &Device, quirks_mode: QuirksMode) -> bool {
|
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
|
// https://drafts.csswg.org/mediaqueries-4/#mq-list
|
||||||
self.media_queries.is_empty() || self.media_queries.iter().any(|mq| {
|
self.media_queries.is_empty() || self.media_queries.iter().any(|mq| {
|
||||||
let media_match = mq.media_type.matches(device.media_type());
|
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 &&
|
let query_match = media_match &&
|
||||||
mq.expressions
|
mq.condition.as_ref().map_or(true, |c| c.matches(device, quirks_mode));
|
||||||
.iter()
|
|
||||||
.all(|expression| expression.matches(&device, quirks_mode));
|
|
||||||
|
|
||||||
// Apply the logical NOT qualifier to the result
|
// Apply the logical NOT qualifier to the result
|
||||||
match mq.qualifier {
|
match mq.qualifier {
|
||||||
|
|
|
@ -9,13 +9,13 @@
|
||||||
use Atom;
|
use Atom;
|
||||||
use cssparser::Parser;
|
use cssparser::Parser;
|
||||||
use parser::ParserContext;
|
use parser::ParserContext;
|
||||||
use selectors::parser::SelectorParseErrorKind;
|
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use str::string_as_ascii_lowercase;
|
use str::string_as_ascii_lowercase;
|
||||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
use style_traits::{CssWriter, ParseError, ToCss};
|
||||||
use super::Expression;
|
use super::media_condition::MediaCondition;
|
||||||
use values::CustomIdent;
|
use values::CustomIdent;
|
||||||
|
|
||||||
|
|
||||||
/// <https://drafts.csswg.org/mediaqueries/#mq-prefix>
|
/// <https://drafts.csswg.org/mediaqueries/#mq-prefix>
|
||||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
|
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
|
||||||
pub enum Qualifier {
|
pub enum Qualifier {
|
||||||
|
@ -65,8 +65,9 @@ pub struct MediaQuery {
|
||||||
pub qualifier: Option<Qualifier>,
|
pub qualifier: Option<Qualifier>,
|
||||||
/// The media type for this query, that can be known, unknown, or "all".
|
/// The media type for this query, that can be known, unknown, or "all".
|
||||||
pub media_type: MediaQueryType,
|
pub media_type: MediaQueryType,
|
||||||
/// The set of expressions that this media query contains.
|
/// The condition that this media query contains. This cannot have `or`
|
||||||
pub expressions: Vec<Expression>,
|
/// in the first level.
|
||||||
|
pub condition: Option<MediaCondition>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToCss for MediaQuery {
|
impl ToCss for MediaQuery {
|
||||||
|
@ -86,28 +87,23 @@ impl ToCss for MediaQuery {
|
||||||
//
|
//
|
||||||
// Otherwise, we'd serialize media queries like "(min-width:
|
// Otherwise, we'd serialize media queries like "(min-width:
|
||||||
// 40px)" in "all (min-width: 40px)", which is unexpected.
|
// 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")?;
|
dest.write_str("all")?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MediaQueryType::Concrete(MediaType(ref desc)) => desc.to_css(dest)?,
|
MediaQueryType::Concrete(MediaType(ref desc)) => desc.to_css(dest)?,
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.expressions.is_empty() {
|
let condition = match self.condition {
|
||||||
return Ok(());
|
Some(ref c) => c,
|
||||||
}
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
if self.media_type != MediaQueryType::All || self.qualifier.is_some() {
|
if self.media_type != MediaQueryType::All || self.qualifier.is_some() {
|
||||||
dest.write_str(" and ")?;
|
dest.write_str(" and ")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.expressions[0].to_css(dest)?;
|
condition.to_css(dest)
|
||||||
|
|
||||||
for expr in self.expressions.iter().skip(1) {
|
|
||||||
dest.write_str(" and ")?;
|
|
||||||
expr.to_css(dest)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +114,7 @@ impl MediaQuery {
|
||||||
Self {
|
Self {
|
||||||
qualifier: Some(Qualifier::Not),
|
qualifier: Some(Qualifier::Not),
|
||||||
media_type: MediaQueryType::All,
|
media_type: MediaQueryType::All,
|
||||||
expressions: vec![],
|
condition: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,41 +124,24 @@ impl MediaQuery {
|
||||||
pub fn parse<'i, 't>(
|
pub fn parse<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<MediaQuery, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
let mut expressions = vec![];
|
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 qualifier = input.try(Qualifier::parse).ok();
|
let condition = if explicit_media_type.is_none() {
|
||||||
let media_type = match input.try(|i| i.expect_ident_cloned()) {
|
Some(MediaCondition::parse(context, input)?)
|
||||||
Ok(ident) => MediaQueryType::parse(&*ident).map_err(|()| {
|
} else if input.try(|i| i.expect_ident_matching("and")).is_ok() {
|
||||||
input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))
|
Some(MediaCondition::parse_disallow_or(context, input)?)
|
||||||
})?,
|
} else {
|
||||||
Err(_) => {
|
None
|
||||||
// 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
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse any subsequent expressions
|
let media_type = explicit_media_type.unwrap_or(MediaQueryType::All);
|
||||||
loop {
|
Ok(Self { qualifier, media_type, condition })
|
||||||
if input
|
|
||||||
.try(|input| input.expect_ident_matching("and"))
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
return Ok(MediaQuery {
|
|
||||||
qualifier,
|
|
||||||
media_type,
|
|
||||||
expressions,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
expressions.push(Expression::parse(context, input)?)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,15 @@
|
||||||
//!
|
//!
|
||||||
//! [mq]: https://drafts.csswg.org/mediaqueries/
|
//! [mq]: https://drafts.csswg.org/mediaqueries/
|
||||||
|
|
||||||
|
mod media_condition;
|
||||||
mod media_list;
|
mod media_list;
|
||||||
mod media_query;
|
mod media_query;
|
||||||
|
|
||||||
|
pub use self::media_condition::MediaCondition;
|
||||||
pub use self::media_list::MediaList;
|
pub use self::media_list::MediaList;
|
||||||
pub use self::media_query::{MediaQuery, MediaQueryType, MediaType, Qualifier};
|
pub use self::media_query::{MediaQuery, MediaQueryType, MediaType, Qualifier};
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
pub use servo::media_queries::{Device, Expression};
|
pub use servo::media_queries::{Device, MediaFeatureExpression};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub use gecko::media_queries::{Device, Expression};
|
pub use gecko::media_queries::{Device, MediaFeatureExpression};
|
||||||
|
|
|
@ -5489,8 +5489,7 @@ clip-path
|
||||||
use values::generics::CounterStyleOrNone;
|
use values::generics::CounterStyleOrNone;
|
||||||
use gecko_bindings::structs::nsStyleContentData;
|
use gecko_bindings::structs::nsStyleContentData;
|
||||||
use gecko_bindings::structs::nsStyleContentAttr;
|
use gecko_bindings::structs::nsStyleContentAttr;
|
||||||
use gecko_bindings::structs::nsStyleContentType;
|
use gecko_bindings::structs::StyleContentType;
|
||||||
use gecko_bindings::structs::nsStyleContentType::*;
|
|
||||||
use gecko_bindings::bindings::Gecko_ClearAndResizeStyleContents;
|
use gecko_bindings::bindings::Gecko_ClearAndResizeStyleContents;
|
||||||
|
|
||||||
// Converts a string as utf16, and returns an owned, zero-terminated raw buffer.
|
// Converts a string as utf16, and returns an owned, zero-terminated raw buffer.
|
||||||
|
@ -5505,19 +5504,19 @@ clip-path
|
||||||
|
|
||||||
fn set_counter_function(
|
fn set_counter_function(
|
||||||
data: &mut nsStyleContentData,
|
data: &mut nsStyleContentData,
|
||||||
content_type: nsStyleContentType,
|
content_type: StyleContentType,
|
||||||
name: &CustomIdent,
|
name: &CustomIdent,
|
||||||
sep: &str,
|
sep: &str,
|
||||||
style: CounterStyleOrNone,
|
style: CounterStyleOrNone,
|
||||||
device: &Device,
|
device: &Device,
|
||||||
) {
|
) {
|
||||||
debug_assert!(content_type == eStyleContentType_Counter ||
|
debug_assert!(content_type == StyleContentType::Counter ||
|
||||||
content_type == eStyleContentType_Counters);
|
content_type == StyleContentType::Counters);
|
||||||
let counter_func = unsafe {
|
let counter_func = unsafe {
|
||||||
bindings::Gecko_SetCounterFunction(data, content_type).as_mut().unwrap()
|
bindings::Gecko_SetCounterFunction(data, content_type).as_mut().unwrap()
|
||||||
};
|
};
|
||||||
counter_func.mIdent.assign(name.0.as_slice());
|
counter_func.mIdent.assign(name.0.as_slice());
|
||||||
if content_type == eStyleContentType_Counters {
|
if content_type == StyleContentType::Counters {
|
||||||
counter_func.mSeparator.assign_utf8(sep);
|
counter_func.mSeparator.assign_utf8(sep);
|
||||||
}
|
}
|
||||||
style.to_gecko_value(&mut counter_func.mCounterStyle, device);
|
style.to_gecko_value(&mut counter_func.mCounterStyle, device);
|
||||||
|
@ -5538,7 +5537,7 @@ clip-path
|
||||||
Gecko_ClearAndResizeStyleContents(&mut self.gecko, 1);
|
Gecko_ClearAndResizeStyleContents(&mut self.gecko, 1);
|
||||||
*self.gecko.mContents[0].mContent.mString.as_mut() = ptr::null_mut();
|
*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) => {
|
Content::Items(items) => {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -5554,7 +5553,7 @@ clip-path
|
||||||
}
|
}
|
||||||
match *item {
|
match *item {
|
||||||
ContentItem::String(ref value) => {
|
ContentItem::String(ref value) => {
|
||||||
self.gecko.mContents[i].mType = eStyleContentType_String;
|
self.gecko.mContents[i].mType = StyleContentType::String;
|
||||||
unsafe {
|
unsafe {
|
||||||
// NB: we share allocators, so doing this is fine.
|
// NB: we share allocators, so doing this is fine.
|
||||||
*self.gecko.mContents[i].mContent.mString.as_mut() =
|
*self.gecko.mContents[i].mContent.mString.as_mut() =
|
||||||
|
@ -5562,7 +5561,7 @@ clip-path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ContentItem::Attr(ref attr) => {
|
ContentItem::Attr(ref attr) => {
|
||||||
self.gecko.mContents[i].mType = eStyleContentType_Attr;
|
self.gecko.mContents[i].mType = StyleContentType::Attr;
|
||||||
unsafe {
|
unsafe {
|
||||||
// NB: we share allocators, so doing this is fine.
|
// NB: we share allocators, so doing this is fine.
|
||||||
let maybe_ns = attr.namespace.clone();
|
let maybe_ns = attr.namespace.clone();
|
||||||
|
@ -5581,17 +5580,17 @@ clip-path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ContentItem::OpenQuote
|
ContentItem::OpenQuote
|
||||||
=> self.gecko.mContents[i].mType = eStyleContentType_OpenQuote,
|
=> self.gecko.mContents[i].mType = StyleContentType::OpenQuote,
|
||||||
ContentItem::CloseQuote
|
ContentItem::CloseQuote
|
||||||
=> self.gecko.mContents[i].mType = eStyleContentType_CloseQuote,
|
=> self.gecko.mContents[i].mType = StyleContentType::CloseQuote,
|
||||||
ContentItem::NoOpenQuote
|
ContentItem::NoOpenQuote
|
||||||
=> self.gecko.mContents[i].mType = eStyleContentType_NoOpenQuote,
|
=> self.gecko.mContents[i].mType = StyleContentType::NoOpenQuote,
|
||||||
ContentItem::NoCloseQuote
|
ContentItem::NoCloseQuote
|
||||||
=> self.gecko.mContents[i].mType = eStyleContentType_NoCloseQuote,
|
=> self.gecko.mContents[i].mType = StyleContentType::NoCloseQuote,
|
||||||
ContentItem::Counter(ref name, ref style) => {
|
ContentItem::Counter(ref name, ref style) => {
|
||||||
set_counter_function(
|
set_counter_function(
|
||||||
&mut self.gecko.mContents[i],
|
&mut self.gecko.mContents[i],
|
||||||
eStyleContentType_Counter,
|
StyleContentType::Counter,
|
||||||
&name,
|
&name,
|
||||||
"",
|
"",
|
||||||
style.clone(),
|
style.clone(),
|
||||||
|
@ -5601,7 +5600,7 @@ clip-path
|
||||||
ContentItem::Counters(ref name, ref sep, ref style) => {
|
ContentItem::Counters(ref name, ref sep, ref style) => {
|
||||||
set_counter_function(
|
set_counter_function(
|
||||||
&mut self.gecko.mContents[i],
|
&mut self.gecko.mContents[i],
|
||||||
eStyleContentType_Counters,
|
StyleContentType::Counters,
|
||||||
&name,
|
&name,
|
||||||
&sep,
|
&sep,
|
||||||
style.clone(),
|
style.clone(),
|
||||||
|
@ -5636,7 +5635,7 @@ clip-path
|
||||||
pub fn clone_content(&self) -> longhands::content::computed_value::T {
|
pub fn clone_content(&self) -> longhands::content::computed_value::T {
|
||||||
use {Atom, Namespace};
|
use {Atom, Namespace};
|
||||||
use gecko::conversions::string_from_chars_pointer;
|
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::generics::counters::{Content, ContentItem};
|
||||||
use values::computed::url::ComputedImageUrl;
|
use values::computed::url::ComputedImageUrl;
|
||||||
use values::{CustomIdent, Either};
|
use values::{CustomIdent, Either};
|
||||||
|
@ -5644,27 +5643,27 @@ clip-path
|
||||||
use values::specified::Attr;
|
use values::specified::Attr;
|
||||||
|
|
||||||
if self.gecko.mContents.is_empty() {
|
if self.gecko.mContents.is_empty() {
|
||||||
return Content::Normal;
|
return Content::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.gecko.mContents.len() == 1 &&
|
if self.gecko.mContents.len() == 1 &&
|
||||||
self.gecko.mContents[0].mType == eStyleContentType_AltContent {
|
self.gecko.mContents[0].mType == StyleContentType::AltContent {
|
||||||
return Content::MozAltContent;
|
return Content::MozAltContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
Content::Items(
|
Content::Items(
|
||||||
self.gecko.mContents.iter().map(|gecko_content| {
|
self.gecko.mContents.iter().map(|gecko_content| {
|
||||||
match gecko_content.mType {
|
match gecko_content.mType {
|
||||||
eStyleContentType_OpenQuote => ContentItem::OpenQuote,
|
StyleContentType::OpenQuote => ContentItem::OpenQuote,
|
||||||
eStyleContentType_CloseQuote => ContentItem::CloseQuote,
|
StyleContentType::CloseQuote => ContentItem::CloseQuote,
|
||||||
eStyleContentType_NoOpenQuote => ContentItem::NoOpenQuote,
|
StyleContentType::NoOpenQuote => ContentItem::NoOpenQuote,
|
||||||
eStyleContentType_NoCloseQuote => ContentItem::NoCloseQuote,
|
StyleContentType::NoCloseQuote => ContentItem::NoCloseQuote,
|
||||||
eStyleContentType_String => {
|
StyleContentType::String => {
|
||||||
let gecko_chars = unsafe { gecko_content.mContent.mString.as_ref() };
|
let gecko_chars = unsafe { gecko_content.mContent.mString.as_ref() };
|
||||||
let string = unsafe { string_from_chars_pointer(*gecko_chars) };
|
let string = unsafe { string_from_chars_pointer(*gecko_chars) };
|
||||||
ContentItem::String(string.into_boxed_str())
|
ContentItem::String(string.into_boxed_str())
|
||||||
},
|
},
|
||||||
eStyleContentType_Attr => {
|
StyleContentType::Attr => {
|
||||||
let (namespace, attribute) = unsafe {
|
let (namespace, attribute) = unsafe {
|
||||||
let s = &**gecko_content.mContent.mAttr.as_ref();
|
let s = &**gecko_content.mContent.mAttr.as_ref();
|
||||||
let ns = if s.mNamespaceURL.mRawPtr.is_null() {
|
let ns = if s.mNamespaceURL.mRawPtr.is_null() {
|
||||||
|
@ -5678,7 +5677,7 @@ clip-path
|
||||||
};
|
};
|
||||||
ContentItem::Attr(Attr { namespace, attribute })
|
ContentItem::Attr(Attr { namespace, attribute })
|
||||||
},
|
},
|
||||||
eStyleContentType_Counter | eStyleContentType_Counters => {
|
StyleContentType::Counter | StyleContentType::Counters => {
|
||||||
let gecko_function =
|
let gecko_function =
|
||||||
unsafe { &**gecko_content.mContent.mCounters.as_ref() };
|
unsafe { &**gecko_content.mContent.mCounters.as_ref() };
|
||||||
let ident = CustomIdent(Atom::from(&*gecko_function.mIdent));
|
let ident = CustomIdent(Atom::from(&*gecko_function.mIdent));
|
||||||
|
@ -5689,14 +5688,14 @@ clip-path
|
||||||
Either::Second(_) =>
|
Either::Second(_) =>
|
||||||
unreachable!("counter function shouldn't have single string type"),
|
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)
|
ContentItem::Counter(ident, style)
|
||||||
} else {
|
} else {
|
||||||
let separator = gecko_function.mSeparator.to_string();
|
let separator = gecko_function.mSeparator.to_string();
|
||||||
ContentItem::Counters(ident, separator.into_boxed_str(), style)
|
ContentItem::Counters(ident, separator.into_boxed_str(), style)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
eStyleContentType_Image => {
|
StyleContentType::Image => {
|
||||||
unsafe {
|
unsafe {
|
||||||
let gecko_image_request =
|
let gecko_image_request =
|
||||||
&**gecko_content.mContent.mImage.as_ref();
|
&**gecko_content.mContent.mImage.as_ref();
|
||||||
|
|
|
@ -856,7 +856,7 @@
|
||||||
elif len(maybe_size) == 1:
|
elif len(maybe_size) == 1:
|
||||||
size = maybe_size[0]
|
size = maybe_size[0]
|
||||||
def phys_ident(side, phy_side):
|
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:
|
% if side is not None:
|
||||||
use logical_geometry::PhysicalSide;
|
use logical_geometry::PhysicalSide;
|
||||||
|
|
|
@ -21,14 +21,15 @@
|
||||||
servo_restyle_damage="reflow_out_of_flow"
|
servo_restyle_damage="reflow_out_of_flow"
|
||||||
)}
|
)}
|
||||||
% endfor
|
% endfor
|
||||||
// offset-* logical properties, map to "top" / "left" / "bottom" / "right"
|
// inset-* logical properties, map to "top" / "left" / "bottom" / "right"
|
||||||
% for side in LOGICAL_SIDES:
|
% for side in LOGICAL_SIDES:
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"offset-%s" % side,
|
"inset-%s" % side,
|
||||||
"LengthOrPercentageOrAuto",
|
"LengthOrPercentageOrAuto",
|
||||||
"computed::LengthOrPercentageOrAuto::Auto",
|
"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",
|
flags="GETCS_NEEDS_LAYOUT_FLUSH",
|
||||||
|
alias="offset-%s:layout.css.offset-logical-properties.enabled" % side,
|
||||||
animation_value_type="ComputedValue",
|
animation_value_type="ComputedValue",
|
||||||
logical=True,
|
logical=True,
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -100,6 +100,7 @@ pub mod longhands {
|
||||||
% for style_struct in data.style_structs:
|
% for style_struct in data.style_structs:
|
||||||
include!("${repr(os.path.join(OUT_DIR, 'longhands/{}.rs'.format(style_struct.name_lower)))[1:-1]}");
|
include!("${repr(os.path.join(OUT_DIR, 'longhands/{}.rs'.format(style_struct.name_lower)))[1:-1]}");
|
||||||
% endfor
|
% endfor
|
||||||
|
pub const ANIMATABLE_PROPERTY_COUNT: usize = ${sum(1 for prop in data.longhands if prop.animatable)};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! unwrap_or_initial {
|
macro_rules! unwrap_or_initial {
|
||||||
|
@ -177,7 +178,6 @@ pub mod shorthands {
|
||||||
data.declare_shorthand(
|
data.declare_shorthand(
|
||||||
"all",
|
"all",
|
||||||
logical_longhands + other_longhands,
|
logical_longhands + other_longhands,
|
||||||
gecko_pref="layout.css.all-shorthand.enabled",
|
|
||||||
spec="https://drafts.csswg.org/css-cascade-3/#all-shorthand"
|
spec="https://drafts.csswg.org/css-cascade-3/#all-shorthand"
|
||||||
)
|
)
|
||||||
%>
|
%>
|
||||||
|
@ -410,6 +410,7 @@ pub struct NonCustomPropertyId(usize);
|
||||||
|
|
||||||
impl NonCustomPropertyId {
|
impl NonCustomPropertyId {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
#[inline]
|
||||||
fn to_nscsspropertyid(self) -> nsCSSPropertyID {
|
fn to_nscsspropertyid(self) -> nsCSSPropertyID {
|
||||||
static MAP: [nsCSSPropertyID; ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())}] = [
|
static MAP: [nsCSSPropertyID; ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())}] = [
|
||||||
% for property in data.longhands + data.shorthands + 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. */
|
* they can be checked in the C++ side via ServoCSSPropList.h. */
|
||||||
/// This property can be animated on the compositor.
|
/// This property can be animated on the compositor.
|
||||||
const CAN_ANIMATE_ON_COMPOSITOR = 0;
|
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.
|
/// 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")]
|
#[cfg(feature = "gecko")]
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()> {
|
pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()> {
|
||||||
|
|
|
@ -196,6 +196,7 @@
|
||||||
</%helpers:shorthand>
|
</%helpers:shorthand>
|
||||||
|
|
||||||
<%helpers:shorthand name="background-position"
|
<%helpers:shorthand name="background-position"
|
||||||
|
flags="SHORTHAND_IN_GETCS"
|
||||||
sub_properties="background-position-x background-position-y"
|
sub_properties="background-position-x background-position-y"
|
||||||
spec="https://drafts.csswg.org/css-backgrounds-4/#the-background-position">
|
spec="https://drafts.csswg.org/css-backgrounds-4/#the-background-position">
|
||||||
use properties::longhands::{background_position_x, background_position_y};
|
use properties::longhands::{background_position_x, background_position_y};
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
<%helpers:shorthand
|
<%helpers:shorthand
|
||||||
name="overflow"
|
name="overflow"
|
||||||
|
flags="SHORTHAND_IN_GETCS"
|
||||||
sub_properties="overflow-x overflow-y"
|
sub_properties="overflow-x overflow-y"
|
||||||
spec="https://drafts.csswg.org/css-overflow/#propdef-overflow"
|
spec="https://drafts.csswg.org/css-overflow/#propdef-overflow"
|
||||||
>
|
>
|
||||||
|
|
|
@ -285,6 +285,7 @@
|
||||||
</%helpers:shorthand>
|
</%helpers:shorthand>
|
||||||
|
|
||||||
<%helpers:shorthand name="font-variant"
|
<%helpers:shorthand name="font-variant"
|
||||||
|
flags="SHORTHAND_IN_GETCS"
|
||||||
sub_properties="font-variant-caps
|
sub_properties="font-variant-caps
|
||||||
${'font-variant-alternates' if product == 'gecko' else ''}
|
${'font-variant-alternates' if product == 'gecko' else ''}
|
||||||
${'font-variant-east-asian' if product == 'gecko' else ''}
|
${'font-variant-east-asian' if product == 'gecko' else ''}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<%namespace name="helpers" file="/helpers.mako.rs" />
|
<%namespace name="helpers" file="/helpers.mako.rs" />
|
||||||
|
|
||||||
<%helpers:shorthand name="mask" products="gecko" extra_prefixes="webkit"
|
<%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
|
sub_properties="mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x
|
||||||
mask-position-y mask-size mask-image"
|
mask-position-y mask-size mask-image"
|
||||||
spec="https://drafts.fxtf.org/css-masking/#propdef-mask">
|
spec="https://drafts.fxtf.org/css-masking/#propdef-mask">
|
||||||
|
@ -182,6 +183,7 @@
|
||||||
</%helpers:shorthand>
|
</%helpers:shorthand>
|
||||||
|
|
||||||
<%helpers:shorthand name="mask-position" products="gecko" extra_prefixes="webkit"
|
<%helpers:shorthand name="mask-position" products="gecko" extra_prefixes="webkit"
|
||||||
|
flags="SHORTHAND_IN_GETCS"
|
||||||
sub_properties="mask-position-x mask-position-y"
|
sub_properties="mask-position-x mask-position-y"
|
||||||
spec="https://drafts.csswg.org/css-masks-4/#the-mask-position">
|
spec="https://drafts.csswg.org/css-masks-4/#the-mask-position">
|
||||||
use properties::longhands::{mask_position_x,mask_position_y};
|
use properties::longhands::{mask_position_x,mask_position_y};
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<%namespace name="helpers" file="/helpers.mako.rs" />
|
<%namespace name="helpers" file="/helpers.mako.rs" />
|
||||||
|
|
||||||
<%helpers:shorthand name="text-decoration"
|
<%helpers:shorthand name="text-decoration"
|
||||||
|
flags="SHORTHAND_IN_GETCS"
|
||||||
sub_properties="text-decoration-line
|
sub_properties="text-decoration-line
|
||||||
${' text-decoration-style text-decoration-color' if product == 'gecko' else ''}"
|
${' text-decoration-style text-decoration-color' if product == 'gecko' else ''}"
|
||||||
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration">
|
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration">
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
//! A data structure to efficiently index structs containing selectors by local
|
//! A data structure to efficiently index structs containing selectors by local
|
||||||
//! name, ids and hash.
|
//! name, ids and hash.
|
||||||
|
|
||||||
use {Atom, LocalName, WeakAtom};
|
use {Atom, LocalName, Namespace, WeakAtom};
|
||||||
use applicable_declarations::ApplicableDeclarationList;
|
use applicable_declarations::ApplicableDeclarationList;
|
||||||
use context::QuirksMode;
|
use context::QuirksMode;
|
||||||
use dom::TElement;
|
use dom::TElement;
|
||||||
|
@ -102,6 +102,8 @@ pub struct SelectorMap<T: 'static> {
|
||||||
pub class_hash: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[T; 1]>>,
|
pub class_hash: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[T; 1]>>,
|
||||||
/// A hash from local name to rules which contain that local name selector.
|
/// A hash from local name to rules which contain that local name selector.
|
||||||
pub local_name_hash: PrecomputedHashMap<LocalName, SmallVec<[T; 1]>>,
|
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.
|
/// Rules that don't have ID, class, or element selectors.
|
||||||
pub other: SmallVec<[T; 1]>,
|
pub other: SmallVec<[T; 1]>,
|
||||||
/// The number of entries in this map.
|
/// The number of entries in this map.
|
||||||
|
@ -125,6 +127,7 @@ impl<T: 'static> SelectorMap<T> {
|
||||||
id_hash: MaybeCaseInsensitiveHashMap::new(),
|
id_hash: MaybeCaseInsensitiveHashMap::new(),
|
||||||
class_hash: MaybeCaseInsensitiveHashMap::new(),
|
class_hash: MaybeCaseInsensitiveHashMap::new(),
|
||||||
local_name_hash: HashMap::default(),
|
local_name_hash: HashMap::default(),
|
||||||
|
namespace_hash: HashMap::default(),
|
||||||
other: SmallVec::new(),
|
other: SmallVec::new(),
|
||||||
count: 0,
|
count: 0,
|
||||||
}
|
}
|
||||||
|
@ -135,6 +138,7 @@ impl<T: 'static> SelectorMap<T> {
|
||||||
self.id_hash.clear();
|
self.id_hash.clear();
|
||||||
self.class_hash.clear();
|
self.class_hash.clear();
|
||||||
self.local_name_hash.clear();
|
self.local_name_hash.clear();
|
||||||
|
self.namespace_hash.clear();
|
||||||
self.other.clear();
|
self.other.clear();
|
||||||
self.count = 0;
|
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(
|
SelectorMap::get_matching_rules(
|
||||||
element,
|
element,
|
||||||
&self.other,
|
&self.other,
|
||||||
|
@ -261,7 +277,8 @@ impl SelectorMap<Rule> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: SelectorMapEntry> SelectorMap<T> {
|
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(
|
pub fn insert(
|
||||||
&mut self,
|
&mut self,
|
||||||
entry: T,
|
entry: T,
|
||||||
|
@ -298,13 +315,17 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
.try_entry(name.clone())?
|
.try_entry(name.clone())?
|
||||||
.or_insert_with(SmallVec::new)
|
.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,
|
Bucket::Universal => &mut self.other,
|
||||||
};
|
};
|
||||||
|
|
||||||
vector.try_push(entry)
|
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
|
/// Each entry is passed to the callback, which returns true to continue
|
||||||
/// iterating entries, or false to terminate the lookup.
|
/// iterating entries, or false to terminate the lookup.
|
||||||
|
@ -319,7 +340,6 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
E: TElement,
|
E: TElement,
|
||||||
F: FnMut(&'a T) -> bool,
|
F: FnMut(&'a T) -> bool,
|
||||||
{
|
{
|
||||||
// Id.
|
|
||||||
if let Some(id) = element.id() {
|
if let Some(id) = element.id() {
|
||||||
if let Some(v) = self.id_hash.get(id, quirks_mode) {
|
if let Some(v) = self.id_hash.get(id, quirks_mode) {
|
||||||
for entry in v.iter() {
|
for entry in v.iter() {
|
||||||
|
@ -330,7 +350,6 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Class.
|
|
||||||
let mut done = false;
|
let mut done = false;
|
||||||
element.each_class(|class| {
|
element.each_class(|class| {
|
||||||
if !done {
|
if !done {
|
||||||
|
@ -348,7 +367,6 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Local name.
|
|
||||||
if let Some(v) = self.local_name_hash.get(element.local_name()) {
|
if let Some(v) = self.local_name_hash.get(element.local_name()) {
|
||||||
for entry in v.iter() {
|
for entry in v.iter() {
|
||||||
if !f(&entry) {
|
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() {
|
for entry in self.other.iter() {
|
||||||
if !f(&entry) {
|
if !f(&entry) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -425,6 +450,7 @@ enum Bucket<'a> {
|
||||||
name: &'a LocalName,
|
name: &'a LocalName,
|
||||||
lower_name: &'a LocalName,
|
lower_name: &'a LocalName,
|
||||||
},
|
},
|
||||||
|
Namespace(&'a Namespace),
|
||||||
Universal,
|
Universal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,6 +462,8 @@ fn specific_bucket_for<'a>(component: &'a Component<SelectorImpl>) -> Bucket<'a>
|
||||||
name: &selector.name,
|
name: &selector.name,
|
||||||
lower_name: &selector.lower_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
|
// ::slotted(..) isn't a normal pseudo-element, so we can insert it on
|
||||||
// the rule hash normally without much problem. For example, in a
|
// the rule hash normally without much problem. For example, in a
|
||||||
// selector like:
|
// 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,
|
// We basically want to find the most specific bucket,
|
||||||
// where:
|
// where:
|
||||||
//
|
//
|
||||||
// id > class > local name > universal.
|
// id > class > local name > namespace > universal.
|
||||||
//
|
//
|
||||||
for ss in &mut iter {
|
for ss in &mut iter {
|
||||||
let new_bucket = specific_bucket_for(ss);
|
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;
|
current_bucket = new_bucket;
|
||||||
},
|
},
|
||||||
Bucket::LocalName { .. } => {
|
Bucket::LocalName { .. } => {
|
||||||
if matches!(current_bucket, Bucket::Universal) {
|
if matches!(current_bucket, Bucket::Universal | Bucket::Namespace(..)) {
|
||||||
current_bucket = new_bucket;
|
current_bucket = new_bucket;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Bucket::Namespace(..) => {
|
||||||
|
if matches!(current_bucket, Bucket::Universal) {
|
||||||
|
current_bucket = new_bucket;
|
||||||
|
}
|
||||||
|
}
|
||||||
Bucket::Universal => {},
|
Bucket::Universal => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,9 +170,9 @@ pub enum ExpressionKind {
|
||||||
/// <http://dev.w3.org/csswg/mediaqueries-3/#media1>
|
/// <http://dev.w3.org/csswg/mediaqueries-3/#media1>
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
#[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.
|
/// The kind of expression we're, just for unit testing.
|
||||||
///
|
///
|
||||||
/// Eventually this will become servo-only.
|
/// Eventually this will become servo-only.
|
||||||
|
@ -183,34 +183,43 @@ impl Expression {
|
||||||
/// Parse a media expression of the form:
|
/// 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>(
|
pub fn parse<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
input.expect_parenthesis_block()?;
|
input.expect_parenthesis_block()?;
|
||||||
input.parse_nested_block(|input| {
|
input.parse_nested_block(|input| {
|
||||||
let name = input.expect_ident_cloned()?;
|
Self::parse_in_parenthesis_block(context, input)
|
||||||
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())))
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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
|
/// Evaluate this expression and return whether it matches the current
|
||||||
/// device.
|
/// device.
|
||||||
pub fn matches(&self, device: &Device, quirks_mode: QuirksMode) -> bool {
|
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
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
where
|
where
|
||||||
W: Write,
|
W: Write,
|
||||||
|
@ -246,8 +255,8 @@ impl ToCss for Expression {
|
||||||
|
|
||||||
/// An enumeration that represents a ranged value.
|
/// An enumeration that represents a ranged value.
|
||||||
///
|
///
|
||||||
/// Only public for testing, implementation details of `Expression` may change
|
/// Only public for testing, implementation details of `MediaFeatureExpression`
|
||||||
/// for Stylo.
|
/// may change for Stylo.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
||||||
pub enum Range<T> {
|
pub enum Range<T> {
|
||||||
|
|
|
@ -428,6 +428,7 @@ macro_rules! font_feature_values_blocks {
|
||||||
fn parse_block<'t>(
|
fn parse_block<'t>(
|
||||||
&mut self,
|
&mut self,
|
||||||
prelude: BlockType,
|
prelude: BlockType,
|
||||||
|
_location: SourceLocation,
|
||||||
input: &mut Parser<'i, 't>
|
input: &mut Parser<'i, 't>
|
||||||
) -> Result<Self::AtRule, ParseError<'i>> {
|
) -> Result<Self::AtRule, ParseError<'i>> {
|
||||||
debug_assert_eq!(self.context.rule_type(), CssRuleType::FontFeatureValues);
|
debug_assert_eq!(self.context.rule_type(), CssRuleType::FontFeatureValues);
|
||||||
|
|
|
@ -509,14 +509,8 @@ impl<'a, 'i> AtRuleParser<'i> for KeyframeListParser<'a> {
|
||||||
type Error = StyleParseErrorKind<'i>;
|
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> {
|
impl<'a, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a> {
|
||||||
type Prelude = KeyframeSelectorParserPrelude;
|
type Prelude = KeyframeSelector;
|
||||||
type QualifiedRule = Arc<Locked<Keyframe>>;
|
type QualifiedRule = Arc<Locked<Keyframe>>;
|
||||||
type Error = StyleParseErrorKind<'i>;
|
type Error = StyleParseErrorKind<'i>;
|
||||||
|
|
||||||
|
@ -525,27 +519,21 @@ impl<'a, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a> {
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<Self::Prelude, ParseError<'i>> {
|
) -> Result<Self::Prelude, ParseError<'i>> {
|
||||||
let start_position = input.position();
|
let start_position = input.position();
|
||||||
let start_location = input.current_source_location();
|
KeyframeSelector::parse(input).map_err(|e| {
|
||||||
match KeyframeSelector::parse(input) {
|
let location = e.location;
|
||||||
Ok(sel) => Ok(KeyframeSelectorParserPrelude {
|
let error = ContextualParseError::InvalidKeyframeRule(
|
||||||
selector: sel,
|
input.slice_from(start_position),
|
||||||
source_location: start_location,
|
e.clone(),
|
||||||
}),
|
|
||||||
Err(e) => {
|
|
||||||
let location = e.location;
|
|
||||||
let error = ContextualParseError::InvalidKeyframeRule(
|
|
||||||
input.slice_from(start_position),
|
|
||||||
e.clone(),
|
|
||||||
);
|
);
|
||||||
self.context.log_css_error(location, error);
|
self.context.log_css_error(location, error);
|
||||||
Err(e)
|
e
|
||||||
},
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_block<'t>(
|
fn parse_block<'t>(
|
||||||
&mut self,
|
&mut self,
|
||||||
prelude: Self::Prelude,
|
selector: Self::Prelude,
|
||||||
|
source_location: SourceLocation,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<Self::QualifiedRule, ParseError<'i>> {
|
) -> Result<Self::QualifiedRule, ParseError<'i>> {
|
||||||
let context = ParserContext::new_with_rule_type(
|
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.
|
// `parse_important` is not called here, `!important` is not allowed in keyframe blocks.
|
||||||
}
|
}
|
||||||
Ok(Arc::new(self.shared_lock.wrap(Keyframe {
|
Ok(Arc::new(self.shared_lock.wrap(Keyframe {
|
||||||
selector: prelude.selector,
|
selector,
|
||||||
block: Arc::new(self.shared_lock.wrap(block)),
|
block: Arc::new(self.shared_lock.wrap(block)),
|
||||||
source_location: prelude.source_location,
|
source_location,
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,31 +146,31 @@ pub enum VendorPrefix {
|
||||||
/// A rule prelude for at-rule with block.
|
/// A rule prelude for at-rule with block.
|
||||||
pub enum AtRuleBlockPrelude {
|
pub enum AtRuleBlockPrelude {
|
||||||
/// A @font-face rule prelude.
|
/// A @font-face rule prelude.
|
||||||
FontFace(SourceLocation),
|
FontFace,
|
||||||
/// A @font-feature-values rule prelude, with its FamilyName list.
|
/// 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.
|
/// A @counter-style rule prelude, with its counter style name.
|
||||||
CounterStyle(CustomIdent, SourceLocation),
|
CounterStyle(CustomIdent),
|
||||||
/// A @media rule prelude, with its media queries.
|
/// A @media rule prelude, with its media queries.
|
||||||
Media(Arc<Locked<MediaList>>, SourceLocation),
|
Media(Arc<Locked<MediaList>>),
|
||||||
/// An @supports rule, with its conditional
|
/// An @supports rule, with its conditional
|
||||||
Supports(SupportsCondition, SourceLocation),
|
Supports(SupportsCondition),
|
||||||
/// A @viewport rule prelude.
|
/// A @viewport rule prelude.
|
||||||
Viewport,
|
Viewport,
|
||||||
/// A @keyframes rule, with its animation name and vendor prefix if exists.
|
/// 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.
|
/// A @page rule prelude.
|
||||||
Page(SourceLocation),
|
Page,
|
||||||
/// A @document rule, with its conditional.
|
/// A @document rule, with its conditional.
|
||||||
Document(DocumentCondition, SourceLocation),
|
Document(DocumentCondition),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A rule prelude for at-rule without block.
|
/// A rule prelude for at-rule without block.
|
||||||
pub enum AtRuleNonBlockPrelude {
|
pub enum AtRuleNonBlockPrelude {
|
||||||
/// A @import rule prelude.
|
/// A @import rule prelude.
|
||||||
Import(CssUrl, Arc<Locked<MediaList>>, SourceLocation),
|
Import(CssUrl, Arc<Locked<MediaList>>),
|
||||||
/// A @namespace rule prelude.
|
/// A @namespace rule prelude.
|
||||||
Namespace(Option<Prefix>, Namespace, SourceLocation),
|
Namespace(Option<Prefix>, Namespace),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
|
impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
|
||||||
|
@ -184,7 +184,6 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
|
||||||
name: CowRcStr<'i>,
|
name: CowRcStr<'i>,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<AtRuleType<AtRuleNonBlockPrelude, AtRuleBlockPrelude>, ParseError<'i>> {
|
) -> Result<AtRuleType<AtRuleNonBlockPrelude, AtRuleBlockPrelude>, ParseError<'i>> {
|
||||||
let location = input.current_source_location();
|
|
||||||
match_ignore_ascii_case! { &*name,
|
match_ignore_ascii_case! { &*name,
|
||||||
"import" => {
|
"import" => {
|
||||||
if !self.check_state(State::Imports) {
|
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 = MediaList::parse(&self.context, input);
|
||||||
let media = Arc::new(self.shared_lock.wrap(media));
|
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));
|
return Ok(AtRuleType::WithoutBlock(prelude));
|
||||||
},
|
},
|
||||||
"namespace" => {
|
"namespace" => {
|
||||||
|
@ -215,7 +214,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
|
||||||
Err(e) => return Err(e.into()),
|
Err(e) => return Err(e.into()),
|
||||||
};
|
};
|
||||||
let url = Namespace::from(maybe_namespace.as_ref());
|
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));
|
return Ok(AtRuleType::WithoutBlock(prelude));
|
||||||
},
|
},
|
||||||
// @charset is removed by rust-cssparser if it’s the first rule in the stylesheet
|
// @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>(
|
fn parse_block<'t>(
|
||||||
&mut self,
|
&mut self,
|
||||||
prelude: AtRuleBlockPrelude,
|
prelude: AtRuleBlockPrelude,
|
||||||
|
location: SourceLocation,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<CssRule, ParseError<'i>> {
|
) -> 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;
|
self.state = State::Body;
|
||||||
rule
|
rule
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn rule_without_block(&mut self, prelude: AtRuleNonBlockPrelude) -> CssRule {
|
fn rule_without_block(
|
||||||
|
&mut self,
|
||||||
|
prelude: AtRuleNonBlockPrelude,
|
||||||
|
source_location: SourceLocation,
|
||||||
|
) -> CssRule {
|
||||||
match prelude {
|
match prelude {
|
||||||
AtRuleNonBlockPrelude::Import(url, media, location) => {
|
AtRuleNonBlockPrelude::Import(url, media) => {
|
||||||
let loader = self.loader
|
let loader = self.loader
|
||||||
.expect("Expected a stylesheet loader for @import");
|
.expect("Expected a stylesheet loader for @import");
|
||||||
|
|
||||||
let import_rule = loader.request_stylesheet(
|
let import_rule = loader.request_stylesheet(
|
||||||
url,
|
url,
|
||||||
location,
|
source_location,
|
||||||
&self.context,
|
&self.context,
|
||||||
&self.shared_lock,
|
&self.shared_lock,
|
||||||
media,
|
media,
|
||||||
|
@ -264,7 +268,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
|
||||||
self.state = State::Imports;
|
self.state = State::Imports;
|
||||||
CssRule::Import(import_rule)
|
CssRule::Import(import_rule)
|
||||||
},
|
},
|
||||||
AtRuleNonBlockPrelude::Namespace(prefix, url, source_location) => {
|
AtRuleNonBlockPrelude::Namespace(prefix, url) => {
|
||||||
let prefix = if let Some(prefix) = prefix {
|
let prefix = if let Some(prefix) = prefix {
|
||||||
self.namespaces.prefixes.insert(prefix.clone(), url.clone());
|
self.namespaces.prefixes.insert(prefix.clone(), url.clone());
|
||||||
Some(prefix)
|
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> {
|
impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a> {
|
||||||
type Prelude = QualifiedRuleParserPrelude;
|
type Prelude = SelectorList<SelectorImpl>;
|
||||||
type QualifiedRule = CssRule;
|
type QualifiedRule = CssRule;
|
||||||
type Error = StyleParseErrorKind<'i>;
|
type Error = StyleParseErrorKind<'i>;
|
||||||
|
|
||||||
|
@ -298,7 +297,7 @@ impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a> {
|
||||||
fn parse_prelude<'t>(
|
fn parse_prelude<'t>(
|
||||||
&mut self,
|
&mut self,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<QualifiedRuleParserPrelude, ParseError<'i>> {
|
) -> Result<Self::Prelude, ParseError<'i>> {
|
||||||
if !self.check_state(State::Body) {
|
if !self.check_state(State::Body) {
|
||||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
}
|
}
|
||||||
|
@ -309,10 +308,16 @@ impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_block<'t>(
|
fn parse_block<'t>(
|
||||||
&mut self,
|
&mut self,
|
||||||
prelude: QualifiedRuleParserPrelude,
|
prelude: Self::Prelude,
|
||||||
|
location: SourceLocation,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<CssRule, ParseError<'i>> {
|
) -> 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;
|
self.state = State::Body;
|
||||||
result
|
result
|
||||||
})
|
})
|
||||||
|
@ -373,20 +378,18 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
|
||||||
name: CowRcStr<'i>,
|
name: CowRcStr<'i>,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<AtRuleType<AtRuleNonBlockPrelude, AtRuleBlockPrelude>, ParseError<'i>> {
|
) -> Result<AtRuleType<AtRuleNonBlockPrelude, AtRuleBlockPrelude>, ParseError<'i>> {
|
||||||
let location = input.current_source_location();
|
|
||||||
|
|
||||||
match_ignore_ascii_case! { &*name,
|
match_ignore_ascii_case! { &*name,
|
||||||
"media" => {
|
"media" => {
|
||||||
let media_queries = MediaList::parse(self.context, input);
|
let media_queries = MediaList::parse(self.context, input);
|
||||||
let arc = Arc::new(self.shared_lock.wrap(media_queries));
|
let arc = Arc::new(self.shared_lock.wrap(media_queries));
|
||||||
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Media(arc, location)))
|
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Media(arc)))
|
||||||
},
|
},
|
||||||
"supports" => {
|
"supports" => {
|
||||||
let cond = SupportsCondition::parse(input)?;
|
let cond = SupportsCondition::parse(input)?;
|
||||||
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Supports(cond, location)))
|
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Supports(cond)))
|
||||||
},
|
},
|
||||||
"font-face" => {
|
"font-face" => {
|
||||||
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::FontFace(location)))
|
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::FontFace))
|
||||||
},
|
},
|
||||||
"font-feature-values" => {
|
"font-feature-values" => {
|
||||||
if !cfg!(feature = "gecko") {
|
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())))
|
return Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone())))
|
||||||
}
|
}
|
||||||
let family_names = parse_family_name_list(self.context, input)?;
|
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" => {
|
"counter-style" => {
|
||||||
if !cfg!(feature = "gecko") {
|
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())))
|
return Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone())))
|
||||||
}
|
}
|
||||||
let name = parse_counter_style_name_definition(input)?;
|
let name = parse_counter_style_name_definition(input)?;
|
||||||
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::CounterStyle(name, location)))
|
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::CounterStyle(name)))
|
||||||
},
|
},
|
||||||
"viewport" => {
|
"viewport" => {
|
||||||
if viewport_rule::enabled() {
|
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)?;
|
let name = KeyframesName::parse(self.context, input)?;
|
||||||
|
|
||||||
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Keyframes(name, prefix, location)))
|
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Keyframes(name, prefix)))
|
||||||
},
|
},
|
||||||
"page" => {
|
"page" => {
|
||||||
if cfg!(feature = "gecko") {
|
if cfg!(feature = "gecko") {
|
||||||
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Page(location)))
|
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Page))
|
||||||
} else {
|
} else {
|
||||||
Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone())))
|
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)?;
|
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())))
|
_ => 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>(
|
fn parse_block<'t>(
|
||||||
&mut self,
|
&mut self,
|
||||||
prelude: AtRuleBlockPrelude,
|
prelude: AtRuleBlockPrelude,
|
||||||
|
source_location: SourceLocation,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<CssRule, ParseError<'i>> {
|
) -> Result<CssRule, ParseError<'i>> {
|
||||||
match prelude {
|
match prelude {
|
||||||
AtRuleBlockPrelude::FontFace(location) => {
|
AtRuleBlockPrelude::FontFace => {
|
||||||
let context = ParserContext::new_with_rule_type(
|
let context = ParserContext::new_with_rule_type(
|
||||||
self.context,
|
self.context,
|
||||||
CssRuleType::FontFace,
|
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(
|
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(
|
let context = ParserContext::new_with_rule_type(
|
||||||
self.context,
|
self.context,
|
||||||
CssRuleType::FontFeatureValues,
|
CssRuleType::FontFeatureValues,
|
||||||
|
@ -478,11 +482,11 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
|
||||||
&context,
|
&context,
|
||||||
input,
|
input,
|
||||||
family_names,
|
family_names,
|
||||||
location,
|
source_location,
|
||||||
),
|
),
|
||||||
))))
|
))))
|
||||||
},
|
},
|
||||||
AtRuleBlockPrelude::CounterStyle(name, location) => {
|
AtRuleBlockPrelude::CounterStyle(name) => {
|
||||||
let context = ParserContext::new_with_rule_type(
|
let context = ParserContext::new_with_rule_type(
|
||||||
self.context,
|
self.context,
|
||||||
CssRuleType::CounterStyle,
|
CssRuleType::CounterStyle,
|
||||||
|
@ -495,19 +499,19 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
|
||||||
name,
|
name,
|
||||||
&context,
|
&context,
|
||||||
input,
|
input,
|
||||||
location,
|
source_location,
|
||||||
)?.into(),
|
)?.into(),
|
||||||
),
|
),
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
AtRuleBlockPrelude::Media(media_queries, source_location) => {
|
AtRuleBlockPrelude::Media(media_queries) => {
|
||||||
Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
|
Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
|
||||||
media_queries,
|
media_queries,
|
||||||
rules: self.parse_nested_rules(input, CssRuleType::Media),
|
rules: self.parse_nested_rules(input, CssRuleType::Media),
|
||||||
source_location,
|
source_location,
|
||||||
}))))
|
}))))
|
||||||
},
|
},
|
||||||
AtRuleBlockPrelude::Supports(condition, source_location) => {
|
AtRuleBlockPrelude::Supports(condition) => {
|
||||||
let eval_context = ParserContext::new_with_rule_type(
|
let eval_context = ParserContext::new_with_rule_type(
|
||||||
self.context,
|
self.context,
|
||||||
CssRuleType::Style,
|
CssRuleType::Style,
|
||||||
|
@ -535,7 +539,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
|
||||||
ViewportRule::parse(&context, input)?,
|
ViewportRule::parse(&context, input)?,
|
||||||
))))
|
))))
|
||||||
},
|
},
|
||||||
AtRuleBlockPrelude::Keyframes(name, vendor_prefix, source_location) => {
|
AtRuleBlockPrelude::Keyframes(name, vendor_prefix) => {
|
||||||
let context = ParserContext::new_with_rule_type(
|
let context = ParserContext::new_with_rule_type(
|
||||||
self.context,
|
self.context,
|
||||||
CssRuleType::Keyframes,
|
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(
|
let context = ParserContext::new_with_rule_type(
|
||||||
self.context,
|
self.context,
|
||||||
CssRuleType::Page,
|
CssRuleType::Page,
|
||||||
|
@ -569,7 +573,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
|
||||||
source_location,
|
source_location,
|
||||||
}))))
|
}))))
|
||||||
},
|
},
|
||||||
AtRuleBlockPrelude::Document(condition, source_location) => {
|
AtRuleBlockPrelude::Document(condition) => {
|
||||||
if !cfg!(feature = "gecko") {
|
if !cfg!(feature = "gecko") {
|
||||||
unreachable!()
|
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> {
|
impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> {
|
||||||
type Prelude = QualifiedRuleParserPrelude;
|
type Prelude = SelectorList<SelectorImpl>;
|
||||||
type QualifiedRule = CssRule;
|
type QualifiedRule = CssRule;
|
||||||
type Error = StyleParseErrorKind<'i>;
|
type Error = StyleParseErrorKind<'i>;
|
||||||
|
|
||||||
fn parse_prelude<'t>(
|
fn parse_prelude<'t>(
|
||||||
&mut self,
|
&mut self,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<QualifiedRuleParserPrelude, ParseError<'i>> {
|
) -> Result<Self::Prelude, ParseError<'i>> {
|
||||||
let selector_parser = SelectorParser {
|
let selector_parser = SelectorParser {
|
||||||
stylesheet_origin: self.stylesheet_origin,
|
stylesheet_origin: self.stylesheet_origin,
|
||||||
namespaces: self.namespaces,
|
namespaces: self.namespaces,
|
||||||
url_data: Some(self.context.url_data),
|
url_data: Some(self.context.url_data),
|
||||||
};
|
};
|
||||||
|
SelectorList::parse(&selector_parser, input)
|
||||||
let source_location = input.current_source_location();
|
|
||||||
let selectors = SelectorList::parse(&selector_parser, input)?;
|
|
||||||
|
|
||||||
Ok(QualifiedRuleParserPrelude { selectors, source_location, })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_block<'t>(
|
fn parse_block<'t>(
|
||||||
&mut self,
|
&mut self,
|
||||||
prelude: QualifiedRuleParserPrelude,
|
selectors: Self::Prelude,
|
||||||
|
source_location: SourceLocation,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<CssRule, ParseError<'i>> {
|
) -> Result<CssRule, ParseError<'i>> {
|
||||||
let context =
|
let context =
|
||||||
ParserContext::new_with_rule_type(self.context, CssRuleType::Style, self.namespaces);
|
ParserContext::new_with_rule_type(self.context, CssRuleType::Style, self.namespaces);
|
||||||
|
|
||||||
let declarations = parse_property_declaration_list(&context, input);
|
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 {
|
Ok(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule {
|
||||||
selectors: prelude.selectors,
|
selectors,
|
||||||
block: Arc::new(self.shared_lock.wrap(declarations)),
|
block,
|
||||||
source_location: prelude.source_location,
|
source_location,
|
||||||
}))))
|
}))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,7 +186,7 @@ impl ComputeSquaredDistance for Color {
|
||||||
(Foreground, Foreground) => SquaredDistance::from_sqrt(0.),
|
(Foreground, Foreground) => SquaredDistance::from_sqrt(0.),
|
||||||
(Numeric(c1), Numeric(c2)) => c1.compute_squared_distance(&c2)?,
|
(Numeric(c1), Numeric(c2)) => c1.compute_squared_distance(&c2)?,
|
||||||
(Foreground, Numeric(color)) | (Numeric(color), Foreground) => {
|
(Foreground, Numeric(color)) | (Numeric(color), Foreground) => {
|
||||||
// `computed_squared_distance` is symmetic.
|
// `computed_squared_distance` is symmetric.
|
||||||
color.compute_squared_distance(&RGBA::transparent())?
|
color.compute_squared_distance(&RGBA::transparent())?
|
||||||
+ SquaredDistance::from_sqrt(1.)
|
+ SquaredDistance::from_sqrt(1.)
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,6 @@ impl ComputeSquaredDistance for Color {
|
||||||
impl ToAnimatedZero for Color {
|
impl ToAnimatedZero for Color {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||||
// FIXME(nox): This does not look correct to me.
|
Ok(RGBA::transparent().into())
|
||||||
Err(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#[cfg(not(feature = "gecko"))]
|
#[cfg(not(feature = "gecko"))]
|
||||||
use values::Impossible;
|
use values::Impossible;
|
||||||
use values::animated::color::RGBA;
|
use values::animated::color::Color;
|
||||||
use values::computed::{Angle, Number};
|
use values::computed::{Angle, Number};
|
||||||
use values::computed::length::Length;
|
use values::computed::length::Length;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -17,7 +17,7 @@ use values::generics::effects::Filter as GenericFilter;
|
||||||
use values::generics::effects::SimpleShadow as GenericSimpleShadow;
|
use values::generics::effects::SimpleShadow as GenericSimpleShadow;
|
||||||
|
|
||||||
/// An animated value for a single `box-shadow`.
|
/// 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`.
|
/// An animated value for a single `filter`.
|
||||||
#[cfg(feature = "gecko")]
|
#[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>;
|
pub type Filter = GenericFilter<Angle, Number, Length, Impossible, Impossible>;
|
||||||
|
|
||||||
/// An animated value for the `drop-shadow()` filter.
|
/// 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 {
|
impl ComputeSquaredDistance for BoxShadow {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#[cfg(not(feature = "gecko"))]
|
#[cfg(not(feature = "gecko"))]
|
||||||
use values::Impossible;
|
use values::Impossible;
|
||||||
use values::computed::{Angle, NonNegativeNumber};
|
use values::computed::{Angle, NonNegativeNumber};
|
||||||
use values::computed::color::RGBAColor;
|
use values::computed::color::Color;
|
||||||
use values::computed::length::{Length, NonNegativeLength};
|
use values::computed::length::{Length, NonNegativeLength};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use values::computed::url::ComputedUrl;
|
use values::computed::url::ComputedUrl;
|
||||||
|
@ -16,7 +16,7 @@ use values::generics::effects::Filter as GenericFilter;
|
||||||
use values::generics::effects::SimpleShadow as GenericSimpleShadow;
|
use values::generics::effects::SimpleShadow as GenericSimpleShadow;
|
||||||
|
|
||||||
/// A computed value for a single shadow of the `box-shadow` property.
|
/// 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`.
|
/// A computed value for a single `filter`.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -27,4 +27,4 @@ pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Sim
|
||||||
pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Impossible, Impossible>;
|
pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Impossible, Impossible>;
|
||||||
|
|
||||||
/// A computed value for the `drop-shadow()` filter.
|
/// A computed value for the `drop-shadow()` filter.
|
||||||
pub type SimpleShadow = GenericSimpleShadow<Option<RGBAColor>, Length, NonNegativeLength>;
|
pub type SimpleShadow = GenericSimpleShadow<Color, Length, NonNegativeLength>;
|
||||||
|
|
|
@ -122,7 +122,6 @@ impl<ImageUrl> Content<ImageUrl> {
|
||||||
pub fn normal() -> Self {
|
pub fn normal() -> Self {
|
||||||
Content::Normal
|
Content::Normal
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Items for the `content` property.
|
/// Items for the `content` property.
|
||||||
|
|
|
@ -17,14 +17,14 @@ use values::generics::effects::BoxShadow as GenericBoxShadow;
|
||||||
use values::generics::effects::Filter as GenericFilter;
|
use values::generics::effects::Filter as GenericFilter;
|
||||||
use values::generics::effects::SimpleShadow as GenericSimpleShadow;
|
use values::generics::effects::SimpleShadow as GenericSimpleShadow;
|
||||||
use values::specified::{Angle, NumberOrPercentage};
|
use values::specified::{Angle, NumberOrPercentage};
|
||||||
use values::specified::color::RGBAColor;
|
use values::specified::color::Color;
|
||||||
use values::specified::length::{Length, NonNegativeLength};
|
use values::specified::length::{Length, NonNegativeLength};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use values::specified::url::SpecifiedUrl;
|
use values::specified::url::SpecifiedUrl;
|
||||||
|
|
||||||
/// A specified value for a single shadow of the `box-shadow` property.
|
/// A specified value for a single shadow of the `box-shadow` property.
|
||||||
pub type BoxShadow =
|
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`.
|
/// A specified value for a single `filter`.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -93,7 +93,7 @@ impl ToComputedValue for Factor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A specified value for the `drop-shadow()` filter.
|
/// 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 {
|
impl Parse for BoxShadow {
|
||||||
fn parse<'i, 't>(
|
fn parse<'i, 't>(
|
||||||
|
@ -135,7 +135,7 @@ impl Parse for BoxShadow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if color.is_none() {
|
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);
|
color = Some(value);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -249,16 +249,18 @@ impl Parse for SimpleShadow {
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> 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 horizontal = Length::parse(context, input)?;
|
||||||
let vertical = Length::parse(context, input)?;
|
let vertical = Length::parse(context, input)?;
|
||||||
let blur = input.try(|i| Length::parse_non_negative(context, i)).ok();
|
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 {
|
Ok(SimpleShadow {
|
||||||
color: color,
|
color,
|
||||||
horizontal: horizontal,
|
horizontal,
|
||||||
vertical: vertical,
|
vertical,
|
||||||
blur: blur.map(NonNegative::<Length>),
|
blur,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,7 +271,10 @@ impl ToComputedValue for SimpleShadow {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||||
ComputedSimpleShadow {
|
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),
|
horizontal: self.horizontal.to_computed_value(context),
|
||||||
vertical: self.vertical.to_computed_value(context),
|
vertical: self.vertical.to_computed_value(context),
|
||||||
blur: self.blur
|
blur: self.blur
|
||||||
|
@ -282,7 +287,7 @@ impl ToComputedValue for SimpleShadow {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||||
SimpleShadow {
|
SimpleShadow {
|
||||||
color: ToComputedValue::from_computed_value(&computed.color),
|
color: Some(ToComputedValue::from_computed_value(&computed.color)),
|
||||||
horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
|
horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
|
||||||
vertical: ToComputedValue::from_computed_value(&computed.vertical),
|
vertical: ToComputedValue::from_computed_value(&computed.vertical),
|
||||||
blur: Some(ToComputedValue::from_computed_value(&computed.blur)),
|
blur: Some(ToComputedValue::from_computed_value(&computed.blur)),
|
||||||
|
|
|
@ -8,7 +8,7 @@ use app_units::Au;
|
||||||
use cssparser::{Delimiter, Parser, Token};
|
use cssparser::{Delimiter, Parser, Token};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
|
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 parser::{Parse, ParserContext};
|
||||||
use selectors::context::QuirksMode;
|
use selectors::context::QuirksMode;
|
||||||
use style_traits::ParseError;
|
use style_traits::ParseError;
|
||||||
|
@ -19,9 +19,7 @@ use values::specified::{Length, NoCalcLength, ViewportPercentageLength};
|
||||||
///
|
///
|
||||||
/// https://html.spec.whatwg.org/multipage/#source-size
|
/// https://html.spec.whatwg.org/multipage/#source-size
|
||||||
pub struct SourceSize {
|
pub struct SourceSize {
|
||||||
// FIXME(emilio): This should be a `MediaCondition`, and support `and` and
|
condition: MediaCondition,
|
||||||
// `or`.
|
|
||||||
condition: MediaExpression,
|
|
||||||
value: Length,
|
value: Length,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +28,7 @@ impl Parse for SourceSize {
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
let condition = MediaExpression::parse(context, input)?;
|
let condition = MediaCondition::parse(context, input)?;
|
||||||
let value = Length::parse_non_negative(context, input)?;
|
let value = Length::parse_non_negative(context, input)?;
|
||||||
|
|
||||||
Ok(Self { condition, value })
|
Ok(Self { condition, value })
|
||||||
|
|
|
@ -15,7 +15,7 @@ gecko = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
app_units = "0.6"
|
app_units = "0.6"
|
||||||
cssparser = "0.23.0"
|
cssparser = "0.24.0"
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
euclid = "0.17"
|
euclid = "0.17"
|
||||||
malloc_size_of = { path = "../malloc_size_of" }
|
malloc_size_of = { path = "../malloc_size_of" }
|
||||||
|
|
|
@ -106,12 +106,12 @@ pub enum StyleParseErrorKind<'i> {
|
||||||
PropertyDeclarationValueNotExhausted,
|
PropertyDeclarationValueNotExhausted,
|
||||||
/// An unexpected dimension token was encountered.
|
/// An unexpected dimension token was encountered.
|
||||||
UnexpectedDimension(CowRcStr<'i>),
|
UnexpectedDimension(CowRcStr<'i>),
|
||||||
/// Expected identifier not found.
|
|
||||||
ExpectedIdentifier(Token<'i>),
|
|
||||||
/// Missing or invalid media feature name.
|
/// Missing or invalid media feature name.
|
||||||
MediaQueryExpectedFeatureName(CowRcStr<'i>),
|
MediaQueryExpectedFeatureName(CowRcStr<'i>),
|
||||||
/// Missing or invalid media feature value.
|
/// Missing or invalid media feature value.
|
||||||
MediaQueryExpectedFeatureValue,
|
MediaQueryExpectedFeatureValue,
|
||||||
|
/// A media feature range operator was not expected.
|
||||||
|
MediaQueryUnexpectedOperator,
|
||||||
/// min- or max- properties must have a value.
|
/// min- or max- properties must have a value.
|
||||||
RangedExpressionWithNoValue,
|
RangedExpressionWithNoValue,
|
||||||
/// A function was encountered that was not expected.
|
/// A function was encountered that was not expected.
|
||||||
|
|
|
@ -13,4 +13,3 @@ euclid = "0.17"
|
||||||
msg = {path = "../../../components/msg"}
|
msg = {path = "../../../components/msg"}
|
||||||
script = {path = "../../../components/script"}
|
script = {path = "../../../components/script"}
|
||||||
servo_url = {path = "../../../components/url"}
|
servo_url = {path = "../../../components/url"}
|
||||||
style = {path = "../../../components/style"}
|
|
||||||
|
|
|
@ -2,104 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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 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]
|
#[test]
|
||||||
fn no_value() {
|
fn no_value() {
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#[cfg(test)] extern crate msg;
|
#[cfg(test)] extern crate msg;
|
||||||
#[cfg(test)] extern crate script;
|
#[cfg(test)] extern crate script;
|
||||||
#[cfg(test)] extern crate servo_url;
|
#[cfg(test)] extern crate servo_url;
|
||||||
#[cfg(test)] extern crate style;
|
|
||||||
|
|
||||||
#[cfg(test)] mod origin;
|
#[cfg(test)] mod origin;
|
||||||
#[cfg(all(test, target_pointer_width = "64"))] mod size_of;
|
#[cfg(all(test, target_pointer_width = "64"))] mod size_of;
|
||||||
|
|
|
@ -12,7 +12,7 @@ doctest = false
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.0"
|
byteorder = "1.0"
|
||||||
app_units = "0.6"
|
app_units = "0.6"
|
||||||
cssparser = "0.23.0"
|
cssparser = "0.24.0"
|
||||||
euclid = "0.17"
|
euclid = "0.17"
|
||||||
html5ever = "0.22"
|
html5ever = "0.22"
|
||||||
parking_lot = "0.5"
|
parking_lot = "0.5"
|
||||||
|
|
|
@ -27,7 +27,6 @@ mod attr;
|
||||||
mod custom_properties;
|
mod custom_properties;
|
||||||
mod keyframes;
|
mod keyframes;
|
||||||
mod logical_geometry;
|
mod logical_geometry;
|
||||||
mod media_queries;
|
|
||||||
mod parsing;
|
mod parsing;
|
||||||
mod properties;
|
mod properties;
|
||||||
mod rule_tree;
|
mod rule_tree;
|
||||||
|
@ -37,16 +36,3 @@ mod str;
|
||||||
mod stylesheets;
|
mod stylesheets;
|
||||||
mod stylist;
|
mod stylist;
|
||||||
mod viewport;
|
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(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue