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:
bors-servo 2018-07-01 05:14:21 -04:00 committed by GitHub
commit c71c55e542
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 872 additions and 1077 deletions

29
Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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(),
}, },
}, },

View file

@ -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 }

View file

@ -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"}

View file

@ -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");

View file

@ -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;

View file

@ -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;

View file

@ -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"

View file

@ -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"

View file

@ -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 |

View file

@ -576,7 +576,7 @@ where
_ => {}, _ => {},
} }
if element.is_link() || combinator.is_sibling() { if element.is_link() {
visited_handling = VisitedHandlingMode::AllLinksUnvisited; visited_handling = VisitedHandlingMode::AllLinksUnvisited;
} }

View file

@ -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,
}; };

View file

@ -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"

View file

@ -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());

View file

@ -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>

View file

@ -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
}
}
}
} }
} }
} }

View file

@ -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, _),

View file

@ -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(

View file

@ -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 |

View file

@ -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(),

View 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))
}
}
}
}
}
}

View file

@ -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 {

View file

@ -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)?)
}
} }
} }

View file

@ -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};

View file

@ -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();

View file

@ -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;

View file

@ -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,
)} )}

View file

@ -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, ()> {

View file

@ -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};

View file

@ -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"
> >

View file

@ -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 ''}

View file

@ -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};

View file

@ -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">

View file

@ -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 => {},
} }
} }

View file

@ -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> {

View file

@ -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);

View file

@ -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,
}))) })))
} }
} }

View file

@ -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 its the first rule in the stylesheet // @charset is removed by rust-cssparser if its 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,
})))) }))))
} }
} }

View file

@ -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(())
} }
} }

View file

@ -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]

View file

@ -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>;

View file

@ -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.

View file

@ -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)),

View file

@ -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 })

View file

@ -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" }

View file

@ -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.

View file

@ -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"}

View file

@ -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() {

View file

@ -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;

View file

@ -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"

View file

@ -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(),
)
}
}

View file

@ -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);
}