diff options
43 files changed, 931 insertions, 714 deletions
diff --git a/Cargo.lock b/Cargo.lock index 5c439fee0ea..29b9ef4a501 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,7 +271,7 @@ dependencies = [ "azure 0.29.0 (git+https://github.com/servo/rust-azure)", "canvas_traits 0.0.1", "compositing 0.0.1", - "cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -289,7 +289,7 @@ dependencies = [ name = "canvas_traits" version = "0.0.1" dependencies = [ - "cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.17.2 (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)", @@ -563,30 +563,32 @@ dependencies = [ [[package]] name = "cssparser" -version = "0.23.2" +version = "0.23.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser-macros 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "dtoa-short 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.3.4 (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)", "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)", "procedural-masquerade 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cssparser-macros" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "phf_codegen 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)", "procedural-masquerade 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1321,11 +1323,6 @@ dependencies = [ [[package]] name = "itoa" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "itoa" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1615,7 +1612,7 @@ name = "malloc_size_of" version = "0.0.1" dependencies = [ "app_units 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)", "hashglobe 0.1.0", "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1625,7 +1622,7 @@ dependencies = [ "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", "servo_arc 0.1.1", - "smallbitvec 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallbitvec 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2282,11 +2279,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "quote" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quote" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ @@ -2472,7 +2464,7 @@ dependencies = [ "chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "cookie 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)", "deny_public_fields 0.0.1", "devtools_traits 0.0.1", "dom_struct 0.0.1", @@ -2549,7 +2541,7 @@ dependencies = [ "app_units 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "canvas_traits 0.0.1", - "cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)", "gfx_traits 0.0.1", "html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2631,7 +2623,7 @@ name = "selectors" version = "0.19.0" dependencies = [ "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2921,7 +2913,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "smallbitvec" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2981,7 +2973,7 @@ dependencies = [ "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.23.10 (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.2 (registry+https://github.com/rust-lang/crates.io-index)", "fallible 0.0.1", @@ -3012,7 +3004,7 @@ dependencies = [ "servo_atoms 0.0.1", "servo_config 0.0.1", "servo_url 0.0.1", - "smallbitvec 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallbitvec 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "style_derive 0.0.1", @@ -3042,7 +3034,7 @@ version = "0.0.1" dependencies = [ "app_units 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.17.2 (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)", @@ -3064,7 +3056,7 @@ version = "0.0.1" dependencies = [ "app_units 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)", "malloc_size_of 0.0.1", "malloc_size_of_derive 0.0.1", @@ -3083,16 +3075,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ @@ -3102,14 +3084,6 @@ dependencies = [ ] [[package]] -name = "synom" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "synstructure" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3298,11 +3272,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unicode-xid" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3787,8 +3756,8 @@ dependencies = [ "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" -"checksum cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8a807ac3ab7a217829c2a3b65732b926b2befe6a35f33b4bf8b503692430f223" -"checksum cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "079adec4af52bb5275eadd004292028c79eb3c5f5b4ee8086a36d4197032f6df" +"checksum cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1c74d99b0f489cc546336b911452562ebfd4aec034b0c526cb77a3a02d3790" +"checksum cssparser-macros 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f3a5383ae18dbfdeb569ed62019f5bddb2a95cd2d3833313c475a0d014777805" "checksum darling 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a78af487e4eb8f4421a1770687b328af6bb4494ca93435210678c6eea875c11" "checksum darling_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b315f49c7b6db3708bca6e6913c194581a44ec619b7a39e131d4dd63733a3698" "checksum darling_macro 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eb69a38fdeaeaf3db712e1df170de67ee9dfc24fb88ca3e9d21e703ec25a4d8e" @@ -3853,7 +3822,6 @@ dependencies = [ "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum ipc-channel 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db9daf099728ac5390c73f54e6e3708f0c514d2b51f24373830f568702eadfca" "checksum itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b07332223953b5051bceb67e8c4700aa65291535568e1f12408c43c4a42c0394" -"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" "checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682" "checksum jemalloc-sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "479294d130502fada93c7a957e8d059b632b03d6204aca37af557dee947f30a9" "checksum jpeg-decoder 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0dfe27a6c0dabd772d0f9b9f8701c4ca12c4d1eebcadf2be1f6f70396f6a1434" @@ -3927,7 +3895,6 @@ dependencies = [ "checksum proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "77997c53ae6edd6d187fec07ec41b207063b5ee6f33680e9fa86d405cdd313d4" "checksum procedural-masquerade 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c93cdc1fb30af9ddf3debc4afbdb0f35126cbd99daa229dd76cdd5349b41d989" "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4" -"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0ff51282f28dc1b53fd154298feaa2e77c5ea0dba68e1fd8b03b72fbe13d2a" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" @@ -3967,7 +3934,7 @@ dependencies = [ "checksum signpost 0.1.0 (git+https://github.com/pcwalton/signpost.git)" = "<none>" "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" -"checksum smallbitvec 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "665fbc8384f961eb55c548daa2a4b1efff1f9d03b7a10f162ac6ad6a781ca966" +"checksum smallbitvec 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c63726029f0069f88467873e47f392575f28f9f16b72ac65465263db4b3a13c" "checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9" "checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" "checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423" @@ -3975,9 +3942,7 @@ dependencies = [ "checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" "checksum swapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e454d048db5527d000bfddb77bd072bbf3a1e2ae785f16d9bd116e07c2ab45eb" -"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59" -"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synstructure 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "98cad891cd238c98e1f0aec9f7c0f620aa696e4e5f7daba56ac67b5e86a6b049" "checksum tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11ce2fe9db64b842314052e2421ac61a73ce41b898dc8e3750398b219c5fc1e0" "checksum tendril 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9de21546595a0873061940d994bbbc5c35f024ae4fd61ec5c5b159115684f508" @@ -4002,7 +3967,6 @@ dependencies = [ "checksum unicode-script 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e8bd7bbf020b2885113e6304f68bcc33881c5552657c58d4e9699cd1b6606e81" "checksum unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946" "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" -"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa35e768d4daf1d85733418a49fb42e10d7f633e394fccab4ab7aba897053fe2" diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs index 43eac985c5e..e3a4a20d380 100644 --- a/components/script/dom/cssstyledeclaration.rs +++ b/components/script/dom/cssstyledeclaration.rs @@ -253,7 +253,7 @@ impl CSSStyleDeclaration { self.owner.mutate_associated_block(|pdb, changed| { if value.is_empty() { // Step 3 - *changed = pdb.remove_property(&id); + *changed = pdb.remove_property(&id, |_| {}); return Ok(()); } @@ -365,7 +365,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { let mut string = String::new(); self.owner.mutate_associated_block(|pdb, changed| { pdb.property_value_to_css(&id, &mut string).unwrap(); - *changed = pdb.remove_property(&id); + *changed = pdb.remove_property(&id, |_| {}); }); // Step 6 diff --git a/components/selectors/builder.rs b/components/selectors/builder.rs index f100166f48d..0be3eb90e0c 100644 --- a/components/selectors/builder.rs +++ b/components/selectors/builder.rs @@ -36,6 +36,7 @@ use std::slice; /// (from left to right). Once the process is complete, callers should invoke /// build(), which transforms the contents of the SelectorBuilder into a heap- /// allocated Selector and leaves the builder in a drained state. +#[derive(Debug)] pub struct SelectorBuilder<Impl: SelectorImpl> { /// The entire sequence of simple selectors, from left to right, without combinators. /// @@ -104,7 +105,7 @@ impl<Impl: SelectorImpl> SelectorBuilder<Impl> { parsed_slotted: bool, ) -> ThinArc<SpecificityAndFlags, Component<Impl>> { // Compute the specificity and flags. - let mut spec = SpecificityAndFlags(specificity(self.simple_selectors.iter())); + let mut spec = SpecificityAndFlags(specificity(&*self, self.simple_selectors.iter())); if parsed_pseudo { spec.0 |= HAS_PSEUDO_BIT; } @@ -268,25 +269,35 @@ impl From<Specificity> for u32 { } } -fn specificity<Impl>(iter: slice::Iter<Component<Impl>>) -> u32 +fn specificity<Impl>(builder: &SelectorBuilder<Impl>, iter: slice::Iter<Component<Impl>>) -> u32 where Impl: SelectorImpl, { - complex_selector_specificity(iter).into() + complex_selector_specificity(builder, iter).into() } -fn complex_selector_specificity<Impl>(mut iter: slice::Iter<Component<Impl>>) -> Specificity +fn complex_selector_specificity<Impl>( + builder: &SelectorBuilder<Impl>, + mut iter: slice::Iter<Component<Impl>>, +) -> Specificity where Impl: SelectorImpl, { fn simple_selector_specificity<Impl>( + builder: &SelectorBuilder<Impl>, simple_selector: &Component<Impl>, specificity: &mut Specificity, ) where Impl: SelectorImpl, { match *simple_selector { - Component::Combinator(..) => unreachable!(), + Component::Combinator(ref combinator) => { + unreachable!( + "Found combinator {:?} in simple selectors vector? {:?}", + combinator, + builder, + ); + } // FIXME(emilio): Spec doesn't define any particular specificity for // ::slotted(), so apply the general rule for pseudos per: // @@ -326,7 +337,7 @@ where }, Component::Negation(ref negated) => { for ss in negated.iter() { - simple_selector_specificity(&ss, specificity); + simple_selector_specificity(builder, &ss, specificity); } }, } @@ -334,7 +345,7 @@ where let mut specificity = Default::default(); for simple_selector in &mut iter { - simple_selector_specificity(&simple_selector, &mut specificity); + simple_selector_specificity(builder, &simple_selector, &mut specificity); } specificity } diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index fc840bfe926..3210f1085b0 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -92,7 +92,7 @@ macro_rules! with_all_bounds { /// NB: We need Clone so that we can derive(Clone) on struct with that /// are parameterized on SelectorImpl. See /// <https://github.com/rust-lang/rust/issues/26925> - pub trait SelectorImpl: Clone + Sized + 'static { + pub trait SelectorImpl: Clone + Debug + Sized + 'static { type ExtraMatchingData: Sized + Default + 'static; type AttrValue: $($InSelector)*; type Identifier: $($InSelector)*; @@ -544,10 +544,26 @@ impl<Impl: SelectorImpl> Selector<Impl> { } /// Whether this selector is a featureless :host selector, with no - /// combinators to the left. + /// combinators to the left, and optionally has a pseudo-element to the + /// right. #[inline] - pub fn is_featureless_host_selector(&self) -> bool { - self.iter().is_featureless_host_selector() + pub fn is_featureless_host_selector_or_pseudo_element(&self) -> bool { + let mut iter = self.iter(); + if !self.has_pseudo_element() { + return iter.is_featureless_host_selector(); + } + + // Skip the pseudo-element. + for _ in &mut iter { } + + match iter.next_sequence() { + None => return false, + Some(combinator) => { + debug_assert_eq!(combinator, Combinator::PseudoElement); + } + } + + iter.is_featureless_host_selector() } /// Returns an iterator over this selector in matching order (right-to-left), diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index 56e427c983a..4fb50d8ad85 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -59,7 +59,7 @@ serde = {version = "1.0", optional = true, features = ["derive"]} servo_arc = { path = "../servo_arc" } servo_atoms = {path = "../atoms", optional = true} servo_config = {path = "../config", optional = true} -smallbitvec = "2.1.0" +smallbitvec = "2.1.1" smallvec = "0.6" string_cache = { version = "0.7", optional = true } style_derive = {path = "../style_derive"} diff --git a/components/style/animation.rs b/components/style/animation.rs index bbf847d7f14..d090ecb1ecb 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -10,7 +10,7 @@ use context::SharedStyleContext; use dom::{OpaqueNode, TElement}; use font_metrics::FontMetricsProvider; use properties::{self, CascadeFlags, ComputedValues, LonghandId}; -use properties::animated_properties::{AnimatedProperty, TransitionProperty}; +use properties::animated_properties::AnimatedProperty; use properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection; use properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState; use rule_tree::CascadeLevel; @@ -20,6 +20,7 @@ use std::sync::mpsc::Sender; use stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue}; use timer::Timer; use values::computed::Time; +use values::computed::box_::TransitionProperty; use values::computed::transform::TimingFunction; use values::generics::box_::AnimationIterationCount; use values::generics::transform::{StepPosition, TimingFunction as GenericTimingFunction}; @@ -303,7 +304,8 @@ impl PropertyAnimation { let duration = box_style.transition_duration_mod(transition_index); match transition_property { - TransitionProperty::Unsupported(_) => result, + TransitionProperty::Custom(..) | + TransitionProperty::Unsupported(..) => result, TransitionProperty::Shorthand(ref shorthand_id) => shorthand_id .longhands() .filter_map(|longhand| { diff --git a/components/style/font_face.rs b/components/style/font_face.rs index 5a111925ae7..57c0f3b0b77 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -323,7 +323,7 @@ macro_rules! is_descriptor_enabled { ("font-variation-settings") => { unsafe { use gecko_bindings::structs::mozilla; - mozilla::StaticPrefs_sVarCache_layout_css_font_variations_enabled + mozilla::StaticPrefs_sVarCache_layout_css_font_variations_enabled != 0 } }; ($name:tt) => { diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 9ff92a19e16..5475be1e3fc 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -67,7 +67,6 @@ use media_queries::Device; use properties::{ComputedValues, LonghandId}; use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock}; use properties::animated_properties::{AnimationValue, AnimationValueMap}; -use properties::animated_properties::TransitionProperty; use properties::style_structs::Font; use rule_tree::CascadeLevel as ServoCascadeLevel; use selector_parser::{AttrValue, Direction, PseudoClassStringArg}; @@ -181,7 +180,6 @@ impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> { let author_styles = AuthorStyles::<GeckoStyleSheet>::from_ffi(author_styles); - debug_assert!(!author_styles.stylesheets.dirty()); debug_assert!( author_styles.quirks_mode == self.as_node().owner_doc().quirks_mode() || author_styles.stylesheets.is_empty() @@ -617,31 +615,37 @@ impl<'le> GeckoElement<'le> { // GeckoNode is a raw reference. // // We can use a Cell<T>, but that's a bit of a pain. + #[inline] fn set_flags(&self, flags: u32) { unsafe { Gecko_SetNodeFlags(self.as_node().0, flags) } } + #[inline] unsafe fn unset_flags(&self, flags: u32) { Gecko_UnsetNodeFlags(self.as_node().0, flags) } /// Returns true if this element has descendants for lazy frame construction. + #[inline] pub fn descendants_need_frames(&self) -> bool { self.flags() & (NODE_DESCENDANTS_NEED_FRAMES as u32) != 0 } /// Returns true if this element needs lazy frame construction. + #[inline] pub fn needs_frame(&self) -> bool { self.flags() & (NODE_NEEDS_FRAME as u32) != 0 } /// Returns a reference to the DOM slots for this Element, if they exist. + #[inline] fn dom_slots(&self) -> Option<&structs::FragmentOrElement_nsDOMSlots> { let slots = self.as_node().0.mSlots as *const structs::FragmentOrElement_nsDOMSlots; unsafe { slots.as_ref() } } /// Returns a reference to the extended DOM slots for this Element. + #[inline] fn extended_slots(&self) -> Option<&structs::FragmentOrElement_nsExtendedDOMSlots> { self.dom_slots().and_then(|s| unsafe { (s._base.mExtendedSlots.mPtr as *const structs::FragmentOrElement_nsExtendedDOMSlots) @@ -699,6 +703,7 @@ impl<'le> GeckoElement<'le> { } } + #[inline] fn non_xul_xbl_binding_parent_raw_content(&self) -> *mut nsIContent { debug_assert!(!self.is_xul_element()); self.extended_slots() @@ -1113,7 +1118,23 @@ impl<'le> TElement for GeckoElement<'le> { assert_eq!(base as *const _, self.0 as *const _, "Bad cast"); } - let assigned_nodes: &[structs::RefPtr<structs::nsINode>] = &*slot.mAssignedNodes; + // FIXME(emilio): Workaround a bindgen bug on Android that causes + // mAssignedNodes to be at the wrong offset. See bug 1466406. + // + // Bug 1466580 tracks running the Android layout tests on automation. + // + // The actual bindgen bug still needs reduction. + let assigned_nodes: &[structs::RefPtr<structs::nsINode>] = + if !cfg!(target_os = "android") { + debug_assert_eq!( + unsafe { bindings::Gecko_GetAssignedNodes(self.0) }, + &slot.mAssignedNodes as *const _, + ); + + &*slot.mAssignedNodes + } else { + unsafe { &**bindings::Gecko_GetAssignedNodes(self.0) } + }; debug_assert_eq!( mem::size_of::<structs::RefPtr<structs::nsINode>>(), @@ -1351,6 +1372,7 @@ impl<'le> TElement for GeckoElement<'le> { !self.is_in_native_anonymous_subtree() } + #[inline] fn implemented_pseudo_element(&self) -> Option<PseudoElement> { if !self.is_in_native_anonymous_subtree() { return None; @@ -1364,6 +1386,7 @@ impl<'le> TElement for GeckoElement<'le> { PseudoElement::from_pseudo_type(pseudo_type) } + #[inline] fn store_children_to_process(&self, _: isize) { // This is only used for bottom-up traversal, and is thus a no-op for Gecko. } @@ -1587,6 +1610,7 @@ impl<'le> TElement for GeckoElement<'le> { ) -> bool { use gecko_bindings::structs::nsCSSPropertyID; use properties::LonghandIdSet; + use values::computed::TransitionProperty; debug_assert!( self.might_need_transitions_update(Some(before_change_style), after_change_style), @@ -1630,6 +1654,7 @@ impl<'le> TElement for GeckoElement<'le> { }; match transition_property { + TransitionProperty::Custom(..) | TransitionProperty::Unsupported(..) => {}, TransitionProperty::Shorthand(ref shorthand) => { if shorthand.longhands().any(property_check_helper) { diff --git a/components/style/gecko_bindings/sugar/ns_compatibility.rs b/components/style/gecko_bindings/sugar/ns_compatibility.rs index 3fe63391d36..eaf97ca6f31 100644 --- a/components/style/gecko_bindings/sugar/ns_compatibility.rs +++ b/components/style/gecko_bindings/sugar/ns_compatibility.rs @@ -8,6 +8,7 @@ use context::QuirksMode; use gecko_bindings::structs::nsCompatibility; impl From<nsCompatibility> for QuirksMode { + #[inline] fn from(mode: nsCompatibility) -> QuirksMode { match mode { nsCompatibility::eCompatibility_FullStandards => QuirksMode::NoQuirks, diff --git a/components/style/gecko_bindings/sugar/refptr.rs b/components/style/gecko_bindings/sugar/refptr.rs index 76a7021ef5b..10c9e627699 100644 --- a/components/style/gecko_bindings/sugar/refptr.rs +++ b/components/style/gecko_bindings/sugar/refptr.rs @@ -4,7 +4,8 @@ //! A rust helper to ease the use of Gecko's refcounted types. -use gecko_bindings::structs; +use Atom; +use gecko_bindings::{structs, bindings}; use gecko_bindings::sugar::ownership::HasArcFFI; use servo_arc::Arc; use std::{fmt, mem, ptr}; @@ -255,13 +256,16 @@ unsafe impl<T: ThreadSafeRefCounted> Send for RefPtr<T> {} unsafe impl<T: ThreadSafeRefCounted> Sync for RefPtr<T> {} macro_rules! impl_refcount { - ($t:ty, $addref:ident, $release:ident) => { + ($t:ty, $addref:path, $release:path) => { unsafe impl RefCounted for $t { + #[inline] fn addref(&self) { - unsafe { ::gecko_bindings::bindings::$addref(self as *const _ as *mut _) } + unsafe { $addref(self as *const _ as *mut _) } } + + #[inline] unsafe fn release(&self) { - ::gecko_bindings::bindings::$release(self as *const _ as *mut _) + $release(self as *const _ as *mut _) } } }; @@ -271,50 +275,63 @@ macro_rules! impl_refcount { // // Gets you a free RefCounted impl implemented via FFI. macro_rules! impl_threadsafe_refcount { - ($t:ty, $addref:ident, $release:ident) => { + ($t:ty, $addref:path, $release:path) => { impl_refcount!($t, $addref, $release); unsafe impl ThreadSafeRefCounted for $t {} }; } impl_threadsafe_refcount!( - ::gecko_bindings::structs::RawGeckoURLExtraData, - Gecko_AddRefURLExtraDataArbitraryThread, - Gecko_ReleaseURLExtraDataArbitraryThread + structs::RawGeckoURLExtraData, + bindings::Gecko_AddRefURLExtraDataArbitraryThread, + bindings::Gecko_ReleaseURLExtraDataArbitraryThread +); +impl_threadsafe_refcount!( + structs::nsStyleQuoteValues, + bindings::Gecko_AddRefQuoteValuesArbitraryThread, + bindings::Gecko_ReleaseQuoteValuesArbitraryThread ); impl_threadsafe_refcount!( - ::gecko_bindings::structs::nsStyleQuoteValues, - Gecko_AddRefQuoteValuesArbitraryThread, - Gecko_ReleaseQuoteValuesArbitraryThread + structs::nsCSSValueSharedList, + bindings::Gecko_AddRefCSSValueSharedListArbitraryThread, + bindings::Gecko_ReleaseCSSValueSharedListArbitraryThread ); impl_threadsafe_refcount!( - ::gecko_bindings::structs::nsCSSValueSharedList, - Gecko_AddRefCSSValueSharedListArbitraryThread, - Gecko_ReleaseCSSValueSharedListArbitraryThread + structs::mozilla::css::URLValue, + bindings::Gecko_AddRefCSSURLValueArbitraryThread, + bindings::Gecko_ReleaseCSSURLValueArbitraryThread ); impl_threadsafe_refcount!( - ::gecko_bindings::structs::mozilla::css::URLValue, - Gecko_AddRefCSSURLValueArbitraryThread, - Gecko_ReleaseCSSURLValueArbitraryThread + structs::mozilla::css::GridTemplateAreasValue, + bindings::Gecko_AddRefGridTemplateAreasValueArbitraryThread, + bindings::Gecko_ReleaseGridTemplateAreasValueArbitraryThread ); impl_threadsafe_refcount!( - ::gecko_bindings::structs::mozilla::css::GridTemplateAreasValue, - Gecko_AddRefGridTemplateAreasValueArbitraryThread, - Gecko_ReleaseGridTemplateAreasValueArbitraryThread + structs::ImageValue, + bindings::Gecko_AddRefImageValueArbitraryThread, + bindings::Gecko_ReleaseImageValueArbitraryThread ); impl_threadsafe_refcount!( - ::gecko_bindings::structs::ImageValue, - Gecko_AddRefImageValueArbitraryThread, - Gecko_ReleaseImageValueArbitraryThread + structs::SharedFontList, + bindings::Gecko_AddRefSharedFontListArbitraryThread, + bindings::Gecko_ReleaseSharedFontListArbitraryThread ); impl_threadsafe_refcount!( - ::gecko_bindings::structs::SharedFontList, - Gecko_AddRefSharedFontListArbitraryThread, - Gecko_ReleaseSharedFontListArbitraryThread + structs::SheetLoadDataHolder, + bindings::Gecko_AddRefSheetLoadDataHolderArbitraryThread, + bindings::Gecko_ReleaseSheetLoadDataHolderArbitraryThread ); +#[inline] +unsafe fn addref_atom(atom: *mut structs::nsAtom) { + mem::forget(Atom::from_raw(atom)); +} +#[inline] +unsafe fn release_atom(atom: *mut structs::nsAtom) { + let _ = Atom::from_addrefed(atom); +} impl_threadsafe_refcount!( - ::gecko_bindings::structs::SheetLoadDataHolder, - Gecko_AddRefSheetLoadDataHolderArbitraryThread, - Gecko_ReleaseSheetLoadDataHolderArbitraryThread + structs::nsAtom, + addref_atom, + release_atom ); diff --git a/components/style/gecko_bindings/sugar/style_complex_color.rs b/components/style/gecko_bindings/sugar/style_complex_color.rs index 696eb6968b6..90e93b72fd0 100644 --- a/components/style/gecko_bindings/sugar/style_complex_color.rs +++ b/components/style/gecko_bindings/sugar/style_complex_color.rs @@ -5,28 +5,21 @@ //! Rust helpers to interact with Gecko's StyleComplexColor. use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor}; -use gecko_bindings::structs::{nscolor, StyleComplexColor}; +use gecko_bindings::structs::StyleComplexColor; +use gecko_bindings::structs::StyleComplexColor_Tag as Tag; use values::{Auto, Either}; -use values::computed::Color as ComputedColor; +use values::computed::{Color as ComputedColor, RGBAColor as ComputedRGBA}; use values::computed::ui::ColorOrAuto; - -impl From<nscolor> for StyleComplexColor { - fn from(other: nscolor) -> Self { - StyleComplexColor { - mColor: other, - mForegroundRatio: 0, - mIsAuto: false, - } - } -} +use values::generics::color::{Color as GenericColor, ComplexColorRatios}; impl StyleComplexColor { /// Create a `StyleComplexColor` value that represents `currentColor`. pub fn current_color() -> Self { StyleComplexColor { mColor: 0, - mForegroundRatio: 255, - mIsAuto: false, + mBgRatio: 0., + mFgRatio: 1., + mTag: Tag::eForeground, } } @@ -34,28 +27,66 @@ impl StyleComplexColor { pub fn auto() -> Self { StyleComplexColor { mColor: 0, - mForegroundRatio: 255, - mIsAuto: true, + mBgRatio: 0., + mFgRatio: 1., + mTag: Tag::eAuto, + } + } +} + +impl From<ComputedRGBA> for StyleComplexColor { + fn from(other: ComputedRGBA) -> Self { + StyleComplexColor { + mColor: convert_rgba_to_nscolor(&other), + mBgRatio: 1., + mFgRatio: 0., + mTag: Tag::eNumeric, } } } impl From<ComputedColor> for StyleComplexColor { fn from(other: ComputedColor) -> Self { - StyleComplexColor { - mColor: convert_rgba_to_nscolor(&other.color).into(), - mForegroundRatio: other.foreground_ratio, - mIsAuto: false, + match other { + GenericColor::Numeric(color) => color.into(), + GenericColor::Foreground => Self::current_color(), + GenericColor::Complex(color, ratios) => { + debug_assert!(ratios != ComplexColorRatios::NUMERIC); + debug_assert!(ratios != ComplexColorRatios::FOREGROUND); + StyleComplexColor { + mColor: convert_rgba_to_nscolor(&color).into(), + mBgRatio: ratios.bg, + mFgRatio: ratios.fg, + mTag: Tag::eComplex, + } + } } } } impl From<StyleComplexColor> for ComputedColor { fn from(other: StyleComplexColor) -> Self { - debug_assert!(!other.mIsAuto); - ComputedColor { - color: convert_nscolor_to_rgba(other.mColor), - foreground_ratio: other.mForegroundRatio, + match other.mTag { + Tag::eNumeric => { + debug_assert!(other.mBgRatio == 1. && other.mFgRatio == 0.); + GenericColor::Numeric(convert_nscolor_to_rgba(other.mColor)) + } + Tag::eForeground => { + debug_assert!(other.mBgRatio == 0. && other.mFgRatio == 1.); + GenericColor::Foreground + } + Tag::eComplex => { + debug_assert!(other.mBgRatio != 1. || other.mFgRatio != 0.); + debug_assert!(other.mBgRatio != 0. || other.mFgRatio != 1.); + GenericColor::Complex( + convert_nscolor_to_rgba(other.mColor), + ComplexColorRatios { + bg: other.mBgRatio, + fg: other.mFgRatio, + }, + ) + } + Tag::eAuto => unreachable!("Unsupport StyleComplexColor with tag eAuto"), } } } @@ -71,7 +102,7 @@ impl From<ColorOrAuto> for StyleComplexColor { impl From<StyleComplexColor> for ColorOrAuto { fn from(other: StyleComplexColor) -> Self { - if !other.mIsAuto { + if other.mTag != Tag::eAuto { Either::First(other.into()) } else { Either::Second(Auto) diff --git a/components/style/parser.rs b/components/style/parser.rs index 26f4aeea071..6dbfe1cfa01 100644 --- a/components/style/parser.rs +++ b/components/style/parser.rs @@ -150,10 +150,19 @@ impl<'a> ParserContext<'a> { } } -// XXXManishearth Replace all specified value parse impls with impls of this -// trait. This will make it easy to write more generic values in the future. /// A trait to abstract parsing of a specified value given a `ParserContext` and /// CSS input. +/// +/// This can be derived on keywords with `#[derive(Parse)]`. +/// +/// The derive code understands the following attributes on each of the variants: +/// +/// * `#[parse(aliases = "foo,bar")]` can be used to alias a value with another +/// at parse-time. +/// +/// * `#[parse(condition = "function")]` can be used to make the parsing of the +/// value conditional on `function`, which needs to fulfill +/// `fn(&ParserContext) -> bool`. pub trait Parse: Sized { /// Parse a value of this type. /// diff --git a/components/style/properties/data.py b/components/style/properties/data.py index 0ac42b365ee..fca4b536e01 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -226,6 +226,10 @@ class Longhand(object): # See compute_damage for the various values this can take self.servo_restyle_damage = servo_restyle_damage + @staticmethod + def type(): + return "longhand" + def experimental(self, product): if product == "gecko": return bool(self.gecko_pref) @@ -361,6 +365,10 @@ class Shorthand(object): animatable = property(get_animatable) transitionable = property(get_transitionable) + @staticmethod + def type(): + return "shorthand" + def experimental(self, product): if product == "gecko": return bool(self.gecko_pref) @@ -392,6 +400,10 @@ class Alias(object): self.allowed_in_page_rule = original.allowed_in_page_rule self.allowed_in_keyframe_block = original.allowed_in_keyframe_block + @staticmethod + def type(): + return "alias" + def experimental(self, product): if product == "gecko": return bool(self.gecko_pref) diff --git a/components/style/properties/declaration_block.rs b/components/style/properties/declaration_block.rs index a66058caf1e..4e368c0c2b7 100644 --- a/components/style/properties/declaration_block.rs +++ b/components/style/properties/declaration_block.rs @@ -492,9 +492,9 @@ impl PropertyDeclarationBlock { /// declaration with more importance, and will ensure that, if inserted, /// it's inserted at the end of the declaration block. /// - /// * For `DeclarationSource::CssOm`, this will override importance and - /// will preserve the original position on the block. + /// * For `DeclarationSource::CssOm`, this will override importance. /// + /// Returns whether the declaration has changed. pub fn push( &mut self, declaration: PropertyDeclaration, @@ -517,15 +517,15 @@ impl PropertyDeclarationBlock { continue; } - let important = self.declarations_importance[i]; - // For declarations from parsing, non-important declarations - // shouldn't override existing important one. - if important && !importance.important() && - matches!(source, DeclarationSource::Parsing) { - return true; - } - if matches!(source, DeclarationSource::Parsing) { + let important = self.declarations_importance[i]; + + // For declarations from parsing, non-important declarations + // shouldn't override existing important one. + if important && !importance.important() { + return false; + } + // As a compatibility hack, specially on Android, // don't allow to override a prefixed webkit display // value with an unprefixed version from parsing @@ -564,27 +564,17 @@ impl PropertyDeclarationBlock { true } - /// Set the declaration importance for a given property, if found. - /// - /// Returns whether any declaration was updated. - pub fn set_importance(&mut self, property: &PropertyId, new_importance: Importance) -> bool { - let mut updated_at_least_one = false; - for (i, declaration) in self.declarations.iter().enumerate() { - if declaration.id().is_or_is_longhand_of(property) { - let is_important = new_importance.important(); - if self.declarations_importance[i] != is_important { - self.declarations_importance.set(i, is_important); - updated_at_least_one = true; - } - } - } - updated_at_least_one - } - /// <https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-removeproperty> /// /// Returns whether any declaration was actually removed. - pub fn remove_property(&mut self, property: &PropertyId) -> bool { + pub fn remove_property<C>( + &mut self, + property: &PropertyId, + mut before_change_callback: C, + ) -> bool + where + C: FnMut(&Self), + { let longhand_id = property.longhand_id(); if let Some(id) = longhand_id { if !self.longhands.contains(id) { @@ -592,23 +582,28 @@ impl PropertyDeclarationBlock { } } let mut removed_at_least_one = false; - let longhands = &mut self.longhands; - let declarations_importance = &mut self.declarations_importance; let mut i = 0; - self.declarations.retain(|declaration| { - let id = declaration.id(); - let remove = id.is_or_is_longhand_of(property); - if remove { + let mut len = self.len(); + while i < len { + { + let id = self.declarations[i].id(); + if !id.is_or_is_longhand_of(property) { + i += 1; + continue; + } + + if !removed_at_least_one { + before_change_callback(&*self); + } removed_at_least_one = true; if let PropertyDeclarationId::Longhand(id) = id { - longhands.remove(id); + self.longhands.remove(id); } - declarations_importance.remove(i); - } else { - i += 1; + self.declarations_importance.remove(i); } - !remove - }); + self.declarations.remove(i); + len -= 1; + } if longhand_id.is_some() { debug_assert!(removed_at_least_one); @@ -1116,9 +1111,7 @@ where let mut parser = Parser::new(&mut input); let start_position = parser.position(); parser.parse_entirely(|parser| { - let name = id.name().into(); - PropertyDeclaration::parse_into(declarations, id, name, &context, parser) - .map_err(|e| e.into()) + PropertyDeclaration::parse_into(declarations, id, &context, parser) }).map_err(|err| { let location = err.location; let error = ContextualParseError::UnsupportedPropertyDeclaration( @@ -1169,7 +1162,7 @@ impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b> { } }; input.parse_until_before(Delimiter::Bang, |input| { - PropertyDeclaration::parse_into(self.declarations, id, name, self.context, input) + PropertyDeclaration::parse_into(self.declarations, id, self.context, input) })?; let importance = match input.try(parse_important) { Ok(()) => Importance::Important, diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 4b902358866..f83e0836c91 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -28,7 +28,6 @@ use gecko_bindings::bindings::Gecko_CopyListStyleImageFrom; use gecko_bindings::bindings::Gecko_EnsureImageLayersLength; use gecko_bindings::bindings::Gecko_SetCursorArrayLength; use gecko_bindings::bindings::Gecko_SetCursorImageValue; -use gecko_bindings::bindings::Gecko_StyleTransition_SetUnsupportedProperty; use gecko_bindings::bindings::Gecko_NewCSSShadowArray; use gecko_bindings::bindings::Gecko_nsStyleFont_SetLang; use gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom; @@ -49,7 +48,6 @@ use gecko::values::GeckoStyleCoordConvertible; use gecko::values::round_border_to_device_pixels; use logical_geometry::WritingMode; use media_queries::Device; -use properties::animated_properties::TransitionProperty; use properties::computed_value_flags::*; use properties::{longhands, Importance, LonghandId}; use properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyDeclarationId}; @@ -60,7 +58,7 @@ use std::marker::PhantomData; use std::mem::{forget, uninitialized, transmute, zeroed}; use std::{cmp, ops, ptr}; use values::{self, CustomIdent, Either, KeyframesName, None_}; -use values::computed::{NonNegativeLength, ToComputedValue, Percentage}; +use values::computed::{NonNegativeLength, ToComputedValue, Percentage, TransitionProperty}; use values::computed::font::FontSize; use values::computed::effects::{BoxShadow, Filter, SimpleShadow}; use values::computed::outline::OutlineStyle; @@ -390,17 +388,9 @@ impl ${style_struct.gecko_struct_name} { <%! def get_gecko_property(ffi_name, self_param = "self"): - if "mBorderColor" in ffi_name: - return ffi_name.replace("mBorderColor", - "unsafe { *%s.gecko.__bindgen_anon_1.mBorderColor.as_ref() }" - % self_param) return "%s.gecko.%s" % (self_param, ffi_name) def set_gecko_property(ffi_name, expr): - if "mBorderColor" in ffi_name: - ffi_name = ffi_name.replace("mBorderColor", - "*self.gecko.__bindgen_anon_1.mBorderColor.as_mut()") - return "unsafe { %s = %s };" % (ffi_name, expr) return "self.gecko.%s = %s;" % (ffi_name, expr) %> @@ -1596,7 +1586,7 @@ fn static_assert() { self.gecko.mComputedBorder.${side.ident} = self.gecko.mBorder.${side.ident}; } - <% impl_color("border_%s_color" % side.ident, "(mBorderColor)[%s]" % side.index) %> + <% impl_color("border_%s_color" % side.ident, "mBorder%sColor" % side.name) %> <% impl_non_negative_length("border_%s_width" % side.ident, "mComputedBorder.%s" % side.ident, @@ -3267,6 +3257,8 @@ fn static_assert() { I::IntoIter: ExactSizeIterator { use gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_no_properties; + use gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable; + use gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN; let v = v.into_iter(); @@ -3274,10 +3266,17 @@ fn static_assert() { self.gecko.mTransitions.ensure_len(v.len()); self.gecko.mTransitionPropertyCount = v.len() as u32; for (servo, gecko) in v.zip(self.gecko.mTransitions.iter_mut()) { + unsafe { gecko.mUnknownProperty.clear() }; + match servo { - TransitionProperty::Unsupported(ref ident) => unsafe { - Gecko_StyleTransition_SetUnsupportedProperty(gecko, ident.0.as_ptr()) + TransitionProperty::Unsupported(ident) => { + gecko.mProperty = eCSSProperty_UNKNOWN; + gecko.mUnknownProperty.mRawPtr = ident.0.into_addrefed(); }, + TransitionProperty::Custom(name) => { + gecko.mProperty = eCSSPropertyExtra_variable; + gecko.mUnknownProperty.mRawPtr = name.into_addrefed(); + } _ => gecko.mProperty = servo.to_nscsspropertyid().unwrap(), } } @@ -3307,15 +3306,24 @@ fn static_assert() { use gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN; let property = self.gecko.mTransitions[index].mProperty; - if property == eCSSProperty_UNKNOWN || property == eCSSPropertyExtra_variable { + if property == eCSSProperty_UNKNOWN { let atom = self.gecko.mTransitions[index].mUnknownProperty.mRawPtr; debug_assert!(!atom.is_null()); TransitionProperty::Unsupported(CustomIdent(unsafe{ Atom::from_raw(atom) })) + } else if property == eCSSPropertyExtra_variable { + let atom = self.gecko.mTransitions[index].mUnknownProperty.mRawPtr; + debug_assert!(!atom.is_null()); + TransitionProperty::Custom(unsafe{ + Atom::from_raw(atom) + }) } else if property == eCSSPropertyExtra_no_properties { - // Actually, we don't expect TransitionProperty::Unsupported also represents "none", - // but if the caller wants to convert it, it is fine. Please use it carefully. + // Actually, we don't expect TransitionProperty::Unsupported also + // represents "none", but if the caller wants to convert it, it is + // fine. Please use it carefully. + // + // FIXME(emilio): This is a hack, is this reachable? TransitionProperty::Unsupported(CustomIdent(atom!("none"))) } else { property.into() @@ -3336,11 +3344,12 @@ fn static_assert() { for (index, transition) in self.gecko.mTransitions.iter_mut().enumerate().take(count as usize) { transition.mProperty = other.gecko.mTransitions[index].mProperty; + unsafe { transition.mUnknownProperty.clear() }; if transition.mProperty == eCSSProperty_UNKNOWN || transition.mProperty == eCSSPropertyExtra_variable { let atom = other.gecko.mTransitions[index].mUnknownProperty.mRawPtr; debug_assert!(!atom.is_null()); - unsafe { Gecko_StyleTransition_SetUnsupportedProperty(transition, atom) }; + transition.mUnknownProperty.mRawPtr = unsafe { Atom::from_raw(atom) }.into_addrefed(); } } } diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index 209c06df095..11a1283f1cc 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -594,7 +594,7 @@ aliases.append(alias) %> % if aliases: - #[css(aliases = "${','.join(aliases)}")] + #[parse(aliases = "${','.join(aliases)}")] % endif % endif ${to_camel_case(variant)}, @@ -809,8 +809,10 @@ use values::generics::rect::Rect; use values::specified; - pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) - -> Result<Longhands, ParseError<'i>> { + pub fn parse_value<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result<Longhands, ParseError<'i>> { let rect = Rect::parse_with(context, input, |_c, i| { % if allow_quirks: ${parser_function}_quirky(_c, i, specified::AllowQuirks::Yes) diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index f45b7ffe91e..d935726eb48 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -9,7 +9,6 @@ from itertools import groupby %> -use cssparser::Parser; #[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap; #[cfg(feature = "gecko")] use gecko_bindings::structs::RawGeckoGfxMatrix4x4; #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID; @@ -27,12 +26,12 @@ use smallvec::SmallVec; use std::{cmp, ptr}; use std::mem::{self, ManuallyDrop}; #[cfg(feature = "gecko")] use hash::FnvHashMap; -use style_traits::{KeywordsCollectFn, ParseError, SpecifiedValueInfo}; use super::ComputedValues; -use values::{CSSFloat, CustomIdent}; +use values::CSSFloat; use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero}; use values::animated::color::RGBA as AnimatedRGBA; use values::animated::effects::Filter as AnimatedFilter; +#[cfg(feature = "gecko")] use values::computed::TransitionProperty; use values::computed::{Angle, CalcLengthOrPercentage}; use values::computed::{ClipRect, Context}; use values::computed::{Length, LengthOrPercentage, LengthOrPercentageOrAuto}; @@ -70,78 +69,6 @@ pub fn nscsspropertyid_is_animatable(property: nsCSSPropertyID) -> bool { } } -/// A given transition property, that is either `All`, a transitionable longhand property, -/// a shorthand with at least one transitionable longhand component, or an unsupported property. -// NB: This needs to be here because it needs all the longhands generated -// beforehand. -#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToCss)] -pub enum TransitionProperty { - /// A shorthand. - Shorthand(ShorthandId), - /// A longhand transitionable property. - Longhand(LonghandId), - /// Unrecognized property which could be any non-transitionable, custom property, or - /// unknown property. - Unsupported(CustomIdent), -} - -impl TransitionProperty { - /// Returns `all`. - #[inline] - pub fn all() -> Self { - TransitionProperty::Shorthand(ShorthandId::All) - } - - /// Parse a transition-property value. - pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> { - // FIXME(https://github.com/rust-lang/rust/issues/33156): remove this - // enum and use PropertyId when stable Rust allows destructors in - // statics. - // - // FIXME: This should handle aliases too. - pub enum StaticId { - Longhand(LonghandId), - Shorthand(ShorthandId), - } - ascii_case_insensitive_phf_map! { - static_id -> StaticId = { - % for prop in data.shorthands: - "${prop.name}" => StaticId::Shorthand(ShorthandId::${prop.camel_case}), - % endfor - % for prop in data.longhands: - "${prop.name}" => StaticId::Longhand(LonghandId::${prop.camel_case}), - % endfor - } - } - - let location = input.current_source_location(); - let ident = input.expect_ident()?; - - Ok(match static_id(&ident) { - Some(&StaticId::Longhand(id)) => TransitionProperty::Longhand(id), - Some(&StaticId::Shorthand(id)) => TransitionProperty::Shorthand(id), - None => { - TransitionProperty::Unsupported( - CustomIdent::from_ident(location, ident, &["none"])?, - ) - }, - }) - } - - /// Convert TransitionProperty to nsCSSPropertyID. - #[cfg(feature = "gecko")] - pub fn to_nscsspropertyid(&self) -> Result<nsCSSPropertyID, ()> { - Ok(match *self { - TransitionProperty::Shorthand(ShorthandId::All) => { - nsCSSPropertyID::eCSSPropertyExtra_all_properties - } - TransitionProperty::Shorthand(ref id) => id.to_nscsspropertyid(), - TransitionProperty::Longhand(ref id) => id.to_nscsspropertyid(), - TransitionProperty::Unsupported(..) => return Err(()), - }) - } -} - /// Convert nsCSSPropertyID to TransitionProperty #[cfg(feature = "gecko")] #[allow(non_upper_case_globals)] @@ -168,15 +95,6 @@ impl From<nsCSSPropertyID> for TransitionProperty { } } -impl SpecifiedValueInfo for TransitionProperty { - fn collect_completion_keywords(f: KeywordsCollectFn) { - // `transition-property` can actually accept all properties and - // arbitrary identifiers, but `all` is a special one we'd like - // to list. - f(&["all"]); - } -} - /// Returns true if this nsCSSPropertyID is one of the transitionable properties. #[cfg(feature = "gecko")] pub fn nscsspropertyid_is_transitionable(property: nsCSSPropertyID) -> bool { diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs index f8389a19a79..898f7391de0 100644 --- a/components/style/properties/longhand/box.mako.rs +++ b/components/style/properties/longhand/box.mako.rs @@ -9,8 +9,6 @@ inherited=False, gecko_name="Display") %> -// TODO(SimonSapin): don't parse `inline-table`, since we don't support it -// // We allow "display" to apply to placeholders because we need to make the // placeholder pseudo-element an inline-block in the UA stylesheet in Gecko. ${helpers.predefined_type( @@ -19,10 +17,10 @@ ${helpers.predefined_type( "computed::Display::inline()", initial_specified_value="specified::Display::inline()", animation_value_type="discrete", - needs_context=False, flags="APPLIES_TO_PLACEHOLDER", spec="https://drafts.csswg.org/css-display/#propdef-display", - servo_restyle_damage="rebuild_and_reflow" + servo_restyle_damage="rebuild_and_reflow", + needs_context=product == "gecko" )} // FIXME(emilio): Listing all the display values here is very unfortunate, we should teach C++ to use the @@ -257,7 +255,6 @@ ${helpers.predefined_type( vector=True, allow_empty="NotInitial", need_index=True, - needs_context=False, animation_value_type="none", extra_prefixes=transition_extra_prefixes, spec="https://drafts.csswg.org/css-transitions/#propdef-transition-property", diff --git a/components/style/properties/longhand/ui.mako.rs b/components/style/properties/longhand/ui.mako.rs index 683a8967ea3..13a940b2b96 100644 --- a/components/style/properties/longhand/ui.mako.rs +++ b/components/style/properties/longhand/ui.mako.rs @@ -41,13 +41,17 @@ ${helpers.single_keyword("-moz-window-shadow", "none default menu tooltip sheet" enabled_in="chrome", spec="None (Nonstandard internal property)")} -// TODO(bug 1419695) This should be hidden from content. -${helpers.predefined_type("-moz-window-opacity", "Opacity", "1.0", products="gecko", - gecko_ffi_name="mWindowOpacity", - animation_value_type="ComputedValue", - spec="None (Nonstandard internal property)")} +${helpers.predefined_type( + "-moz-window-opacity", + "Opacity", + "1.0", + products="gecko", + gecko_ffi_name="mWindowOpacity", + animation_value_type="ComputedValue", + spec="None (Nonstandard internal property)", + enabled_in="chrome", +)} -// TODO(bug 1419695) This should be hidden from content. ${helpers.predefined_type( "-moz-window-transform", "Transform", @@ -56,10 +60,10 @@ ${helpers.predefined_type( gecko_ffi_name="mSpecifiedWindowTransform", flags="GETCS_NEEDS_LAYOUT_FLUSH", animation_value_type="ComputedValue", - spec="None (Nonstandard internal property)" + spec="None (Nonstandard internal property)", + enabled_in="chrome", )} -// TODO(bug 1419695) This should be hidden from content. ${helpers.predefined_type( "-moz-window-transform-origin", "TransformOrigin", @@ -69,7 +73,8 @@ ${helpers.predefined_type( products="gecko", boxed=True, flags="GETCS_NEEDS_LAYOUT_FLUSH", - spec="None (Nonstandard internal property)" + spec="None (Nonstandard internal property)", + enabled_in="chrome", )} // TODO(emilio): Probably also should be hidden from content. diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index b7bc96973b3..4d18e0ca634 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -22,8 +22,7 @@ use std::cell::RefCell; use std::fmt::{self, Write}; use std::mem::{self, ManuallyDrop}; -#[cfg(feature = "servo")] use cssparser::RGBA; -use cssparser::{CowRcStr, Parser, TokenSerializationType, serialize_identifier}; +use cssparser::{Parser, RGBA, TokenSerializationType}; use cssparser::ParserInput; #[cfg(feature = "servo")] use euclid::SideOffsets2D; use context::QuirksMode; @@ -45,10 +44,10 @@ use shared_lock::StylesheetGuards; use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode}; use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; use stylesheets::{CssRuleType, Origin, UrlExtraData}; -#[cfg(feature = "servo")] use values::Either; use values::generics::text::LineHeight; use values::computed; use values::computed::NonNegativeLength; +use values::serialize_atom_name; use rule_tree::{CascadeLevel, StrongRuleNode}; use self::computed_value_flags::*; use str::{CssString, CssStringBorrow, CssStringWriter}; @@ -427,6 +426,17 @@ impl NonCustomPropertyId { MAP[self.0] } + /// Get the property name. + #[inline] + fn name(self) -> &'static str { + static MAP: [&'static str; ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())}] = [ + % for property in data.longhands + data.shorthands + data.all_aliases(): + "${property.name}", + % endfor + ]; + MAP[self.0] + } + #[inline] fn enabled_for_all_content(self) -> bool { ${static_non_custom_property_id_set( @@ -499,6 +509,11 @@ impl NonCustomPropertyId { _ => {} } + self.allowed_in_ignoring_rule_type(context) + } + + + fn allowed_in_ignoring_rule_type(self, context: &ParserContext) -> bool { // The semantics of these are kinda hard to reason about, what follows // is a description of the different combinations that can happen with // these three sets. @@ -576,18 +591,21 @@ impl NonCustomPropertyId { } impl From<LonghandId> for NonCustomPropertyId { + #[inline] fn from(id: LonghandId) -> Self { NonCustomPropertyId(id as usize) } } impl From<ShorthandId> for NonCustomPropertyId { + #[inline] fn from(id: ShorthandId) -> Self { NonCustomPropertyId((id as usize) + ${len(data.longhands)}) } } impl From<AliasId> for NonCustomPropertyId { + #[inline] fn from(id: AliasId) -> Self { NonCustomPropertyId(id as usize + ${len(data.longhands) + len(data.shorthands)}) } @@ -814,13 +832,13 @@ bitflags! { const APPLIES_TO_FIRST_LINE = 1 << 4; /// This longhand property applies to ::placeholder. const APPLIES_TO_PLACEHOLDER = 1 << 5; + /// This property's getComputedStyle implementation requires layout + /// to be flushed. + const GETCS_NEEDS_LAYOUT_FLUSH = 1 << 6; /* The following flags are currently not used in Rust code, they * only need to be listed in corresponding properties so that * they can be checked in the C++ side via ServoCSSPropList.h. */ - /// This property's getComputedStyle implementation requires layout - /// to be flushed. - const GETCS_NEEDS_LAYOUT_FLUSH = 0; /// This property can be animated on the compositor. const CAN_ANIMATE_ON_COMPOSITOR = 0; } @@ -854,12 +872,9 @@ impl fmt::Debug for LonghandId { impl LonghandId { /// Get the name of this longhand property. + #[inline] pub fn name(&self) -> &'static str { - match *self { - % for property in data.longhands: - LonghandId::${property.camel_case} => "${property.name}", - % endfor - } + NonCustomPropertyId::from(*self).name() } /// Returns whether the longhand property is inherited by default. @@ -1197,12 +1212,9 @@ impl ToCss for ShorthandId { impl ShorthandId { /// Get the name for this shorthand property. + #[inline] pub fn name(&self) -> &'static str { - match *self { - % for property in data.shorthands: - ShorthandId::${property.camel_case} => "${property.name}", - % endfor - } + NonCustomPropertyId::from(*self).name() } /// Converts from a ShorthandId to an adequate nsCSSPropertyID. @@ -1423,11 +1435,17 @@ impl UnparsedValue { // As of this writing, only the base URL is used for property // values. // - // FIXME(emilio): These bits are slightly fishy. + // NOTE(emilio): we intentionally pase `None` as the rule type here. + // If something starts depending on it, it's probably a bug, since + // it'd change how values are parsed depending on whether we're in a + // @keyframes rule or not, for example... So think twice about + // whether you want to do this! + // + // FIXME(emilio): ParsingMode is slightly fishy... let context = ParserContext::new( Origin::Author, &self.url_data, - Some(CssRuleType::Style), + None, ParsingMode::DEFAULT, quirks_mode, ); @@ -1495,8 +1513,9 @@ impl<'a> ToCss for PropertyDeclarationId<'a> { { match *self { PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()), - PropertyDeclarationId::Custom(_) => { - serialize_identifier(&self.name(), dest) + PropertyDeclarationId::Custom(ref name) => { + dest.write_str("--")?; + serialize_atom_name(name, dest) } } } @@ -1576,8 +1595,9 @@ impl ToCss for PropertyId { PropertyId::Shorthand(id) => dest.write_str(id.name()), PropertyId::LonghandAlias(id, _) => dest.write_str(id.name()), PropertyId::ShorthandAlias(id, _) => dest.write_str(id.name()), - PropertyId::Custom(_) => { - serialize_identifier(&self.name(), dest) + PropertyId::Custom(ref name) => { + dest.write_str("--")?; + serialize_atom_name(name, dest) } } } @@ -1674,6 +1694,24 @@ impl PropertyId { Ok(id) } + /// Parses a property name, and returns an error if it's unknown or isn't + /// allowed in this context, ignoring the rule_type checks. + /// + /// This is useful for parsing stuff from CSS values, for example. + #[inline] + pub fn parse_ignoring_rule_type( + name: &str, + context: &ParserContext, + ) -> Result<Self, ()> { + let id = Self::parse_unchecked(name)?; + + if !id.allowed_in_ignoring_rule_type(context) { + return Err(()); + } + + Ok(id) + } + /// Returns a property id from Gecko's nsCSSPropertyID. #[cfg(feature = "gecko")] #[allow(non_upper_case_globals)] @@ -1728,21 +1766,6 @@ impl PropertyId { } } - /// Returns the name of the property without CSS escaping. - pub fn name(&self) -> Cow<'static, str> { - match *self { - PropertyId::ShorthandAlias(id, _) | - PropertyId::Shorthand(id) => id.name().into(), - PropertyId::LonghandAlias(id, _) | - PropertyId::Longhand(id) => id.name().into(), - PropertyId::Custom(ref name) => { - let mut s = String::new(); - write!(&mut s, "--{}", name).unwrap(); - s.into() - } - } - } - fn non_custom_id(&self) -> Option<NonCustomPropertyId> { Some(match *self { PropertyId::Custom(_) => return None, @@ -1787,6 +1810,16 @@ impl PropertyId { id.allowed_in(context) } + #[inline] + fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool { + let id = match self.non_custom_id() { + // Custom properties are allowed everywhere + None => return true, + Some(id) => id, + }; + id.allowed_in_ignoring_rule_type(context) + } + /// Whether the property supports the given CSS type. /// `ty` should a bitflags of constants in style_traits::CssType. pub fn supports_type(&self, ty: u8) -> bool { @@ -1995,13 +2028,13 @@ impl PropertyDeclaration { pub fn parse_into<'i, 't>( declarations: &mut SourcePropertyDeclaration, id: PropertyId, - name: CowRcStr<'i>, context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<(), ParseError<'i>> { assert!(declarations.is_empty()); debug_assert!(id.allowed_in(context), "{:?}", id); + let non_custom_id = id.non_custom_id(); let start = input.state(); match id { PropertyId::Custom(property_name) => { @@ -2012,7 +2045,10 @@ impl PropertyDeclaration { Ok(keyword) => DeclaredValueOwned::CSSWideKeyword(keyword), Err(()) => match ::custom_properties::SpecifiedValue::parse(input) { Ok(value) => DeclaredValueOwned::Value(value), - Err(e) => return Err(StyleParseErrorKind::new_invalid(name, e)), + Err(e) => return Err(StyleParseErrorKind::new_invalid( + format!("--{}", property_name), + e, + )), } }; declarations.push(PropertyDeclaration::Custom(CustomDeclaration { @@ -2037,7 +2073,10 @@ impl PropertyDeclaration { input.reset(&start); let (first_token_type, css) = ::custom_properties::parse_non_custom_with_var(input).map_err(|e| { - StyleParseErrorKind::new_invalid(name, e) + StyleParseErrorKind::new_invalid( + non_custom_id.unwrap().name(), + e, + ) })?; Ok(PropertyDeclaration::WithVariables(VariableDeclaration { id, @@ -2049,7 +2088,10 @@ impl PropertyDeclaration { }), })) } else { - Err(StyleParseErrorKind::new_invalid(name, err)) + Err(StyleParseErrorKind::new_invalid( + non_custom_id.unwrap().name(), + err, + )) } }) }).map(|declaration| { @@ -2083,7 +2125,10 @@ impl PropertyDeclaration { input.reset(&start); let (first_token_type, css) = ::custom_properties::parse_non_custom_with_var(input).map_err(|e| { - StyleParseErrorKind::new_invalid(name, e) + StyleParseErrorKind::new_invalid( + non_custom_id.unwrap().name(), + e, + ) })?; let unparsed = Arc::new(UnparsedValue { css: css.into_owned(), @@ -2105,7 +2150,10 @@ impl PropertyDeclaration { } Ok(()) } else { - Err(StyleParseErrorKind::new_invalid(name, err)) + Err(StyleParseErrorKind::new_invalid( + non_custom_id.unwrap().name(), + err, + )) } }) } @@ -2453,6 +2501,7 @@ pub mod style_structs { /// Whether this is a multicol style. #[cfg(feature = "servo")] pub fn is_multicol(&self) -> bool { + use values::Either; match self.column_width { Either::First(_width) => true, Either::Second(_auto) => !self.column_count.is_auto(), @@ -2564,6 +2613,59 @@ impl ComputedValues { pub fn custom_properties(&self) -> Option<<&Arc<::custom_properties::CustomPropertiesMap>> { self.custom_properties.as_ref() } + + /// Writes the value of the given longhand as a string in `dest`. + /// + /// Note that the value will usually be the computed value, except for + /// colors, where it's resolved. + pub fn get_longhand_property_value<W>( + &self, + property_id: LonghandId, + dest: &mut CssWriter<W> + ) -> fmt::Result + where + W: Write, + { + // TODO(emilio): Is it worth to merge branches here just like + // PropertyDeclaration::to_css does? + // + // We'd need to get a concept of ~resolved value, which may not be worth + // it. + match property_id { + % for prop in data.longhands: + LonghandId::${prop.camel_case} => { + let style_struct = + self.get_${prop.style_struct.ident.strip("_")}(); + let value = + style_struct + % if prop.logical: + .clone_${prop.ident}(self.writing_mode); + % else: + .clone_${prop.ident}(); + % endif + + % if prop.predefined_type == "Color": + let value = self.resolve_color(value); + % endif + + value.to_css(dest) + } + % endfor + } + } + + /// Resolves the currentColor keyword. + /// + /// Any color value from computed values (except for the 'color' property + /// itself) should go through this method. + /// + /// Usage example: + /// let top_color = + /// style.resolve_color(style.get_border().clone_border_top_color()); + #[inline] + pub fn resolve_color(&self, color: computed::Color) -> RGBA { + color.to_rgba(self.get_color().clone_color()) + } } #[cfg(feature = "servo")] @@ -2598,6 +2700,26 @@ impl ComputedValues { /// Get the initial computed values. pub fn initial_values() -> &'static Self { &*INITIAL_SERVO_VALUES } + + /// Serializes the computed value of this property as a string. + pub fn computed_value_to_string(&self, property: PropertyDeclarationId) -> String { + match property { + PropertyDeclarationId::Longhand(id) => { + let mut s = String::new(); + self.get_longhand_property_value( + id, + &mut CssWriter::new(&mut s) + ).unwrap(); + s + } + PropertyDeclarationId::Custom(name) => { + self.custom_properties + .as_ref() + .and_then(|map| map.get(name)) + .map_or(String::new(), |value| value.to_css_string()) + } + } + } } #[cfg(feature = "servo")] @@ -2676,18 +2798,6 @@ impl ComputedValuesInner { self.get_column().is_multicol() } - /// Resolves the currentColor keyword. - /// - /// Any color value from computed values (except for the 'color' property - /// itself) should go through this method. - /// - /// Usage example: - /// let top_color = style.resolve_color(style.Border.border_top_color); - #[inline] - pub fn resolve_color(&self, color: computed::Color) -> RGBA { - color.to_rgba(self.get_color().color) - } - /// Get the logical computed inline size. #[inline] pub fn content_inline_size(&self) -> computed::LengthOrPercentageOrAuto { @@ -2848,26 +2958,6 @@ impl ComputedValuesInner { // Neither perspective nor transform present false } - - /// Serializes the computed value of this property as a string. - pub fn computed_value_to_string(&self, property: PropertyDeclarationId) -> String { - match property { - % for style_struct in data.active_style_structs(): - % for longhand in style_struct.longhands: - PropertyDeclarationId::Longhand(LonghandId::${longhand.camel_case}) => { - self.${style_struct.ident}.${longhand.ident}.to_css_string() - } - % endfor - % endfor - PropertyDeclarationId::Custom(name) => { - self.custom_properties - .as_ref() - .and_then(|map| map.get(name)) - .map(|value| value.to_css_string()) - .unwrap_or(String::new()) - } - } - } } % if product == "gecko": diff --git a/components/style/properties/shorthand/box.mako.rs b/components/style/properties/shorthand/box.mako.rs index 1d3bab6eaa4..842dfbd7229 100644 --- a/components/style/properties/shorthand/box.mako.rs +++ b/components/style/properties/shorthand/box.mako.rs @@ -123,9 +123,11 @@ macro_rules! try_parse_one { transition-timing-function transition-delay" spec="https://drafts.csswg.org/css-transitions/#propdef-transition"> + use parser::Parse; % for prop in "delay duration property timing_function".split(): use properties::longhands::transition_${prop}; % endfor + use values::specified::TransitionProperty; pub fn parse_value<'i, 't>( context: &ParserContext, @@ -137,7 +139,7 @@ macro_rules! try_parse_one { % endfor // Unlike other properties, transition-property uses an Option<> to // represent 'none' as `None`. - transition_property: Option<transition_property::SingleSpecifiedValue>, + transition_property: Option<TransitionProperty>, } fn parse_one_transition<'i, 't>( @@ -158,10 +160,12 @@ macro_rules! try_parse_one { // Must check 'transition-property' after 'transition-timing-function' since // 'transition-property' accepts any keyword. if property.is_none() { - if let Ok(value) = input.try(|i| transition_property::SingleSpecifiedValue::parse(i)) { + if let Ok(value) = input.try(|i| TransitionProperty::parse(context, i)) { property = Some(Some(value)); continue; - } else if input.try(|i| i.expect_ident_matching("none")).is_ok() { + } + + if input.try(|i| i.expect_ident_matching("none")).is_ok() { // 'none' is not a valid value for <single-transition-property>, // so it's not acceptable in the function above. property = Some(None); diff --git a/components/style/style_adjuster.rs b/components/style/style_adjuster.rs index 2bb400f63ec..8bbc40ca8c6 100644 --- a/components/style/style_adjuster.rs +++ b/components/style/style_adjuster.rs @@ -315,39 +315,6 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { } } - #[cfg(feature = "gecko")] - fn adjust_for_contain(&mut self) { - use properties::longhands::contain::SpecifiedValue; - - // An element with contain: paint needs to be a formatting context, and - // also implies overflow: clip. - // - // TODO(emilio): This mimics Gecko, but spec links are missing! - let contain = self.style.get_box().clone_contain(); - if !contain.contains(SpecifiedValue::PAINT) { - return; - } - - if self.style.get_box().clone_display() == Display::Inline { - self.style - .mutate_box() - .set_adjusted_display(Display::InlineBlock, false); - } - - // When 'contain: paint', update overflow from 'visible' to 'clip'. - if self.style - .get_box() - .clone_contain() - .contains(SpecifiedValue::PAINT) - { - if self.style.get_box().clone_overflow_x() == Overflow::Visible { - let box_style = self.style.mutate_box(); - box_style.set_overflow_x(Overflow::MozHiddenUnscrollable); - box_style.set_overflow_y(Overflow::MozHiddenUnscrollable); - } - } - } - /// When mathvariant is not "none", font-weight and font-style are /// both forced to "normal". #[cfg(feature = "gecko")] @@ -752,7 +719,6 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { #[cfg(feature = "gecko")] { self.adjust_for_table_text_align(); - self.adjust_for_contain(); self.adjust_for_mathvariant(); self.adjust_for_justify_items(); } diff --git a/components/style/stylesheets/keyframes_rule.rs b/components/style/stylesheets/keyframes_rule.rs index afe797c12ff..5f39267ac7b 100644 --- a/components/style/stylesheets/keyframes_rule.rs +++ b/components/style/stylesheets/keyframes_rule.rs @@ -623,12 +623,12 @@ impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeDeclarationParser<'a, 'b> { let id = match PropertyId::parse(&name, self.context) { Ok(id) => id, Err(()) => return Err(input.new_custom_error( - StyleParseErrorKind::UnknownProperty(name.clone()) + StyleParseErrorKind::UnknownProperty(name) )), }; // TODO(emilio): Shouldn't this use parse_entirely? - PropertyDeclaration::parse_into(self.declarations, id, name, self.context, input)?; + PropertyDeclaration::parse_into(self.declarations, id, self.context, input)?; // In case there is still unparsed text in the declaration, we should // roll back. diff --git a/components/style/stylesheets/supports_rule.rs b/components/style/stylesheets/supports_rule.rs index 5e2739d153d..f8c3235f295 100644 --- a/components/style/stylesheets/supports_rule.rs +++ b/components/style/stylesheets/supports_rule.rs @@ -316,20 +316,23 @@ impl Declaration { let mut input = ParserInput::new(&self.0); let mut input = Parser::new(&mut input); input.parse_entirely(|input| -> Result<(), CssParseError<()>> { - let prop = input.expect_ident_cloned().unwrap(); - input.expect_colon().unwrap(); + let prop = input.expect_ident_cloned().unwrap(); + input.expect_colon().unwrap(); - let id = PropertyId::parse(&prop, context) - .map_err(|_| input.new_custom_error(()))?; + let id = PropertyId::parse(&prop, context) + .map_err(|_| input.new_custom_error(()))?; - let mut declarations = SourcePropertyDeclaration::new(); - input.parse_until_before(Delimiter::Bang, |input| { - PropertyDeclaration::parse_into(&mut declarations, id, prop, &context, input) - .map_err(|_| input.new_custom_error(())) - })?; - let _ = input.try(parse_important); - Ok(()) - }) - .is_ok() + let mut declarations = SourcePropertyDeclaration::new(); + input.parse_until_before(Delimiter::Bang, |input| { + PropertyDeclaration::parse_into( + &mut declarations, + id, + &context, + input, + ).map_err(|_| input.new_custom_error(())) + })?; + let _ = input.try(parse_important); + Ok(()) + }).is_ok() } } diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 34f187e483a..e777921e668 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -1951,14 +1951,9 @@ pub struct CascadeData { /// cascade level. normal_rules: ElementAndPseudoRules, - /// The `:host` pseudo rules that are the rightmost selector. - /// - /// Note that as of right now these can't affect invalidation in any way, - /// until we support the :host(<selector>) notation. - /// - /// Also, note that other engines don't accept stuff like :host::before / - /// :host::after, so we don't need to store pseudo rules at all. - host_rules: Option<Box<SelectorMap<Rule>>>, + /// The `:host` pseudo rules that are the rightmost selector (without + /// accounting for pseudo-elements). + host_rules: Option<Box<ElementAndPseudoRules>>, /// The data coming from ::slotted() pseudo-element rules. /// @@ -2122,11 +2117,7 @@ impl CascadeData { #[inline] fn host_rules(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap<Rule>> { - if pseudo.is_some() { - return None; - } - - self.host_rules.as_ref().map(|rules| &**rules) + self.host_rules.as_ref().and_then(|d| d.rules(pseudo)) } #[inline] @@ -2258,20 +2249,20 @@ impl CascadeData { } } - if selector.is_featureless_host_selector() { - let host_rules = self.host_rules - .get_or_insert_with(|| Box::new(Default::default())); - host_rules.insert(rule, quirks_mode)?; + // NOTE(emilio): It's fine to look at :host and then at + // ::slotted(..), since :host::slotted(..) could never + // possibly match, as <slot> is not a valid shadow host. + let rules = if selector.is_featureless_host_selector_or_pseudo_element() { + self.host_rules + .get_or_insert_with(|| Box::new(Default::default())) + } else if selector.is_slotted() { + self.slotted_rules + .get_or_insert_with(|| Box::new(Default::default())) } else { - let rules = if selector.is_slotted() { - self.slotted_rules - .get_or_insert_with(|| Box::new(Default::default())) - } else { - &mut self.normal_rules - }; + &mut self.normal_rules + }; - rules.insert(rule, pseudo_element, quirks_mode)?; - } + rules.insert(rule, pseudo_element, quirks_mode)?; } self.rules_source_order += 1; }, diff --git a/components/style/values/animated/color.rs b/components/style/values/animated/color.rs index 614c17df00c..1b01a86457a 100644 --- a/components/style/values/animated/color.rs +++ b/components/style/values/animated/color.rs @@ -6,6 +6,7 @@ use values::animated::{Animate, Procedure, ToAnimatedZero}; use values::distance::{ComputeSquaredDistance, SquaredDistance}; +use values::generics::color::{Color as GenericColor, ComplexColorRatios}; /// An animated RGBA color. /// @@ -91,42 +92,26 @@ impl ComputeSquaredDistance for RGBA { } } -#[allow(missing_docs)] -#[cfg_attr(feature = "servo", derive(MallocSizeOf))] -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Color { - pub color: RGBA, - pub foreground_ratio: f32, -} +/// An animated value for `<color>`. +pub type Color = GenericColor<RGBA>; impl Color { - fn currentcolor() -> Self { - Color { - color: RGBA::transparent(), - foreground_ratio: 1., + fn effective_intermediate_rgba(&self) -> RGBA { + match *self { + GenericColor::Numeric(color) => color, + GenericColor::Foreground => RGBA::transparent(), + GenericColor::Complex(color, ratios) => RGBA { + alpha: color.alpha * ratios.bg, + ..color.clone() + }, } } - /// Returns a transparent intermediate color. - pub fn transparent() -> Self { - Color { - color: RGBA::transparent(), - foreground_ratio: 0., - } - } - - fn is_currentcolor(&self) -> bool { - self.foreground_ratio >= 1. - } - - fn is_numeric(&self) -> bool { - self.foreground_ratio <= 0. - } - - fn effective_intermediate_rgba(&self) -> RGBA { - RGBA { - alpha: self.color.alpha * (1. - self.foreground_ratio), - ..self.color + fn effective_ratios(&self) -> ComplexColorRatios { + match *self { + GenericColor::Numeric(..) => ComplexColorRatios::NUMERIC, + GenericColor::Foreground => ComplexColorRatios::FOREGROUND, + GenericColor::Complex(.., ratios) => ratios, } } } @@ -134,80 +119,88 @@ impl Color { impl Animate for Color { #[inline] fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { + use self::GenericColor::*; + // Common cases are interpolating between two numeric colors, // two currentcolors, and a numeric color and a currentcolor. - // - // Note: this algorithm assumes self_portion + other_portion - // equals to one, so it may be broken for additive operation. - // To properly support additive color interpolation, we would - // need two ratio fields in computed color types. let (this_weight, other_weight) = procedure.weights(); - if self.foreground_ratio == other.foreground_ratio { - if self.is_currentcolor() { - Ok(Color::currentcolor()) - } else { - Ok(Color { - color: self.color.animate(&other.color, procedure)?, - foreground_ratio: self.foreground_ratio, - }) - } - } else if self.is_currentcolor() && other.is_numeric() { - Ok(Color { - color: other.color, - foreground_ratio: this_weight as f32, - }) - } else if self.is_numeric() && other.is_currentcolor() { - Ok(Color { - color: self.color, - foreground_ratio: other_weight as f32, - }) - } else { - // For interpolating between two complex colors, we need to - // generate colors with effective alpha value. - let self_color = self.effective_intermediate_rgba(); - let other_color = other.effective_intermediate_rgba(); - let color = self_color.animate(&other_color, procedure)?; - // Then we compute the final foreground ratio, and derive - // the final alpha value from the effective alpha value. - let foreground_ratio = self.foreground_ratio - .animate(&other.foreground_ratio, procedure)?; - let alpha = color.alpha / (1. - foreground_ratio); - Ok(Color { - color: RGBA { - alpha: alpha, - ..color + + Ok(match (*self, *other, procedure) { + // Any interpolation of currentColor with currentColor returns currentColor. + (Foreground, Foreground, Procedure::Interpolate { .. }) => Color::currentcolor(), + // Animating two numeric colors. + (Numeric(c1), Numeric(c2), _) => Numeric(c1.animate(&c2, procedure)?), + // Combinations of numeric color and currentColor + (Foreground, Numeric(color), _) => Self::with_ratios( + color, + ComplexColorRatios { + bg: other_weight as f32, + fg: this_weight as f32, }, - foreground_ratio: foreground_ratio, - }) - } + ), + (Numeric(color), Foreground, _) => Self::with_ratios( + color, + ComplexColorRatios { + bg: this_weight as f32, + fg: other_weight as f32, + }, + ), + + // Any other animation of currentColor with currentColor. + (Foreground, Foreground, _) => Self::with_ratios( + RGBA::transparent(), + ComplexColorRatios { + bg: 0., + fg: (this_weight + other_weight) as f32, + }, + ), + + // Defer to complex calculations + _ => { + // For interpolating between two complex colors, we need to + // generate colors with effective alpha value. + let self_color = self.effective_intermediate_rgba(); + let other_color = other.effective_intermediate_rgba(); + let color = self_color.animate(&other_color, procedure)?; + // Then we compute the final background ratio, and derive + // the final alpha value from the effective alpha value. + let self_ratios = self.effective_ratios(); + let other_ratios = other.effective_ratios(); + let ratios = self_ratios.animate(&other_ratios, procedure)?; + let alpha = color.alpha / ratios.bg; + let color = RGBA { alpha, ..color }; + + Self::with_ratios(color, ratios) + } + }) } } impl ComputeSquaredDistance for Color { #[inline] fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + use self::GenericColor::*; + // All comments from the Animate impl also applies here. - if self.foreground_ratio == other.foreground_ratio { - if self.is_currentcolor() { - Ok(SquaredDistance::from_sqrt(0.)) - } else { - self.color.compute_squared_distance(&other.color) + Ok(match (*self, *other) { + (Foreground, Foreground) => SquaredDistance::from_sqrt(0.), + (Numeric(c1), Numeric(c2)) => c1.compute_squared_distance(&c2)?, + (Foreground, Numeric(color)) | (Numeric(color), Foreground) => { + // `computed_squared_distance` is symmetic. + color.compute_squared_distance(&RGBA::transparent())? + + SquaredDistance::from_sqrt(1.) } - } else if self.is_currentcolor() && other.is_numeric() { - Ok( - RGBA::transparent().compute_squared_distance(&other.color)? + - SquaredDistance::from_sqrt(1.), - ) - } else if self.is_numeric() && other.is_currentcolor() { - Ok(self.color.compute_squared_distance(&RGBA::transparent())? + - SquaredDistance::from_sqrt(1.)) - } else { - let self_color = self.effective_intermediate_rgba(); - let other_color = other.effective_intermediate_rgba(); - Ok(self_color.compute_squared_distance(&other_color)? + - self.foreground_ratio - .compute_squared_distance(&other.foreground_ratio)?) - } + (_, _) => { + let self_color = self.effective_intermediate_rgba(); + let other_color = other.effective_intermediate_rgba(); + let self_ratios = self.effective_ratios(); + let other_ratios = other.effective_ratios(); + + self_color.compute_squared_distance(&other_color)? + + self_ratios.bg.compute_squared_distance(&other_ratios.bg)? + + self_ratios.fg.compute_squared_distance(&other_ratios.fg)? + } + }) } } diff --git a/components/style/values/computed/box.rs b/components/style/values/computed/box.rs index 96696702a2a..224fe4669a1 100644 --- a/components/style/values/computed/box.rs +++ b/components/style/values/computed/box.rs @@ -11,7 +11,7 @@ use values::generics::box_::Perspective as GenericPerspective; use values::generics::box_::VerticalAlign as GenericVerticalAlign; pub use values::specified::box_::{AnimationName, Contain, Display, OverflowClipBox}; -pub use values::specified::box_::{OverscrollBehavior, ScrollSnapType, TouchAction, WillChange}; +pub use values::specified::box_::{OverscrollBehavior, ScrollSnapType, TouchAction, TransitionProperty, WillChange}; /// A computed value for the `vertical-align` property. pub type VerticalAlign = GenericVerticalAlign<LengthOrPercentage>; diff --git a/components/style/values/computed/color.rs b/components/style/values/computed/color.rs index d37aef00b9d..5effffe5966 100644 --- a/components/style/values/computed/color.rs +++ b/components/style/values/computed/color.rs @@ -8,20 +8,8 @@ use cssparser::{Color as CSSParserColor, RGBA}; use std::fmt; use style_traits::{CssWriter, ToCss}; use values::animated::ToAnimatedValue; -use values::animated::color::{Color as AnimatedColor, RGBA as AnimatedRGBA}; - -/// This struct represents a combined color from a numeric color and -/// the current foreground color (currentcolor keyword). -/// Conceptually, the formula is "color * (1 - p) + currentcolor * p" -/// where p is foreground_ratio. -#[derive(Clone, Copy, Debug, MallocSizeOf)] -pub struct Color { - /// RGBA color. - pub color: RGBA, - - /// The ratio of currentcolor in complex color. - pub foreground_ratio: u8, -} +use values::animated::color::RGBA as AnimatedRGBA; +use values::generics::color::Color as GenericColor; /// Computed value type for the specified RGBAColor. pub type RGBAColor = RGBA; @@ -29,89 +17,49 @@ pub type RGBAColor = RGBA; /// The computed value of the `color` property. pub type ColorPropertyValue = RGBA; -impl Color { - /// Returns a numeric color representing the given RGBA value. - pub fn rgba(rgba: RGBA) -> Color { - Color { - color: rgba, - foreground_ratio: 0, - } - } +/// A computed value for `<color>`. +pub type Color = GenericColor<RGBAColor>; +impl Color { /// Returns a complex color value representing transparent. pub fn transparent() -> Color { Color::rgba(RGBA::transparent()) } - /// Returns a complex color value representing currentcolor. - pub fn currentcolor() -> Color { - Color { - color: RGBA::transparent(), - foreground_ratio: u8::max_value(), - } - } - - /// Whether it is a numeric color (no currentcolor component). - pub fn is_numeric(&self) -> bool { - self.foreground_ratio == 0 - } - - /// Whether it is a currentcolor value (no numeric color component). - pub fn is_currentcolor(&self) -> bool { - self.foreground_ratio == u8::max_value() - } - /// Combine this complex color with the given foreground color into /// a numeric RGBA color. It currently uses linear blending. pub fn to_rgba(&self, fg_color: RGBA) -> RGBA { - // Common cases that the complex color is either pure numeric - // color or pure currentcolor. - if self.is_numeric() { - return self.color; - } - if self.is_currentcolor() { - return fg_color.clone(); - } - - fn blend_color_component(bg: u8, fg: u8, fg_alpha: u8) -> u8 { - let bg_ratio = (u8::max_value() - fg_alpha) as u32; - let fg_ratio = fg_alpha as u32; - let color = bg as u32 * bg_ratio + fg as u32 * fg_ratio; - // Rounding divide the number by 255 - ((color + 127) / 255) as u8 - } - - // Common case that alpha channel is equal (usually both are opaque). - let fg_ratio = self.foreground_ratio; - if self.color.alpha == fg_color.alpha { - let r = blend_color_component(self.color.red, fg_color.red, fg_ratio); - let g = blend_color_component(self.color.green, fg_color.green, fg_ratio); - let b = blend_color_component(self.color.blue, fg_color.blue, fg_ratio); - return RGBA::new(r, g, b, fg_color.alpha); - } + let (color, ratios) = match *self { + // Common cases that the complex color is either pure numeric + // color or pure currentcolor. + GenericColor::Numeric(color) => return color, + GenericColor::Foreground => return fg_color, + GenericColor::Complex(color, ratios) => (color, ratios), + }; // For the more complicated case that the alpha value differs, // we use the following formula to compute the components: - // alpha = self_alpha * (1 - fg_ratio) + fg_alpha * fg_ratio - // color = (self_color * self_alpha * (1 - fg_ratio) + + // alpha = self_alpha * bg_ratio + fg_alpha * fg_ratio + // color = (self_color * self_alpha * bg_ratio + // fg_color * fg_alpha * fg_ratio) / alpha - let p1 = (1. / 255.) * (255 - fg_ratio) as f32; - let a1 = self.color.alpha_f32(); - let r1 = a1 * self.color.red_f32(); - let g1 = a1 * self.color.green_f32(); - let b1 = a1 * self.color.blue_f32(); + let p1 = ratios.bg; + let a1 = color.alpha_f32(); + let r1 = a1 * color.red_f32(); + let g1 = a1 * color.green_f32(); + let b1 = a1 * color.blue_f32(); - let p2 = 1. - p1; + let p2 = ratios.fg; let a2 = fg_color.alpha_f32(); let r2 = a2 * fg_color.red_f32(); let g2 = a2 * fg_color.green_f32(); let b2 = a2 * fg_color.blue_f32(); let a = p1 * a1 + p2 * a2; - if a == 0.0 { + if a <= 0. { return RGBA::transparent(); } + let a = f32::min(a, 1.); let inverse_a = 1. / a; let r = (p1 * r1 + p2 * r2) * inverse_a; @@ -121,53 +69,15 @@ impl Color { } } -impl PartialEq for Color { - fn eq(&self, other: &Color) -> bool { - self.foreground_ratio == other.foreground_ratio && - (self.is_currentcolor() || self.color == other.color) - } -} - -impl From<RGBA> for Color { - fn from(color: RGBA) -> Color { - Color { - color: color, - foreground_ratio: 0, - } - } -} - impl ToCss for Color { fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write, { - if self.is_numeric() { - self.color.to_css(dest) - } else if self.is_currentcolor() { - CSSParserColor::CurrentColor.to_css(dest) - } else { - Ok(()) - } - } -} - -impl ToAnimatedValue for Color { - type AnimatedValue = AnimatedColor; - - #[inline] - fn to_animated_value(self) -> Self::AnimatedValue { - AnimatedColor { - color: self.color.to_animated_value(), - foreground_ratio: self.foreground_ratio as f32 * (1. / 255.), - } - } - - #[inline] - fn from_animated_value(animated: Self::AnimatedValue) -> Self { - Color { - color: RGBA::from_animated_value(animated.color), - foreground_ratio: (animated.foreground_ratio * 255.).round() as u8, + match *self { + GenericColor::Numeric(color) => color.to_css(dest), + GenericColor::Foreground => CSSParserColor::CurrentColor.to_css(dest), + _ => Ok(()), } } } diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index d6660f6a6eb..e742c4e49f1 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -30,7 +30,6 @@ use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent use super::specified; pub use app_units::Au; -pub use properties::animated_properties::TransitionProperty; #[cfg(feature = "gecko")] pub use self::align::{AlignContent, AlignItems, JustifyContent, JustifyItems, SelfAlignment}; #[cfg(feature = "gecko")] @@ -43,7 +42,7 @@ pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis, FontV pub use self::font::{FontFamily, FontLanguageOverride, FontStyle, FontVariantEastAsian, FontVariationSettings}; pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric}; pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom}; -pub use self::box_::{AnimationIterationCount, AnimationName, Contain, Display}; +pub use self::box_::{AnimationIterationCount, AnimationName, Contain, Display, TransitionProperty}; pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective}; pub use self::box_::{ScrollSnapType, TouchAction, VerticalAlign, WillChange}; pub use self::color::{Color, ColorPropertyValue, RGBAColor}; diff --git a/components/style/values/generics/color.rs b/components/style/values/generics/color.rs new file mode 100644 index 00000000000..e84bd690c7c --- /dev/null +++ b/components/style/values/generics/color.rs @@ -0,0 +1,76 @@ +/* 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/. */ + +//! Generic types for color properties. + +/// Ratios representing the contribution of color and currentcolor to +/// the final color value. +#[derive(Animate, Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue)] +pub struct ComplexColorRatios { + /// Numeric color contribution. + pub bg: f32, + /// Foreground color, aka currentcolor, contribution. + pub fg: f32, +} + +impl ComplexColorRatios { + /// Ratios representing a `Numeric` color. + pub const NUMERIC: ComplexColorRatios = ComplexColorRatios { bg: 1., fg: 0. }; + /// Ratios representing the `Foreground` color. + pub const FOREGROUND: ComplexColorRatios = ComplexColorRatios { bg: 0., fg: 1. }; +} + +/// This enum represents a combined color from a numeric color and +/// the current foreground color (currentcolor keyword). +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue)] +pub enum Color<RGBA> { + /// Numeric RGBA color. + Numeric(RGBA), + + /// The current foreground color. + Foreground, + + /// A linear combination of numeric color and currentcolor. + /// The formula is: `color * ratios.bg + currentcolor * ratios.fg`. + Complex(RGBA, ComplexColorRatios), +} + +impl<RGBA> Color<RGBA> { + /// Create a color based upon the specified ratios. + pub fn with_ratios(color: RGBA, ratios: ComplexColorRatios) -> Self { + if ratios == ComplexColorRatios::NUMERIC { + Color::Numeric(color) + } else if ratios == ComplexColorRatios::FOREGROUND { + Color::Foreground + } else { + Color::Complex(color, ratios) + } + } + + /// Returns a numeric color representing the given RGBA value. + pub fn rgba(color: RGBA) -> Self { + Color::Numeric(color) + } + + /// Returns a complex color value representing currentcolor. + pub fn currentcolor() -> Self { + Color::Foreground + } + + /// Whether it is a numeric color (no currentcolor component). + pub fn is_numeric(&self) -> bool { + matches!(*self, Color::Numeric(..)) + } + + /// Whether it is a currentcolor value (no numeric color component). + pub fn is_currentcolor(&self) -> bool { + matches!(*self, Color::Foreground) + } +} + +impl<RGBA> From<RGBA> for Color<RGBA> { + fn from(color: RGBA) -> Self { + Self::rgba(color) + } +} diff --git a/components/style/values/generics/mod.rs b/components/style/values/generics/mod.rs index c3b29c93769..e6c1befea9f 100644 --- a/components/style/values/generics/mod.rs +++ b/components/style/values/generics/mod.rs @@ -17,6 +17,7 @@ pub mod basic_shape; pub mod border; #[path = "box.rs"] pub mod box_; +pub mod color; pub mod column; pub mod counters; pub mod effects; diff --git a/components/style/values/mod.rs b/components/style/values/mod.rs index f02242e78d1..a5f8d0abd32 100644 --- a/components/style/values/mod.rs +++ b/components/style/values/mod.rs @@ -9,7 +9,7 @@ #![deny(missing_docs)] use Atom; -pub use cssparser::{serialize_identifier, CowRcStr, Parser, SourceLocation, Token, RGBA}; +pub use cssparser::{serialize_identifier, serialize_name, CowRcStr, Parser, SourceLocation, Token, RGBA}; use parser::{Parse, ParserContext}; use selectors::parser::SelectorParseErrorKind; use std::fmt::{self, Debug, Write}; @@ -60,6 +60,28 @@ where serialize_identifier(&ident, dest) } +/// Serialize a name which is represented as an Atom. +#[cfg(feature = "gecko")] +pub fn serialize_atom_name<W>(ident: &Atom, dest: &mut W) -> fmt::Result +where + W: Write, +{ + ident.with_str(|s| serialize_name(s, dest)) +} + +/// Serialize a name which is represented as an Atom. +#[cfg(feature = "servo")] +pub fn serialize_atom_name<Static, W>( + ident: &::string_cache::Atom<Static>, + dest: &mut W, +) -> fmt::Result +where + Static: ::string_cache::StaticAtomSet, + W: Write, +{ + serialize_name(&ident, dest) +} + /// Serialize a normalized value into percentage. pub fn serialize_percentage<W>(value: CSSFloat, dest: &mut CssWriter<W>) -> fmt::Result where diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs index dcce3d592ab..7980fb2b2e7 100644 --- a/components/style/values/specified/box.rs +++ b/components/style/values/specified/box.rs @@ -6,11 +6,12 @@ use Atom; use cssparser::Parser; +use custom_properties::Name as CustomPropertyName; use parser::{Parse, ParserContext}; -use properties::{LonghandId, PropertyId, PropertyFlags, PropertyDeclarationId}; +use properties::{LonghandId, ShorthandId, PropertyId, PropertyFlags, PropertyDeclarationId}; use selectors::parser::SelectorParseErrorKind; use std::fmt::{self, Write}; -use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; +use style_traits::{CssWriter, KeywordsCollectFn, ParseError, StyleParseErrorKind, SpecifiedValueInfo, ToCss}; use values::{CustomIdent, KeyframesName}; use values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount; use values::generics::box_::Perspective as GenericPerspective; @@ -18,6 +19,17 @@ use values::generics::box_::VerticalAlign as GenericVerticalAlign; use values::specified::{AllowQuirks, Number}; use values::specified::length::{LengthOrPercentage, NonNegativeLength}; +#[cfg(feature = "gecko")] +fn moz_display_values_enabled(context: &ParserContext) -> bool { + use gecko_bindings::structs; + use stylesheets::Origin; + context.stylesheet_origin == Origin::UserAgent || + context.chrome_rules_enabled() || + unsafe { + structs::StaticPrefs_sVarCache_layout_css_xul_display_values_content_enabled + } +} + #[allow(missing_docs)] #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)] @@ -41,9 +53,9 @@ pub enum Display { TableCaption, ListItem, None, - #[css(aliases = "-webkit-flex")] + #[parse(aliases = "-webkit-flex")] Flex, - #[css(aliases = "-webkit-inline-flex")] + #[parse(aliases = "-webkit-inline-flex")] InlineFlex, #[cfg(feature = "gecko")] Grid, @@ -72,22 +84,31 @@ pub enum Display { #[cfg(feature = "gecko")] MozInlineBox, #[cfg(feature = "gecko")] + #[parse(condition = "moz_display_values_enabled")] MozGrid, #[cfg(feature = "gecko")] + #[parse(condition = "moz_display_values_enabled")] MozInlineGrid, #[cfg(feature = "gecko")] + #[parse(condition = "moz_display_values_enabled")] MozGridGroup, #[cfg(feature = "gecko")] + #[parse(condition = "moz_display_values_enabled")] MozGridLine, #[cfg(feature = "gecko")] + #[parse(condition = "moz_display_values_enabled")] MozStack, #[cfg(feature = "gecko")] + #[parse(condition = "moz_display_values_enabled")] MozInlineStack, #[cfg(feature = "gecko")] + #[parse(condition = "moz_display_values_enabled")] MozDeck, #[cfg(feature = "gecko")] + #[parse(condition = "moz_display_values_enabled")] MozPopup, #[cfg(feature = "gecko")] + #[parse(condition = "moz_display_values_enabled")] MozGroupbox, } @@ -444,7 +465,7 @@ fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits { } fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits { - let id = match PropertyId::parse(ident, context) { + let id = match PropertyId::parse_ignoring_rule_type(ident, context) { Ok(id) => id, Err(..) => return WillChangeBits::empty(), }; @@ -714,3 +735,96 @@ impl Parse for Perspective { )?)) } } + +/// A given transition property, that is either `All`, a longhand or shorthand +/// property, or an unsupported or custom property. +#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue)] +pub enum TransitionProperty { + /// A shorthand. + Shorthand(ShorthandId), + /// A longhand transitionable property. + Longhand(LonghandId), + /// A custom property. + Custom(CustomPropertyName), + /// Unrecognized property which could be any non-transitionable, custom property, or + /// unknown property. + Unsupported(CustomIdent), +} + +impl ToCss for TransitionProperty { + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result + where + W: Write, + { + use values::serialize_atom_name; + match *self { + TransitionProperty::Shorthand(ref s) => s.to_css(dest), + TransitionProperty::Longhand(ref l) => l.to_css(dest), + TransitionProperty::Custom(ref name) => { + dest.write_str("--")?; + serialize_atom_name(name, dest) + } + TransitionProperty::Unsupported(ref i) => i.to_css(dest), + } + } +} + +impl Parse for TransitionProperty { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result<Self, ParseError<'i>> { + let location = input.current_source_location(); + let ident = input.expect_ident()?; + + let id = match PropertyId::parse_ignoring_rule_type(&ident, context) { + Ok(id) => id, + Err(..) => return Ok(TransitionProperty::Unsupported( + CustomIdent::from_ident(location, ident, &["none"])?, + )), + }; + + Ok(match id.as_shorthand() { + Ok(s) => TransitionProperty::Shorthand(s), + Err(longhand_or_custom) => { + match longhand_or_custom { + PropertyDeclarationId::Longhand(id) => TransitionProperty::Longhand(id), + PropertyDeclarationId::Custom(custom) => { + TransitionProperty::Custom(custom.clone()) + } + } + } + }) + } +} + +impl SpecifiedValueInfo for TransitionProperty { + fn collect_completion_keywords(f: KeywordsCollectFn) { + // `transition-property` can actually accept all properties and + // arbitrary identifiers, but `all` is a special one we'd like + // to list. + f(&["all"]); + } +} + +impl TransitionProperty { + /// Returns `all`. + #[inline] + pub fn all() -> Self { + TransitionProperty::Shorthand(ShorthandId::All) + } + + /// Convert TransitionProperty to nsCSSPropertyID. + #[cfg(feature = "gecko")] + pub fn to_nscsspropertyid(&self) -> Result<::gecko_bindings::structs::nsCSSPropertyID, ()> { + Ok(match *self { + TransitionProperty::Shorthand(ShorthandId::All) => { + ::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_all_properties + } + TransitionProperty::Shorthand(ref id) => id.to_nscsspropertyid(), + TransitionProperty::Longhand(ref id) => id.to_nscsspropertyid(), + TransitionProperty::Custom(..) | + TransitionProperty::Unsupported(..) => return Err(()), + }) + } +} diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index 3066c5b8c2a..c05f0ddce0e 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -18,6 +18,7 @@ use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError, StyleParse use style_traits::{SpecifiedValueInfo, ToCss, ValueParseErrorKind}; use super::AllowQuirks; use values::computed::{Color as ComputedColor, Context, ToComputedValue}; +use values::generics::color::Color as GenericColor; use values::specified::calc::CalcNode; /// Specified color value @@ -88,11 +89,11 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::ColorComponentParser<'i> for ColorComponen }; Ok(AngleOrNumber::Angle { degrees }) - }, + } Token::Number { value, .. } => Ok(AngleOrNumber::Number { value }), Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { input.parse_nested_block(|i| CalcNode::parse_angle_or_number(self.0, i)) - }, + } t => return Err(location.new_unexpected_token_error(t)), } } @@ -119,10 +120,10 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::ColorComponentParser<'i> for ColorComponen Token::Number { value, .. } => Ok(NumberOrPercentage::Number { value }), Token::Percentage { unit_value, .. } => { Ok(NumberOrPercentage::Percentage { unit_value }) - }, + } Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { input.parse_nested_block(|i| CalcNode::parse_number_or_percentage(self.0, i)) - }, + } t => return Err(location.new_unexpected_token_error(t)), } } @@ -168,10 +169,10 @@ impl Parse for Color { Err(e.location.new_custom_error(StyleParseErrorKind::ValueError( ValueParseErrorKind::InvalidColor(t), ))) - }, + } _ => Err(e), } - }, + } } } } @@ -275,10 +276,10 @@ impl Color { } return parse_hash_color(ident.as_bytes()) .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - }, + } ref t => { return Err(location.new_unexpected_token_error(t.clone())); - }, + } }; if value < 0 { return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); @@ -358,11 +359,11 @@ impl Color { Keyword::MozVisitedhyperlinktext => pres_context.mVisitedLinkColor, }) }) - }, + } #[cfg(feature = "gecko")] Color::InheritFromBodyQuirk => { _context.map(|context| ComputedColor::rgba(context.device().body_text_color())) - }, + } } } } @@ -372,7 +373,7 @@ impl ToComputedValue for Color { fn to_computed_value(&self, context: &Context) -> ComputedColor { let result = self.to_computed_color(Some(context)).unwrap(); - if result.foreground_ratio != 0 { + if !result.is_numeric() { if let Some(longhand) = context.for_non_inherited_property { if longhand.stores_complex_colors_lossily() { context.rule_cache_conditions.borrow_mut().set_uncacheable(); @@ -383,12 +384,10 @@ impl ToComputedValue for Color { } fn from_computed_value(computed: &ComputedColor) -> Self { - if computed.is_numeric() { - Color::rgba(computed.color) - } else if computed.is_currentcolor() { - Color::currentcolor() - } else { - Color::Complex(*computed) + match *computed { + GenericColor::Numeric(color) => Color::rgba(color), + GenericColor::Foreground => Color::currentcolor(), + GenericColor::Complex(..) => Color::Complex(*computed), } } } diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index c25d58332f5..c5794bbad1e 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -23,7 +23,6 @@ use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericT use values::serialize_atom_identifier; use values::specified::calc::CalcNode; -pub use properties::animated_properties::TransitionProperty; pub use self::angle::Angle; #[cfg(feature = "gecko")] pub use self::align::{AlignContent, AlignItems, AlignSelf, ContentDistribution}; @@ -40,7 +39,7 @@ pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumer pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom}; pub use self::box_::{AnimationIterationCount, AnimationName, Contain, Display}; pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective}; -pub use self::box_::{ScrollSnapType, TouchAction, VerticalAlign, WillChange}; +pub use self::box_::{ScrollSnapType, TouchAction, TransitionProperty, VerticalAlign, WillChange}; pub use self::color::{Color, ColorPropertyValue, RGBAColor}; pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset}; pub use self::effects::{BoxShadow, Filter, SimpleShadow}; diff --git a/components/style_derive/lib.rs b/components/style_derive/lib.rs index 88f0bbadfb1..8c8c0705e8f 100644 --- a/components/style_derive/lib.rs +++ b/components/style_derive/lib.rs @@ -40,7 +40,7 @@ pub fn derive_to_animated_value(stream: TokenStream) -> TokenStream { to_animated_value::derive(input).into() } -#[proc_macro_derive(Parse, attributes(css))] +#[proc_macro_derive(Parse, attributes(css, parse))] pub fn derive_parse(stream: TokenStream) -> TokenStream { let input = syn::parse(stream).unwrap(); parse::derive(input).into() @@ -64,7 +64,7 @@ pub fn derive_to_css(stream: TokenStream) -> TokenStream { to_css::derive(input).into() } -#[proc_macro_derive(SpecifiedValueInfo, attributes(css, value_info))] +#[proc_macro_derive(SpecifiedValueInfo, attributes(css, parse, value_info))] pub fn derive_specified_value_info(stream: TokenStream) -> TokenStream { let input = syn::parse(stream).unwrap(); specified_value_info::derive(input).into() diff --git a/components/style_derive/parse.rs b/components/style_derive/parse.rs index 7727d6b89bb..13d8aa6f48b 100644 --- a/components/style_derive/parse.rs +++ b/components/style_derive/parse.rs @@ -4,14 +4,22 @@ use cg; use quote::Tokens; -use syn::DeriveInput; +use syn::{DeriveInput, Path}; use synstructure; use to_css::CssVariantAttrs; +#[darling(attributes(parse), default)] +#[derive(Default, FromVariant)] +pub struct ParseVariantAttrs { + pub aliases: Option<String>, + pub condition: Option<Path>, +} + pub fn derive(input: DeriveInput) -> Tokens { let name = &input.ident; let s = synstructure::Structure::new(&input); + let mut saw_condition = false; let match_body = s.variants().iter().fold(quote!(), |match_body, variant| { let bindings = variant.bindings(); assert!( @@ -19,23 +27,31 @@ pub fn derive(input: DeriveInput) -> Tokens { "Parse is only supported for single-variant enums for now" ); - let variant_attrs = cg::parse_variant_attrs_from_ast::<CssVariantAttrs>(&variant.ast()); - if variant_attrs.skip { + let css_variant_attrs = + cg::parse_variant_attrs_from_ast::<CssVariantAttrs>(&variant.ast()); + let parse_attrs = + cg::parse_variant_attrs_from_ast::<ParseVariantAttrs>(&variant.ast()); + if css_variant_attrs.skip { return match_body; } let identifier = cg::to_css_identifier( - &variant_attrs.keyword.unwrap_or(variant.ast().ident.as_ref().into()), + &css_variant_attrs.keyword.unwrap_or(variant.ast().ident.as_ref().into()), ); let ident = &variant.ast().ident; + saw_condition |= parse_attrs.condition.is_some(); + let condition = match parse_attrs.condition { + Some(ref p) => quote! { if #p(context) }, + None => quote! { }, + }; + let mut body = quote! { #match_body - #identifier => Ok(#name::#ident), + #identifier #condition => Ok(#name::#ident), }; - - let aliases = match variant_attrs.aliases { + let aliases = match parse_attrs.aliases { Some(aliases) => aliases, None => return body, }; @@ -43,25 +59,51 @@ pub fn derive(input: DeriveInput) -> Tokens { for alias in aliases.split(",") { body = quote! { #body - #alias => Ok(#name::#ident), + #alias #condition => Ok(#name::#ident), }; } body }); + let context_ident = if saw_condition { + quote! { context } + } else { + quote! { _ } + }; + + let parse_body = if saw_condition { + quote! { + let location = input.current_source_location(); + let ident = input.expect_ident()?; + match_ignore_ascii_case! { &ident, + #match_body + _ => Err(location.new_unexpected_token_error( + ::cssparser::Token::Ident(ident.clone()) + )) + } + } + } else { + quote! { Self::parse(input) } + }; + + let parse_trait_impl = quote! { impl ::parser::Parse for #name { #[inline] fn parse<'i, 't>( - _: &::parser::ParserContext, + #context_ident: &::parser::ParserContext, input: &mut ::cssparser::Parser<'i, 't>, ) -> Result<Self, ::style_traits::ParseError<'i>> { - Self::parse(input) + #parse_body } } }; + if saw_condition { + return parse_trait_impl; + } + // TODO(emilio): It'd be nice to get rid of these, but that makes the // conversion harder... let methods_impl = quote! { diff --git a/components/style_derive/specified_value_info.rs b/components/style_derive/specified_value_info.rs index 7aae081641d..b707ec2c69f 100644 --- a/components/style_derive/specified_value_info.rs +++ b/components/style_derive/specified_value_info.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use cg; +use parse::ParseVariantAttrs; use quote::Tokens; use syn::{Data, DeriveInput, Fields, Ident, Type}; use to_css::{CssFieldAttrs, CssInputAttrs, CssVariantAttrs}; @@ -33,10 +34,11 @@ pub fn derive(mut input: DeriveInput) -> Tokens { for v in e.variants.iter() { let css_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&v); let info_attrs = cg::parse_variant_attrs::<ValueInfoVariantAttrs>(&v); + let parse_attrs = cg::parse_variant_attrs::<ParseVariantAttrs>(&v); if css_attrs.skip { continue; } - if let Some(aliases) = css_attrs.aliases { + if let Some(aliases) = parse_attrs.aliases { for alias in aliases.split(",") { values.push(alias.to_string()); } diff --git a/components/style_derive/to_css.rs b/components/style_derive/to_css.rs index 0caef8b01a7..471cf37b6de 100644 --- a/components/style_derive/to_css.rs +++ b/components/style_derive/to_css.rs @@ -234,7 +234,6 @@ pub struct CssVariantAttrs { pub comma: bool, pub dimension: bool, pub keyword: Option<String>, - pub aliases: Option<String>, pub skip: bool, } diff --git a/components/style_traits/lib.rs b/components/style_traits/lib.rs index f232b157cf9..b1eaf4e086f 100644 --- a/components/style_traits/lib.rs +++ b/components/style_traits/lib.rs @@ -177,7 +177,11 @@ pub enum ValueParseErrorKind<'i> { impl<'i> StyleParseErrorKind<'i> { /// Create an InvalidValue parse error - pub fn new_invalid(name: CowRcStr<'i>, value_error: ParseError<'i>) -> ParseError<'i> { + pub fn new_invalid<S>(name: S, value_error: ParseError<'i>) -> ParseError<'i> + where + S: Into<CowRcStr<'i>>, + { + let name = name.into(); let variant = match value_error.kind { cssparser::ParseErrorKind::Custom(StyleParseErrorKind::ValueError(e)) => { match e { diff --git a/components/style_traits/specified_value_info.rs b/components/style_traits/specified_value_info.rs index d9f4578b1a8..e0dd5544d0a 100644 --- a/components/style_traits/specified_value_info.rs +++ b/components/style_traits/specified_value_info.rs @@ -43,7 +43,7 @@ pub type KeywordsCollectFn<'a> = &'a mut FnMut(&[&'static str]); /// name is listed in `collect_completion_keywords`. /// * If `#[css(skip)]` is found, the content inside the variant or /// field is ignored. -/// * Values listed in `#[css(if_empty)]`, `#[css(aliases)]`, and +/// * Values listed in `#[css(if_empty)]`, `#[parse(aliases)]`, and /// `#[css(keyword)]` are added into `collect_completion_keywords`. /// /// In addition to `css` attributes, it also has `value_info` helper diff --git a/servo-tidy.toml b/servo-tidy.toml index 90d316726a4..c3539b5b141 100644 --- a/servo-tidy.toml +++ b/servo-tidy.toml @@ -39,13 +39,9 @@ num = [ # Ignored packages with duplicated versions packages = [ "bitflags", - "itoa", "log", "num-traits", - "quote", "rand", - "syn", - "unicode-xid", "winapi", ] # Files that are ignored for all tidy and lint checks. diff --git a/tests/wpt/metadata/css/css-transitions/currentcolor-animation-001.html.ini b/tests/wpt/metadata/css/css-transitions/currentcolor-animation-001.html.ini deleted file mode 100644 index c8a94fa2ed1..00000000000 --- a/tests/wpt/metadata/css/css-transitions/currentcolor-animation-001.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[currentcolor-animation-001.html] - type: testharness - [currentcolortransition] - expected: FAIL - |