diff options
161 files changed, 7193 insertions, 4188 deletions
diff --git a/Cargo.lock b/Cargo.lock index 3610f0cf2cb..bb087bfd3a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -196,6 +196,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bitflags" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -299,7 +304,7 @@ version = "0.0.1" dependencies = [ "azure 0.15.0 (git+https://github.com/servo/rust-azure)", "canvas_traits 0.0.1", - "cssparser 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -314,7 +319,7 @@ dependencies = [ name = "canvas_traits" version = "0.0.1" dependencies = [ - "cssparser 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -528,7 +533,7 @@ dependencies = [ [[package]] name = "cssparser" -version = "0.13.3" +version = "0.13.5" 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)", @@ -959,7 +964,7 @@ name = "geckoservo" version = "0.0.1" dependencies = [ "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cssparser 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1024,7 +1029,7 @@ dependencies = [ name = "gfx_tests" version = "0.0.1" dependencies = [ - "cssparser 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", "gfx 0.0.1", "ipc-channel 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "style 0.0.1", @@ -1366,7 +1371,7 @@ dependencies = [ "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "canvas_traits 0.0.1", - "cssparser 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "gfx 0.0.1", @@ -1701,7 +1706,7 @@ name = "msg" version = "0.0.1" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cssparser 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2143,6 +2148,15 @@ dependencies = [ ] [[package]] +name = "pulldown-cmark" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "quasi" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2300,7 +2314,7 @@ dependencies = [ "caseless 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "cookie 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cssparser 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.13.5 (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", @@ -2351,6 +2365,7 @@ dependencies = [ "smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "style 0.0.1", "style_traits 0.0.1", + "swapper 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "tinyfiledialogs 2.5.9 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2369,7 +2384,7 @@ dependencies = [ "app_units 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "canvas_traits 0.0.1", - "cssparser 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "gfx_traits 0.0.1", "heapsize 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2407,7 +2422,6 @@ dependencies = [ name = "script_traits" version = "0.0.1" dependencies = [ - "app_units 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "bluetooth_traits 0.0.1", "canvas_traits 0.0.1", "cookie 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2440,9 +2454,11 @@ name = "selectors" version = "0.18.0" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cssparser 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", "precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "size_of_test 0.0.1", "smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2739,6 +2755,15 @@ name = "size_of_test" version = "0.0.1" [[package]] +name = "skeptic" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pulldown-cmark 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "slab" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2798,13 +2823,14 @@ dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cssparser 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "html5ever 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2828,6 +2854,7 @@ dependencies = [ "smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "style_traits 0.0.1", "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2838,7 +2865,7 @@ version = "0.0.1" dependencies = [ "app_units 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cssparser 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "html5ever 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2857,7 +2884,7 @@ name = "style_traits" version = "0.0.1" dependencies = [ "app_units 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cssparser 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2870,19 +2897,26 @@ name = "stylo_tests" version = "0.0.1" dependencies = [ "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cssparser 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cssparser 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "geckoservo 0.0.1", "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "selectors 0.18.0", "style 0.0.1", "style_traits 0.0.1", ] [[package]] +name = "swapper" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "skeptic 0.9.0 (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" @@ -2969,6 +3003,14 @@ dependencies = [ ] [[package]] +name = "tempdir" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "tendril" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3395,6 +3437,7 @@ dependencies = [ "checksum bindgen 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ccaf8958532d7e570e905266ee2dc1094c3e5c3c3cfc2c299368747a30a5e654" "checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c" "checksum bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5b97c2c8e8bbb4251754f559df8af22fb264853c7d009084a576cdf12565089d" +"checksum bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4f67931368edf3a9a51d29886d245f1c3db2f1ef0dcc9e35ff70341b78c10d23" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4" "checksum bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80b13e2ab064ff3aa0bdbf1eff533f9822dc37899821f5f98c67f263eab51707" @@ -3422,7 +3465,7 @@ dependencies = [ "checksum core-foundation-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "41115a6aa5d3e1e5ef98148373f25971d1fad53818553f216495f9e67e90a624" "checksum core-graphics 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ead017dcf77f503dc991f6b52de6084eeea60a94b0a652baa9bf88654a28e83f" "checksum core-text 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0e9719616a10f717628e074744f8c55df7b450f7a34d29c196d14f4498aad05d" -"checksum cssparser 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9d2214de0e040001626d6a36020538d4b35a07cb260fcad0cf64f61fd1857e0e" +"checksum cssparser 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)" = "88dc7bd2a41b9c6c66456ac709d9efead1deb390d2c252c59e0ddfff9cdf0c94" "checksum cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "079adec4af52bb5275eadd004292028c79eb3c5f5b4ee8086a36d4197032f6df" "checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" "checksum dbus 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "47f881971824401c27bc1ff9f641d54ac66e0f409631806fa7be8cad8e6be450" @@ -3550,6 +3593,7 @@ dependencies = [ "checksum png 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3cb773e9a557edb568ce9935cf783e3cdcabe06a9449d41b3e5506d88e582c82" "checksum precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf1fc3616b3ef726a847f2cd2388c646ef6a1f1ba4835c2629004da48184150" "checksum procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f566249236c6ca4340f7ca78968271f0ed2b0f234007a61b66f9ecd0af09260" +"checksum pulldown-cmark 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1058d7bb927ca067656537eec4e02c2b4b70eaaa129664c5b90c111e20326f41" "checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3" "checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" @@ -3586,12 +3630,14 @@ dependencies = [ "checksum signpost 0.1.0 (git+https://github.com/pcwalton/signpost.git)" = "<none>" "checksum simd 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a94d14a2ae1f1f110937de5fb69e494372560181c7e1739a097fcc2cee37ba0" "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" +"checksum skeptic 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd7d8dc1315094150052d0ab767840376335a98ac66ef313ff911cdf439a5b69" "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" "checksum smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4f8266519bc1d17d0b5b16f6c21295625d562841c708f6376f49028a43e9c11e" "checksum string_cache 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f55fba06c5e294108f22e8512eb598cb13388a117991e411a8df8f41a1219a75" "checksum string_cache_codegen 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "479cde50c3539481f33906a387f2bd17c8e87cb848c35b6021d41fb81ff9b4d7" "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.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca610b32bb8bfc5e7f705480c3a1edfeb70b6582495d343872c8bee0dcf758c" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synstructure 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ccc9780bf1aa601943988c2876ab22413c01ad1739689aa6af18d0aa0b3f38b" @@ -3600,6 +3646,7 @@ dependencies = [ "checksum syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13ad4762fe52abc9f4008e85c4fb1b1fe3aa91ccb99ff4826a439c7c598e1047" "checksum syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6e0e4dbae163dd98989464c23dd503161b338790640e11537686f2ef0f25c791" "checksum target_build_utils 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f42dc058080c19c6a58bdd1bf962904ee4f5ef1fe2a81b529f31dacc750c679f" +"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" "checksum tendril 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4ce04c250d202db8004921e3d3bc95eaa4f2126c6937a428ae39d12d0e38df62" "checksum term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d168af3930b369cfe245132550579d47dfd873d69470755a19c2c6568dbbd989" "checksum term_size 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "07b6c1ac5b3fffd75073276bca1ceed01f67a28537097a2a9539e116e50fb21a" diff --git a/components/config/opts.rs b/components/config/opts.rs index 9130fb4cb5d..1b0938a5e9c 100644 --- a/components/config/opts.rs +++ b/components/config/opts.rs @@ -485,7 +485,7 @@ pub fn default_opts() -> Opts { replace_surrogates: false, gc_profile: false, load_webfonts_synchronously: false, - headless: true, + headless: false, hard_fail: true, bubble_inline_sizes_separately: false, show_debug_fragment_borders: false, diff --git a/components/constellation/frame.rs b/components/constellation/browsingcontext.rs index ac85c14d214..5f6f29aecf6 100644 --- a/components/constellation/frame.rs +++ b/components/constellation/browsingcontext.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use euclid::size::TypedSize2D; -use msg::constellation_msg::{FrameId, PipelineId}; +use msg::constellation_msg::{BrowsingContextId, PipelineId}; use pipeline::Pipeline; use script_traits::LoadData; use std::collections::HashMap; @@ -12,17 +12,16 @@ use std::mem::replace; use std::time::Instant; use style_traits::CSSPixel; -/// A frame in the frame tree. -/// Each frame is the constellation's view of a browsing context. +/// The constellation's view of a browsing context. /// Each browsing context has a session history, caused by -/// navigation and traversing the history. Each frame has its +/// navigation and traversing the history. Each browsing contest has its /// current entry, plus past and future entries. The past is sorted /// chronologically, the future is sorted reverse chronologically: /// in particular prev.pop() is the latest past entry, and /// next.pop() is the earliest future entry. -pub struct Frame { - /// The frame id. - pub id: FrameId, +pub struct BrowsingContext { + /// The browsing context id. + pub id: BrowsingContextId, /// The size of the frame. pub size: Option<TypedSize2D<f32, CSSPixel>>, @@ -37,17 +36,17 @@ pub struct Frame { pub load_data: LoadData, /// The past session history, ordered chronologically. - pub prev: Vec<FrameState>, + pub prev: Vec<SessionHistoryEntry>, /// The future session history, ordered reverse chronologically. - pub next: Vec<FrameState>, + pub next: Vec<SessionHistoryEntry>, } -impl Frame { - /// Create a new frame. - /// Note this just creates the frame, it doesn't add it to the frame tree. - pub fn new(id: FrameId, pipeline_id: PipelineId, load_data: LoadData) -> Frame { - Frame { +impl BrowsingContext { + /// Create a new browsing context. + /// Note this just creates the browsing context, it doesn't add it to the constellation's set of browsing contexts. + pub fn new(id: BrowsingContextId, pipeline_id: PipelineId, load_data: LoadData) -> BrowsingContext { + BrowsingContext { id: id, size: None, pipeline_id: pipeline_id, @@ -58,17 +57,17 @@ impl Frame { } } - /// Get the current frame state. - pub fn current(&self) -> FrameState { - FrameState { + /// Get the current session history entry. + pub fn current(&self) -> SessionHistoryEntry { + SessionHistoryEntry { instant: self.instant, - frame_id: self.id, + browsing_context_id: self.id, pipeline_id: Some(self.pipeline_id), load_data: self.load_data.clone(), } } - /// Set the current frame entry, and push the current frame entry into the past. + /// Set the current session history entry, and push the current frame entry into the past. pub fn load(&mut self, pipeline_id: PipelineId, load_data: LoadData) { let current = self.current(); self.prev.push(current); @@ -78,25 +77,27 @@ impl Frame { } /// Set the future to be empty. - pub fn remove_forward_entries(&mut self) -> Vec<FrameState> { + pub fn remove_forward_entries(&mut self) -> Vec<SessionHistoryEntry> { replace(&mut self.next, vec!()) } - /// Update the current entry of the Frame from an entry that has been traversed to. - pub fn update_current(&mut self, pipeline_id: PipelineId, entry: FrameState) { + /// Update the current entry of the BrowsingContext from an entry that has been traversed to. + pub fn update_current(&mut self, pipeline_id: PipelineId, entry: SessionHistoryEntry) { self.pipeline_id = pipeline_id; self.instant = entry.instant; self.load_data = entry.load_data; } } -/// An entry in a frame's session history. +/// An entry in a browsing context's session history. /// Each entry stores the pipeline id for a document in the session history. /// /// When we operate on the joint session history, entries are sorted chronologically, /// so we timestamp the entries by when the entry was added to the session history. +/// +/// https://html.spec.whatwg.org/multipage/#session-history-entry #[derive(Clone)] -pub struct FrameState { +pub struct SessionHistoryEntry { /// The timestamp for when the session history entry was created pub instant: Instant, @@ -108,14 +109,14 @@ pub struct FrameState { pub load_data: LoadData, /// The frame that this session history entry is part of - pub frame_id: FrameId, + pub browsing_context_id: BrowsingContextId, } -/// Represents a pending change in the frame tree, that will be applied +/// Represents a pending change in a session history, that will be applied /// once the new pipeline has loaded and completed initial layout / paint. -pub struct FrameChange { - /// The frame to change. - pub frame_id: FrameId, +pub struct SessionHistoryChange { + /// The browsing context to change. + pub browsing_context_id: BrowsingContextId, /// The pipeline for the document being loaded. pub new_pipeline_id: PipelineId, @@ -129,16 +130,15 @@ pub struct FrameChange { pub replace_instant: Option<Instant>, } -/// An iterator over a frame tree, returning the fully active frames in -/// depth-first order. Note that this iterator only returns the fully -/// active frames, that is ones where every ancestor frame is -/// in the currently active pipeline of its parent frame. -pub struct FrameTreeIterator<'a> { - /// The frames still to iterate over. - pub stack: Vec<FrameId>, +/// An iterator over browsing contexts, returning the descendant +/// contexts whose active documents are fully active, in depth-first +/// order. +pub struct FullyActiveBrowsingContextsIterator<'a> { + /// The browsing contexts still to iterate over. + pub stack: Vec<BrowsingContextId>, - /// The set of all frames. - pub frames: &'a HashMap<FrameId, Frame>, + /// The set of all browsing contexts. + pub browsing_contexts: &'a HashMap<BrowsingContextId, BrowsingContext>, /// The set of all pipelines. We use this to find the active /// children of a frame, which are the iframes in the currently @@ -146,73 +146,73 @@ pub struct FrameTreeIterator<'a> { pub pipelines: &'a HashMap<PipelineId, Pipeline>, } -impl<'a> Iterator for FrameTreeIterator<'a> { - type Item = &'a Frame; - fn next(&mut self) -> Option<&'a Frame> { +impl<'a> Iterator for FullyActiveBrowsingContextsIterator<'a> { + type Item = &'a BrowsingContext; + fn next(&mut self) -> Option<&'a BrowsingContext> { loop { - let frame_id = match self.stack.pop() { - Some(frame_id) => frame_id, + let browsing_context_id = match self.stack.pop() { + Some(browsing_context_id) => browsing_context_id, None => return None, }; - let frame = match self.frames.get(&frame_id) { - Some(frame) => frame, + let browsing_context = match self.browsing_contexts.get(&browsing_context_id) { + Some(browsing_context) => browsing_context, None => { - warn!("Frame {:?} iterated after closure.", frame_id); + warn!("BrowsingContext {:?} iterated after closure.", browsing_context_id); continue; }, }; - let pipeline = match self.pipelines.get(&frame.pipeline_id) { + let pipeline = match self.pipelines.get(&browsing_context.pipeline_id) { Some(pipeline) => pipeline, None => { - warn!("Pipeline {:?} iterated after closure.", frame.pipeline_id); + warn!("Pipeline {:?} iterated after closure.", browsing_context.pipeline_id); continue; }, }; self.stack.extend(pipeline.children.iter()); - return Some(frame) + return Some(browsing_context) } } } -/// An iterator over a frame tree, returning all frames in depth-first -/// order. Note that this iterator returns all frames, not just the -/// fully active ones. -pub struct FullFrameTreeIterator<'a> { - /// The frames still to iterate over. - pub stack: Vec<FrameId>, +/// An iterator over browsing contexts, returning all descendant +/// contexts in depth-first order. Note that this iterator returns all +/// contexts, not just the fully active ones. +pub struct AllBrowsingContextsIterator<'a> { + /// The browsing contexts still to iterate over. + pub stack: Vec<BrowsingContextId>, - /// The set of all frames. - pub frames: &'a HashMap<FrameId, Frame>, + /// The set of all browsing contexts. + pub browsing_contexts: &'a HashMap<BrowsingContextId, BrowsingContext>, /// The set of all pipelines. We use this to find the - /// children of a frame, which are the iframes in all documents + /// children of a browsing context, which are the iframes in all documents /// in the session history. pub pipelines: &'a HashMap<PipelineId, Pipeline>, } -impl<'a> Iterator for FullFrameTreeIterator<'a> { - type Item = &'a Frame; - fn next(&mut self) -> Option<&'a Frame> { +impl<'a> Iterator for AllBrowsingContextsIterator<'a> { + type Item = &'a BrowsingContext; + fn next(&mut self) -> Option<&'a BrowsingContext> { let pipelines = self.pipelines; loop { - let frame_id = match self.stack.pop() { - Some(frame_id) => frame_id, + let browsing_context_id = match self.stack.pop() { + Some(browsing_context_id) => browsing_context_id, None => return None, }; - let frame = match self.frames.get(&frame_id) { - Some(frame) => frame, + let browsing_context = match self.browsing_contexts.get(&browsing_context_id) { + Some(browsing_context) => browsing_context, None => { - warn!("Frame {:?} iterated after closure.", frame_id); + warn!("BrowsingContext {:?} iterated after closure.", browsing_context_id); continue; }, }; - let child_frame_ids = frame.prev.iter().chain(frame.next.iter()) + let child_browsing_context_ids = browsing_context.prev.iter().chain(browsing_context.next.iter()) .filter_map(|entry| entry.pipeline_id) - .chain(once(frame.pipeline_id)) + .chain(once(browsing_context.pipeline_id)) .filter_map(|pipeline_id| pipelines.get(&pipeline_id)) .flat_map(|pipeline| pipeline.children.iter()); - self.stack.extend(child_frame_ids); - return Some(frame) + self.stack.extend(child_browsing_context_ids); + return Some(browsing_context) } } } diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 01a59070730..c16ff8c2049 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -16,32 +16,34 @@ //! layout threads. Pipelines may share script threads, but not //! layout threads. //! -//! * The set of all `Frame` objects. Each frame gives the constellation's -//! view of a browsing context. Each browsing context stores an independent -//! session history, created by navigation of that frame. The session +//! * The set of all `BrowsingContext` objects. Each browsing context +//! gives the constellation's view of a `WindowProxy`. +//! Each browsing context stores an independent +//! session history, created by navigation. The session //! history can be traversed, for example by the back and forwards UI, //! so each session history maintains a list of past and future pipelines, //! as well as the current active pipeline. //! -//! There are two kinds of frames: top-level frames (for example tabs -//! in a browser UI), and nested frames (typically caused by `iframe` -//! elements). Frames have a hierarchy (typically caused by `iframe`s -//! containing `iframe`s), giving rise to a frame tree with a root frame. -//! The logical relationship between these types is: +//! There are two kinds of browsing context: top-level ones (for +//! example tabs in a browser UI), and nested ones (typically caused +//! by `iframe` elements). Browsing contexts have a hierarchy +//! (typically caused by `iframe`s containing `iframe`s), giving rise +//! to a tree with a root top-level browsing context. The logical +//! relationship between these types is: //! //! ``` -//! +---------+ +------------+ +-------------+ -//! | Frame | --parent?--> | Pipeline | --event_loop--> | EventLoop | -//! | | --current--> | | | | -//! | | --prev*----> | | <---pipeline*-- | | -//! | | --next*----> | | +-------------+ -//! | | | | -//! | | <----frame-- | | -//! +---------+ +------------+ +//! +------------+ +------------+ +---------+ +//! | Browsing | ------parent?------> | Pipeline | --event_loop--> | Event | +//! | Context | ------current------> | | | Loop | +//! | | ------prev*--------> | | <---pipeline*-- | | +//! | | ------next*--------> | | +---------+ +//! | | | | +//! | | <-browsing_context-- | | +//! +------------+ +------------+ //! ``` // //! Complicating matters, there are also mozbrowser iframes, which are top-level -//! frames with a parent. +//! iframes with a parent. //! //! The constellation also maintains channels to threads, including: //! @@ -53,7 +55,7 @@ //! * The devtools, debugger and webdriver servers. //! //! The constellation passes messages between the threads, and updates its state -//! to track the evolving state of the frame tree. +//! to track the evolving state of the browsing context tree. //! //! The constellation acts as a logger, tracking any `warn!` messages from threads, //! and converting any `error!` or `panic!` into a crash report, which is filed @@ -64,6 +66,8 @@ use backtrace::Backtrace; use bluetooth_traits::BluetoothRequest; +use browsingcontext::{BrowsingContext, SessionHistoryChange, SessionHistoryEntry}; +use browsingcontext::{FullyActiveBrowsingContextsIterator, AllBrowsingContextsIterator}; use canvas::canvas_paint_thread::CanvasPaintThread; use canvas::webgl_paint_thread::WebGLPaintThread; use canvas_traits::CanvasMsg; @@ -75,7 +79,6 @@ use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg}; use euclid::scale_factor::ScaleFactor; use euclid::size::{Size2D, TypedSize2D}; use event_loop::EventLoop; -use frame::{Frame, FrameChange, FrameState, FrameTreeIterator, FullFrameTreeIterator}; use gfx::font_cache_thread::FontCacheThread; use gfx_traits::Epoch; use ipc_channel::{Error as IpcError}; @@ -84,7 +87,7 @@ use ipc_channel::router::ROUTER; use itertools::Itertools; use layout_traits::LayoutThreadFactory; use log::{Log, LogLevel, LogLevelFilter, LogMetadata, LogRecord}; -use msg::constellation_msg::{FrameId, FrameType, PipelineId}; +use msg::constellation_msg::{BrowsingContextId, FrameType, PipelineId}; use msg::constellation_msg::{Key, KeyModifiers, KeyState}; use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId, TraversalDirection}; use net_traits::{self, IpcSend, ResourceThreads}; @@ -222,32 +225,33 @@ pub struct Constellation<Message, LTF, STF> { /// The set of all event loops in the browser. We generate a new /// event loop for each registered domain name (aka eTLD+1) in - /// each top-level frame. We store the event loops in a map - /// indexed by top-level frame id (as a `FrameId`) and registered + /// each top-level browsing context. We store the event loops in a map + /// indexed by top-level browsing context id + /// (as a `BrowsingContextId`) and registered /// domain name (as a `Host`) to event loops. This double /// indirection ensures that separate tabs do not share event /// loops, even if the same domain is loaded in each. /// It is important that scripts with the same eTLD+1 /// share an event loop, since they can use `document.domain` /// to become same-origin, at which point they can share DOM objects. - event_loops: HashMap<FrameId, HashMap<Host, Weak<EventLoop>>>, + event_loops: HashMap<BrowsingContextId, HashMap<Host, Weak<EventLoop>>>, /// The set of all the pipelines in the browser. /// (See the `pipeline` module for more details.) pipelines: HashMap<PipelineId, Pipeline>, - /// The set of all the frames in the browser. - frames: HashMap<FrameId, Frame>, + /// The set of all the browsing contexts in the browser. + browsing_contexts: HashMap<BrowsingContextId, BrowsingContext>, /// When a navigation is performed, we do not immediately update - /// the frame tree, instead we ask the event loop to begin loading - /// the new document, and do not update the frame tree until the + /// the session history, instead we ask the event loop to begin loading + /// the new document, and do not update the browsing context until the /// document is active. Between starting the load and it activating, - /// we store a `FrameChange` object for the navigation in progress. - pending_frames: Vec<FrameChange>, + /// we store a `SessionHistoryChange` object for the navigation in progress. + pending_changes: Vec<SessionHistoryChange>, - /// The root frame. - root_frame_id: FrameId, + /// The root browsing context. + root_browsing_context_id: BrowsingContextId, /// The currently focused pipeline for key events. focus_pipeline_id: Option<PipelineId>, @@ -339,8 +343,8 @@ impl WebDriverData { /// This enum gives the possible states of preparing such an image. #[derive(Debug, PartialEq)] enum ReadyToSave { - NoRootFrame, - PendingFrames, + NoRootBrowsingContext, + PendingChanges, WebFontNotLoaded, DocumentLoading, EpochMismatch, @@ -391,9 +395,9 @@ impl Log for FromScriptLogger { fn log(&self, record: &LogRecord) { if let Some(entry) = log_entry(record) { debug!("Sending log entry {:?}.", entry); - let top_level_frame_id = FrameId::installed(); + let top_level_id = BrowsingContextId::installed(); let thread_name = thread::current().name().map(ToOwned::to_owned); - let msg = FromScriptMsg::LogEntry(top_level_frame_id, thread_name, entry); + let msg = FromScriptMsg::LogEntry(top_level_id, thread_name, entry); let chan = self.constellation_chan.lock().unwrap_or_else(|err| err.into_inner()); let _ = chan.send(msg); } @@ -429,9 +433,9 @@ impl Log for FromCompositorLogger { fn log(&self, record: &LogRecord) { if let Some(entry) = log_entry(record) { debug!("Sending log entry {:?}.", entry); - let top_level_frame_id = FrameId::installed(); + let top_level_id = BrowsingContextId::installed(); let thread_name = thread::current().name().map(ToOwned::to_owned); - let msg = FromCompositorMsg::LogEntry(top_level_frame_id, thread_name, entry); + let msg = FromCompositorMsg::LogEntry(top_level_id, thread_name, entry); let chan = self.constellation_chan.lock().unwrap_or_else(|err| err.into_inner()); let _ = chan.send(msg); } @@ -516,11 +520,11 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> swmanager_sender: sw_mgr_clone, event_loops: HashMap::new(), pipelines: HashMap::new(), - frames: HashMap::new(), - pending_frames: vec!(), + browsing_contexts: HashMap::new(), + pending_changes: vec!(), // We initialize the namespace at 1, since we reserved namespace 0 for the constellation next_pipeline_namespace_id: PipelineNamespaceId(1), - root_frame_id: FrameId::new(), + root_browsing_context_id: BrowsingContextId::new(), focus_pipeline_id: None, time_profiler_chan: state.time_profiler_chan, mem_profiler_chan: state.mem_profiler_chan, @@ -575,7 +579,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> /// Helper function for creating a pipeline fn new_pipeline(&mut self, pipeline_id: PipelineId, - frame_id: FrameId, + browsing_context_id: BrowsingContextId, parent_info: Option<(PipelineId, FrameType)>, initial_window_size: Option<TypedSize2D<f32, CSSPixel>>, // TODO: we have to provide ownership of the LoadData @@ -589,13 +593,13 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> // TODO: can we get a case where the child pipeline is created // before the parent is part of the frame tree? - let top_level_frame_id = match parent_info { - Some((_, FrameType::MozBrowserIFrame)) => frame_id, - Some((parent_id, _)) => self.get_top_level_frame_for_pipeline(parent_id), - None => self.root_frame_id, + let top_level_id = match parent_info { + Some((_, FrameType::MozBrowserIFrame)) => browsing_context_id, + Some((parent_id, _)) => self.get_top_level_browsing_context_for_pipeline(parent_id), + None => self.root_browsing_context_id, }; - debug!("Creating new pipeline {} in top-level frame {}.", pipeline_id, top_level_frame_id); + debug!("Creating new pipeline {} in top-level browsing context {}.", pipeline_id, top_level_id); let (event_loop, host) = match sandbox { IFrameSandboxState::IFrameSandboxed => (None, None), @@ -606,7 +610,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> match reg_host(&load_data.url) { None => (None, None), Some(host) => { - let event_loop = self.event_loops.get(&top_level_frame_id) + let event_loop = self.event_loops.get(&top_level_id) .and_then(|map| map.get(&host)) .and_then(|weak| weak.upgrade()); match event_loop { @@ -637,23 +641,23 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> .and_then(|(parent_pipeline_id, _)| self.pipelines.get(&parent_pipeline_id)) .map(|pipeline| pipeline.visible); - let prev_visibility = self.frames.get(&frame_id) - .and_then(|frame| self.pipelines.get(&frame.pipeline_id)) + let prev_visibility = self.browsing_contexts.get(&browsing_context_id) + .and_then(|browsing_context| self.pipelines.get(&browsing_context.pipeline_id)) .map(|pipeline| pipeline.visible) .or(parent_visibility); // TODO: think about the case where the child pipeline is created // before the parent is part of the frame tree. - let top_level_frame_id = match parent_info { - Some((_, FrameType::MozBrowserIFrame)) => frame_id, - Some((parent_id, _)) => self.get_top_level_frame_for_pipeline(parent_id), - None => self.root_frame_id, + let top_level_browsing_context_id = match parent_info { + Some((_, FrameType::MozBrowserIFrame)) => browsing_context_id, + Some((parent_id, _)) => self.get_top_level_browsing_context_for_pipeline(parent_id), + None => self.root_browsing_context_id, }; let result = Pipeline::spawn::<Message, LTF, STF>(InitialPipelineState { id: pipeline_id, - frame_id: frame_id, - top_level_frame_id: top_level_frame_id, + browsing_context_id: browsing_context_id, + top_level_browsing_context_id: top_level_browsing_context_id, parent_info: parent_info, constellation_chan: self.script_sender.clone(), layout_to_constellation_chan: self.layout_sender.clone(), @@ -683,8 +687,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> }; if let Some(host) = host { - debug!("Adding new host entry {} for top-level frame {}.", host, top_level_frame_id); - self.event_loops.entry(top_level_frame_id) + debug!("Adding new host entry {} for top-level browsing context {}.", host, top_level_browsing_context_id); + self.event_loops.entry(top_level_browsing_context_id) .or_insert_with(HashMap::new) .insert(host, Rc::downgrade(&pipeline.event_loop)); } @@ -693,75 +697,84 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> self.pipelines.insert(pipeline_id, pipeline); } - /// Get an iterator for the current frame tree. Specify self.root_frame_id to - /// iterate the entire tree, or a specific frame id to iterate only that sub-tree. - /// Iterates over the fully active frames in the tree. - fn current_frame_tree_iter(&self, frame_id_root: FrameId) -> FrameTreeIterator { - FrameTreeIterator { - stack: vec!(frame_id_root), + /// Get an iterator for browsing contexts. Specify self.root_browsing context_id to + /// iterate the entire tree, or a specific browsing context id to iterate only that sub-tree. + /// Iterates over the fully active browsing contexts in the tree. + fn fully_active_browsing_contexts_iter(&self, browsing_context_id: BrowsingContextId) + -> FullyActiveBrowsingContextsIterator + { + FullyActiveBrowsingContextsIterator { + stack: vec!(browsing_context_id), pipelines: &self.pipelines, - frames: &self.frames, + browsing_contexts: &self.browsing_contexts, } } - /// Get an iterator for the current frame tree. Specify self.root_frame_id to - /// iterate the entire tree, or a specific frame id to iterate only that sub-tree. - /// Iterates over all frames in the tree. - fn full_frame_tree_iter(&self, frame_id_root: FrameId) -> FullFrameTreeIterator { - FullFrameTreeIterator { - stack: vec!(frame_id_root), + /// Get an iterator for browsing contexts. Specify self.root_browsing_context_id to + /// iterate the entire tree, or a specific browsing context id to iterate only that sub-tree. + /// Iterates over all browsing contexts in the tree. + fn all_browsing_contexts_iter(&self, browsing_context_id: BrowsingContextId) + -> AllBrowsingContextsIterator + { + AllBrowsingContextsIterator { + stack: vec!(browsing_context_id), pipelines: &self.pipelines, - frames: &self.frames, + browsing_contexts: &self.browsing_contexts, } } /// The joint session future is the merge of the session future of every - /// frame in the frame tree, sorted chronologically. - fn joint_session_future<'a>(&'a self, frame_id_root: FrameId) -> impl Iterator<Item = &'a FrameState> + 'a { - self.full_frame_tree_iter(frame_id_root) - .map(|frame| frame.next.iter().rev()) + /// browsing_context, sorted chronologically. + fn joint_session_future<'a>(&'a self, browsing_context_id: BrowsingContextId) + -> impl Iterator<Item = &'a SessionHistoryEntry> + 'a + { + self.all_browsing_contexts_iter(browsing_context_id) + .map(|browsing_context| browsing_context.next.iter().rev()) .kmerge_by(|a, b| a.instant.cmp(&b.instant) == Ordering::Less) } /// Is the joint session future empty? - fn joint_session_future_is_empty(&self, frame_id_root: FrameId) -> bool { - self.full_frame_tree_iter(frame_id_root) - .all(|frame| frame.next.is_empty()) + fn joint_session_future_is_empty(&self, browsing_context_id: BrowsingContextId) -> bool { + self.all_browsing_contexts_iter(browsing_context_id) + .all(|browsing_context| browsing_context.next.is_empty()) } /// The joint session past is the merge of the session past of every - /// frame in the frame tree, sorted reverse chronologically. - fn joint_session_past<'a>(&'a self, frame_id_root: FrameId) -> impl Iterator<Item = &'a FrameState> + 'a { - self.full_frame_tree_iter(frame_id_root) - .map(|frame| frame.prev.iter().rev().scan(frame.instant, |prev_instant, entry| { - let instant = *prev_instant; - *prev_instant = entry.instant; - Some((instant, entry)) - })) + /// browsing_context, sorted reverse chronologically. + fn joint_session_past<'a>(&'a self, browsing_context_id: BrowsingContextId) + -> impl Iterator<Item = &'a SessionHistoryEntry> + 'a + { + self.all_browsing_contexts_iter(browsing_context_id) + .map(|browsing_context| browsing_context.prev.iter().rev() + .scan(browsing_context.instant, |prev_instant, entry| { + let instant = *prev_instant; + *prev_instant = entry.instant; + Some((instant, entry)) + })) .kmerge_by(|a, b| a.0.cmp(&b.0) == Ordering::Greater) .map(|(_, entry)| entry) } /// Is the joint session past empty? - fn joint_session_past_is_empty(&self, frame_id_root: FrameId) -> bool { - self.full_frame_tree_iter(frame_id_root) - .all(|frame| frame.prev.is_empty()) + fn joint_session_past_is_empty(&self, browsing_context_id: BrowsingContextId) -> bool { + self.all_browsing_contexts_iter(browsing_context_id) + .all(|browsing_context| browsing_context.prev.is_empty()) } - /// Create a new frame and update the internal bookkeeping. - fn new_frame(&mut self, - frame_id: FrameId, + /// Create a new browsing context and update the internal bookkeeping. + fn new_browsing_context(&mut self, + browsing_context_id: BrowsingContextId, pipeline_id: PipelineId, load_data: LoadData) { - let frame = Frame::new(frame_id, pipeline_id, load_data); - self.frames.insert(frame_id, frame); + let browsing_context = BrowsingContext::new(browsing_context_id, pipeline_id, load_data); + self.browsing_contexts.insert(browsing_context_id, browsing_context); - // If a child frame, add it to the parent pipeline. + // If a child browsing_context, add it to the parent pipeline. let parent_info = self.pipelines.get(&pipeline_id) .and_then(|pipeline| pipeline.parent_info); if let Some((parent_id, _)) = parent_info { if let Some(parent) = self.pipelines.get_mut(&parent_id) { - parent.add_child(frame_id); + parent.add_child(browsing_context_id); } } } @@ -810,8 +823,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> // Treat deserialization error the same as receiving a panic message debug!("Deserialization failed ({:?}).", err); let reason = format!("Deserialization failed ({})", err); - let root_frame_id = self.root_frame_id; - return self.handle_panic(root_frame_id, reason, None); + let root_browsing_context_id = self.root_browsing_context_id; + return self.handle_panic(root_browsing_context_id, reason, None); } }; @@ -846,13 +859,13 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> debug!("constellation exiting"); self.handle_exit(); } - FromCompositorMsg::GetFrame(pipeline_id, resp_chan) => { - debug!("constellation got get root pipeline message"); - self.handle_get_frame(pipeline_id, resp_chan); + FromCompositorMsg::GetBrowsingContext(pipeline_id, resp_chan) => { + debug!("constellation got get browsing context message"); + self.handle_get_browsing_context(pipeline_id, resp_chan); } - FromCompositorMsg::GetPipeline(frame_id, resp_chan) => { + FromCompositorMsg::GetPipeline(browsing_context_id, resp_chan) => { debug!("constellation got get root pipeline message"); - self.handle_get_pipeline(frame_id, resp_chan); + self.handle_get_pipeline(browsing_context_id, resp_chan); } FromCompositorMsg::GetPipelineTitle(pipeline_id) => { debug!("constellation got get-pipeline-title message"); @@ -863,7 +876,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> self.handle_key_msg(ch, key, state, modifiers); } // Load a new page from a typed url - // If there is already a pending page (self.pending_frames), it will not be overridden; + // If there is already a pending page (self.pending_changes), it will not be overridden; // However, if the id is not encompassed by another change, it will be. FromCompositorMsg::LoadUrl(source_id, load_data) => { debug!("constellation got URL load message from compositor"); @@ -906,8 +919,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> debug!("constellation got reload message"); self.handle_reload_msg(); } - FromCompositorMsg::LogEntry(top_level_frame_id, thread_name, entry) => { - self.handle_log_entry(top_level_frame_id, thread_name, entry); + FromCompositorMsg::LogEntry(top_level_browsing_context_id, thread_name, entry) => { + self.handle_log_entry(top_level_browsing_context_id, thread_name, entry); } FromCompositorMsg::SetWebVRThread(webvr_thread) => { assert!(self.webvr_thread.is_none()); @@ -942,7 +955,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> self.handle_change_running_animations_state(pipeline_id, animation_state) } // Load a new page from a mouse click - // If there is already a pending page (self.pending_frames), it will not be overridden; + // If there is already a pending page (self.pending_changes), it will not be overridden; // However, if the id is not encompassed by another change, it will be. FromScriptMsg::LoadUrl(source_id, load_data, replace) => { debug!("constellation got URL load message from script"); @@ -978,9 +991,9 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> warn!("constellation got set final url message for dead pipeline"); } } - FromScriptMsg::PostMessage(frame_id, origin, data) => { + FromScriptMsg::PostMessage(browsing_context_id, origin, data) => { debug!("constellation got postMessage message"); - self.handle_post_message_msg(frame_id, origin, data); + self.handle_post_message_msg(browsing_context_id, origin, data); } FromScriptMsg::MozBrowserEvent(parent_pipeline_id, pipeline_id, event) => { debug!("constellation got mozbrowser event message"); @@ -1017,9 +1030,9 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> debug!("constellation got set visibility change complete message"); self.handle_visibility_change_complete(pipeline_id, visible); } - FromScriptMsg::RemoveIFrame(frame_id, sender) => { + FromScriptMsg::RemoveIFrame(browsing_context_id, sender) => { debug!("constellation got remove iframe message"); - let removed_pipeline_ids = self.handle_remove_iframe_msg(frame_id); + let removed_pipeline_ids = self.handle_remove_iframe_msg(browsing_context_id); if let Err(e) = sender.send(removed_pipeline_ids) { warn!("Error replying to remove iframe ({})", e); } @@ -1074,8 +1087,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> FromScriptMsg::Exit => { self.compositor_proxy.send(ToCompositorMsg::Exit); } - FromScriptMsg::LogEntry(top_level_frame_id, thread_name, entry) => { - self.handle_log_entry(top_level_frame_id, thread_name, entry); + FromScriptMsg::LogEntry(top_level_browsing_context_id, thread_name, entry) => { + self.handle_log_entry(top_level_browsing_context_id, thread_name, entry); } FromScriptMsg::SetTitle(pipeline_id, title) => { @@ -1089,10 +1102,10 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> FromScriptMsg::TouchEventProcessed(result) => { self.compositor_proxy.send(ToCompositorMsg::TouchEventProcessed(result)) } - FromScriptMsg::GetFrameId(pipeline_id, sender) => { - let result = self.pipelines.get(&pipeline_id).map(|pipeline| pipeline.frame_id); + FromScriptMsg::GetBrowsingContextId(pipeline_id, sender) => { + let result = self.pipelines.get(&pipeline_id).map(|pipeline| pipeline.browsing_context_id); if let Err(e) = sender.send(result) { - warn!("Sending reply to get frame id failed ({:?}).", e); + warn!("Sending reply to get browsing context failed ({:?}).", e); } } FromScriptMsg::GetParentInfo(pipeline_id, sender) => { @@ -1128,9 +1141,9 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } // Layout sends new sizes for all subframes. This needs to be reflected by all // frame trees in the navigation context containing the subframe. - FromLayoutMsg::FrameSizes(iframe_sizes) => { - debug!("constellation got frame size message"); - self.handle_frame_size_msg(iframe_sizes); + FromLayoutMsg::IFrameSizes(iframe_sizes) => { + debug!("constellation got iframe size message"); + self.handle_iframe_size_msg(iframe_sizes); } FromLayoutMsg::SetCursor(cursor) => { self.handle_set_cursor_msg(cursor) @@ -1172,24 +1185,24 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> self.mem_profiler_chan.send(mem::ProfilerMsg::Exit); - // TODO: exit before the root frame is initialized? - debug!("Removing root frame."); - let root_frame_id = self.root_frame_id; - self.close_frame(root_frame_id, ExitPipelineMode::Normal); + // TODO: exit before the root browsing context is initialized? + debug!("Removing root browsing context."); + let root_browsing_context_id = self.root_browsing_context_id; + self.close_browsing_context(root_browsing_context_id, ExitPipelineMode::Normal); - // Close any pending frames and pipelines - while let Some(pending) = self.pending_frames.pop() { - debug!("Removing pending frame {}.", pending.frame_id); - self.close_frame(pending.frame_id, ExitPipelineMode::Normal); + // Close any pending changes and pipelines + while let Some(pending) = self.pending_changes.pop() { + debug!("Removing pending browsing context {}.", pending.browsing_context_id); + self.close_browsing_context(pending.browsing_context_id, ExitPipelineMode::Normal); debug!("Removing pending pipeline {}.", pending.new_pipeline_id); self.close_pipeline(pending.new_pipeline_id, DiscardBrowsingContext::Yes, ExitPipelineMode::Normal); } - // In case there are frames which weren't attached to the frame tree, we close them. - let frame_ids: Vec<FrameId> = self.frames.keys().cloned().collect(); - for frame_id in frame_ids { - debug!("Removing detached frame {}.", frame_id); - self.close_frame(frame_id, ExitPipelineMode::Normal); + // In case there are browsing contexts which weren't attached, we close them. + let browsing_context_ids: Vec<BrowsingContextId> = self.browsing_contexts.keys().cloned().collect(); + for browsing_context_id in browsing_context_ids { + debug!("Removing detached browsing context {}.", browsing_context_id); + self.close_browsing_context(browsing_context_id, ExitPipelineMode::Normal); } // In case there are pipelines which weren't attached to the pipeline tree, we close them. @@ -1276,12 +1289,15 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> fn handle_send_error(&mut self, pipeline_id: PipelineId, err: IpcError) { // Treat send error the same as receiving a panic message debug!("Pipeline {:?} send error ({}).", pipeline_id, err); - let top_level_frame_id = self.get_top_level_frame_for_pipeline(pipeline_id); + let top_level_browsing_context_id = self.get_top_level_browsing_context_for_pipeline(pipeline_id); let reason = format!("Send failed ({})", err); - self.handle_panic(top_level_frame_id, reason, None); + self.handle_panic(top_level_browsing_context_id, reason, None); } - fn handle_panic(&mut self, top_level_frame_id: FrameId, reason: String, backtrace: Option<String>) { + fn handle_panic(&mut self, top_level_browsing_context_id: BrowsingContextId, + reason: String, + backtrace: Option<String>) + { if opts::get().hard_fail { // It's quite difficult to make Servo exit cleanly if some threads have failed. // Hard fail exists for test runners so we crash and that's good enough. @@ -1289,15 +1305,15 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> process::exit(1); } - debug!("Panic handler for top-level frame {}: {}.", top_level_frame_id, reason); + debug!("Panic handler for top-level browsing context {}: {}.", top_level_browsing_context_id, reason); // Notify the browser chrome that the pipeline has failed - self.trigger_mozbrowsererror(top_level_frame_id, reason, backtrace); + self.trigger_mozbrowsererror(top_level_browsing_context_id, reason, backtrace); let (window_size, pipeline_id) = { - let frame = self.frames.get(&top_level_frame_id); - let window_size = frame.and_then(|frame| frame.size); - let pipeline_id = frame.map(|frame| frame.pipeline_id); + let browsing_context = self.browsing_contexts.get(&top_level_browsing_context_id); + let window_size = browsing_context.and_then(|browsing_context| browsing_context.size); + let pipeline_id = browsing_context.map(|browsing_context| browsing_context.pipeline_id); (window_size, pipeline_id) }; @@ -1308,7 +1324,9 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> (pipeline_url, parent_info) }; - self.close_frame_children(top_level_frame_id, DiscardBrowsingContext::No, ExitPipelineMode::Force); + self.close_browsing_context_children(top_level_browsing_context_id, + DiscardBrowsingContext::No, + ExitPipelineMode::Force); let failure_url = ServoUrl::parse("about:failure").expect("infallible"); @@ -1323,22 +1341,26 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> let new_pipeline_id = PipelineId::new(); let load_data = LoadData::new(failure_url, None, None, None); let sandbox = IFrameSandboxState::IFrameSandboxed; - self.new_pipeline(new_pipeline_id, top_level_frame_id, parent_info, + self.new_pipeline(new_pipeline_id, top_level_browsing_context_id, parent_info, window_size, load_data.clone(), sandbox, false); - self.pending_frames.push(FrameChange { - frame_id: top_level_frame_id, + self.pending_changes.push(SessionHistoryChange { + browsing_context_id: top_level_browsing_context_id, new_pipeline_id: new_pipeline_id, load_data: load_data, replace_instant: None, }); } - fn handle_log_entry(&mut self, top_level_frame_id: Option<FrameId>, thread_name: Option<String>, entry: LogEntry) { + fn handle_log_entry(&mut self, + top_level_browsing_context_id: Option<BrowsingContextId>, + thread_name: Option<String>, + entry: LogEntry) + { debug!("Received log entry {:?}.", entry); + let top_level_browsing_context_id = top_level_browsing_context_id.unwrap_or(self.root_browsing_context_id); match entry { LogEntry::Panic(reason, backtrace) => { - let top_level_frame_id = top_level_frame_id.unwrap_or(self.root_frame_id); - self.handle_panic(top_level_frame_id, reason, Some(backtrace)); + self.handle_panic(top_level_browsing_context_id, reason, Some(backtrace)); }, LogEntry::Error(reason) | LogEntry::Warn(reason) => { // VecDeque::truncate is unstable @@ -1365,47 +1387,53 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> fn handle_init_load(&mut self, url: ServoUrl) { let window_size = self.window_size.initial_viewport; let root_pipeline_id = PipelineId::new(); - let root_frame_id = self.root_frame_id; + let root_browsing_context_id = self.root_browsing_context_id; let load_data = LoadData::new(url.clone(), None, None, None); let sandbox = IFrameSandboxState::IFrameUnsandboxed; - self.new_pipeline(root_pipeline_id, root_frame_id, None, Some(window_size), load_data.clone(), sandbox, false); + self.new_pipeline(root_pipeline_id, + root_browsing_context_id, + None, + Some(window_size), + load_data.clone(), + sandbox, + false); self.handle_load_start_msg(root_pipeline_id); - self.pending_frames.push(FrameChange { - frame_id: self.root_frame_id, + self.pending_changes.push(SessionHistoryChange { + browsing_context_id: self.root_browsing_context_id, new_pipeline_id: root_pipeline_id, load_data: load_data, replace_instant: None, }); } - fn handle_frame_size_msg(&mut self, - iframe_sizes: Vec<(FrameId, TypedSize2D<f32, CSSPixel>)>) { - for (frame_id, size) in iframe_sizes { + fn handle_iframe_size_msg(&mut self, + iframe_sizes: Vec<(BrowsingContextId, TypedSize2D<f32, CSSPixel>)>) { + for (browsing_context_id, size) in iframe_sizes { let window_size = WindowSizeData { initial_viewport: size, device_pixel_ratio: self.window_size.device_pixel_ratio, }; - self.resize_frame(window_size, WindowSizeType::Initial, frame_id); + self.resize_frame(window_size, WindowSizeType::Initial, browsing_context_id); } } fn handle_subframe_loaded(&mut self, pipeline_id: PipelineId) { - let (frame_id, parent_id) = match self.pipelines.get(&pipeline_id) { + let (browsing_context_id, parent_id) = match self.pipelines.get(&pipeline_id) { Some(pipeline) => match pipeline.parent_info { - Some((parent_id, _)) => (pipeline.frame_id, parent_id), + Some((parent_id, _)) => (pipeline.browsing_context_id, parent_id), None => return warn!("Pipeline {} has no parent.", pipeline_id), }, None => return warn!("Pipeline {} loaded after closure.", pipeline_id), }; - let msg = ConstellationControlMsg::DispatchFrameLoadEvent { - target: frame_id, + let msg = ConstellationControlMsg::DispatchIFrameLoadEvent { + target: browsing_context_id, parent: parent_id, child: pipeline_id, }; let result = match self.pipelines.get(&parent_id) { Some(parent) => parent.event_loop.send(msg), - None => return warn!("Parent {} frame loaded after closure.", parent_id), + None => return warn!("Parent {} browsing context loaded after closure.", parent_id), }; if let Err(e) = result { self.handle_send_error(parent_id, e); @@ -1413,8 +1441,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } // The script thread associated with pipeline_id has loaded a URL in an iframe via script. This - // will result in a new pipeline being spawned and a frame tree being added to - // parent_pipeline_id's frame tree's children. This message is never the result of a + // will result in a new pipeline being spawned and a child being added to + // the parent pipeline. This message is never the result of a // page navigation. fn handle_script_loaded_url_in_iframe_msg(&mut self, load_info: IFrameLoadInfoWithData) { let (load_data, window_size, is_private) = { @@ -1439,27 +1467,29 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> let is_private = load_info.info.is_private || source_pipeline.is_private; - let window_size = self.frames.get(&load_info.info.frame_id).and_then(|frame| frame.size); + let window_size = self.browsing_contexts.get(&load_info.info.browsing_context_id) + .and_then(|browsing_context| browsing_context.size); (load_data, window_size, is_private) }; let replace_instant = if load_info.info.replace { - self.frames.get(&load_info.info.frame_id).map(|frame| frame.instant) + self.browsing_contexts.get(&load_info.info.browsing_context_id) + .map(|browsing_context| browsing_context.instant) } else { None }; - // Create the new pipeline, attached to the parent and push to pending frames - self.pending_frames.push(FrameChange { - frame_id: load_info.info.frame_id, + // Create the new pipeline, attached to the parent and push to pending changes + self.pending_changes.push(SessionHistoryChange { + browsing_context_id: load_info.info.browsing_context_id, new_pipeline_id: load_info.info.new_pipeline_id, load_data: load_data.clone(), replace_instant: replace_instant, }); self.new_pipeline(load_info.info.new_pipeline_id, - load_info.info.frame_id, + load_info.info.browsing_context_id, Some((load_info.info.parent_pipeline_id, load_info.info.frame_type)), window_size, load_data, @@ -1475,7 +1505,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> new_pipeline_id, frame_type, replace, - frame_id, + browsing_context_id, is_private, } = load_info; @@ -1490,7 +1520,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> let script_sender = parent_pipeline.event_loop.clone(); Pipeline::new(new_pipeline_id, - frame_id, + browsing_context_id, Some((parent_pipeline_id, frame_type)), script_sender, layout_sender, @@ -1504,7 +1534,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> let load_data = LoadData::new(url, Some(parent_pipeline_id), None, None); let replace_instant = if replace { - self.frames.get(&frame_id).map(|frame| frame.instant) + self.browsing_contexts.get(&browsing_context_id).map(|browsing_context| browsing_context.instant) } else { None }; @@ -1512,8 +1542,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> assert!(!self.pipelines.contains_key(&new_pipeline_id)); self.pipelines.insert(new_pipeline_id, pipeline); - self.pending_frames.push(FrameChange { - frame_id: frame_id, + self.pending_changes.push(SessionHistoryChange { + browsing_context_id: browsing_context_id, new_pipeline_id: new_pipeline_id, load_data: load_data, replace_instant: replace_instant, @@ -1566,13 +1596,13 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> let title = String::from("Alert"); let return_value = String::from(""); let event = MozBrowserEvent::ShowModalPrompt(prompt_type, title, message, return_value); - let top_level_frame_id = self.get_top_level_frame_for_pipeline(pipeline_id); + let top_level_browsing_context_id = self.get_top_level_browsing_context_for_pipeline(pipeline_id); - match self.frames.get(&self.root_frame_id) { - None => warn!("Alert sent after root frame closure."), - Some(root_frame) => match self.pipelines.get(&root_frame.pipeline_id) { + match self.browsing_contexts.get(&self.root_browsing_context_id) { + None => warn!("Alert sent after root browsing context closure."), + Some(root_browsing_context) => match self.pipelines.get(&root_browsing_context.pipeline_id) { None => warn!("Alert sent after root pipeline closure."), - Some(root_pipeline) => root_pipeline.trigger_mozbrowser_event(Some(top_level_frame_id), event), + Some(pipeline) => pipeline.trigger_mozbrowser_event(Some(top_level_browsing_context_id), event), } } } @@ -1602,8 +1632,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> // requested change so it can update its internal state. // // If replace is true, the current entry is replaced instead of a new entry being added. - let (frame_id, parent_info) = match self.pipelines.get(&source_id) { - Some(pipeline) => (pipeline.frame_id, pipeline.parent_info), + let (browsing_context_id, parent_info) = match self.pipelines.get(&source_id) { + Some(pipeline) => (pipeline.browsing_context_id, pipeline.parent_info), None => { warn!("Pipeline {:?} loaded after closure.", source_id); return None; @@ -1614,7 +1644,10 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> self.handle_load_start_msg(source_id); // Message the constellation to find the script thread for this iframe // and issue an iframe load through there. - let msg = ConstellationControlMsg::Navigate(parent_pipeline_id, frame_id, load_data, replace); + let msg = ConstellationControlMsg::Navigate(parent_pipeline_id, + browsing_context_id, + load_data, + replace); let result = match self.pipelines.get(&parent_pipeline_id) { Some(parent_pipeline) => parent_pipeline.event_loop.send(msg), None => { @@ -1628,17 +1661,17 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> Some(source_id) } None => { - let root_frame_id = self.root_frame_id; + let root_browsing_context_id = self.root_browsing_context_id; // Make sure no pending page would be overridden. - for frame_change in &self.pending_frames { - if frame_change.frame_id == root_frame_id { + for change in &self.pending_changes { + if change.browsing_context_id == root_browsing_context_id { // id that sent load msg is being changed already; abort return None; } } - if !self.pipeline_is_in_current_frame(source_id) { + if self.get_activity(source_id) == DocumentActivity::Inactive { // Disregard this load if the navigating pipeline is not actually // active. This could be caused by a delayed navigation (eg. from // a timer) or a race between multiple navigations (such as an @@ -1647,26 +1680,32 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } self.handle_load_start_msg(source_id); - // Being here means either there are no pending frames, or none of the pending + // Being here means either there are no pending changes, or none of the pending // changes would be overridden by changing the subframe associated with source_id. // Create the new pipeline - let window_size = self.frames.get(&root_frame_id).and_then(|frame| frame.size); + let window_size = self.browsing_contexts.get(&root_browsing_context_id) + .and_then(|browsing_context| browsing_context.size); let new_pipeline_id = PipelineId::new(); let sandbox = IFrameSandboxState::IFrameUnsandboxed; let replace_instant = if replace { - self.frames.get(&frame_id).map(|frame| frame.instant) + self.browsing_contexts.get(&browsing_context_id).map(|browsing_context| browsing_context.instant) } else { None }; - self.pending_frames.push(FrameChange { - frame_id: root_frame_id, + self.pending_changes.push(SessionHistoryChange { + browsing_context_id: root_browsing_context_id, new_pipeline_id: new_pipeline_id, load_data: load_data.clone(), replace_instant: replace_instant, }); - self.new_pipeline(new_pipeline_id, root_frame_id, None, window_size, load_data, sandbox, false); - + self.new_pipeline(new_pipeline_id, + root_browsing_context_id, + None, + window_size, + load_data, + sandbox, + false); Some(new_pipeline_id) } } @@ -1695,27 +1734,27 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> fn handle_traverse_history_msg(&mut self, pipeline_id: Option<PipelineId>, direction: TraversalDirection) { - let top_level_frame_id = pipeline_id - .map(|pipeline_id| self.get_top_level_frame_for_pipeline(pipeline_id)) - .unwrap_or(self.root_frame_id); + let top_level_browsing_context_id = pipeline_id + .map(|pipeline_id| self.get_top_level_browsing_context_for_pipeline(pipeline_id)) + .unwrap_or(self.root_browsing_context_id); let mut size = 0; let mut table = HashMap::new(); match direction { TraversalDirection::Forward(delta) => { - for entry in self.joint_session_future(top_level_frame_id).take(delta) { + for entry in self.joint_session_future(top_level_browsing_context_id).take(delta) { size = size + 1; - table.insert(entry.frame_id, entry.clone()); + table.insert(entry.browsing_context_id, entry.clone()); } if size < delta { return debug!("Traversing forward too much."); } }, TraversalDirection::Back(delta) => { - for entry in self.joint_session_past(top_level_frame_id).take(delta) { + for entry in self.joint_session_past(top_level_browsing_context_id).take(delta) { size = size + 1; - table.insert(entry.frame_id, entry.clone()); + table.insert(entry.browsing_context_id, entry.clone()); } if size < delta { return debug!("Traversing back too much."); @@ -1729,23 +1768,23 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } fn handle_joint_session_history_length(&self, pipeline_id: PipelineId, sender: IpcSender<u32>) { - let frame_id = self.get_top_level_frame_for_pipeline(pipeline_id); + let browsing_context_id = self.get_top_level_browsing_context_for_pipeline(pipeline_id); // Initialize length at 1 to count for the current active entry let mut length = 1; - for frame in self.full_frame_tree_iter(frame_id) { - length += frame.next.len(); - length += frame.prev.len(); + for browsing_context in self.all_browsing_contexts_iter(browsing_context_id) { + length += browsing_context.next.len(); + length += browsing_context.prev.len(); } let _ = sender.send(length as u32); } fn handle_key_msg(&mut self, ch: Option<char>, key: Key, state: KeyState, mods: KeyModifiers) { // Send to the explicitly focused pipeline (if it exists), or the root - // frame's current pipeline. If neither exist, fall back to sending to + // browsing context's current pipeline. If neither exist, fall back to sending to // the compositor below. - let root_pipeline_id = self.frames.get(&self.root_frame_id) - .map(|root_frame| root_frame.pipeline_id); + let root_pipeline_id = self.browsing_contexts.get(&self.root_browsing_context_id) + .map(|root_browsing_context| root_browsing_context.pipeline_id); let pipeline_id = self.focus_pipeline_id.or(root_pipeline_id); match pipeline_id { @@ -1769,8 +1808,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> fn handle_reload_msg(&mut self) { // Send Reload constellation msg to root script channel. - let root_pipeline_id = self.frames.get(&self.root_frame_id) - .map(|root_frame| root_frame.pipeline_id); + let root_pipeline_id = self.browsing_contexts.get(&self.root_browsing_context_id) + .map(|root_browsing_context| root_browsing_context.pipeline_id); if let Some(pipeline_id) = root_pipeline_id { let msg = ConstellationControlMsg::Reload(pipeline_id); @@ -1794,10 +1833,14 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } } - fn handle_post_message_msg(&mut self, frame_id: FrameId, origin: Option<ImmutableOrigin>, data: Vec<u8>) { - let pipeline_id = match self.frames.get(&frame_id) { - None => return warn!("postMessage to closed frame {}.", frame_id), - Some(frame) => frame.pipeline_id, + fn handle_post_message_msg(&mut self, + browsing_context_id: BrowsingContextId, + origin: Option<ImmutableOrigin>, + data: Vec<u8>) + { + let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) { + None => return warn!("postMessage to closed browsing_context {}.", browsing_context_id), + Some(browsing_context) => browsing_context.pipeline_id, }; let msg = ConstellationControlMsg::PostMessage(pipeline_id, origin, data); let result = match self.pipelines.get(&pipeline_id) { @@ -1819,20 +1862,21 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> // and pass the event to that script thread. // If the pipeline lookup fails, it is because we have torn down the pipeline, // so it is reasonable to silently ignore the event. - let frame_id = self.pipelines.get(&pipeline_id).map(|pipeline| pipeline.frame_id); + let browsing_context_id = self.pipelines.get(&pipeline_id).map(|pipeline| pipeline.browsing_context_id); match self.pipelines.get(&parent_pipeline_id) { - Some(pipeline) => pipeline.trigger_mozbrowser_event(frame_id, event), + Some(pipeline) => pipeline.trigger_mozbrowser_event(browsing_context_id, event), None => warn!("Pipeline {:?} handling mozbrowser event after closure.", parent_pipeline_id), } } - fn handle_get_pipeline(&mut self, frame_id: Option<FrameId>, + fn handle_get_pipeline(&mut self, + browsing_context_id: Option<BrowsingContextId>, resp_chan: IpcSender<Option<PipelineId>>) { - let frame_id = frame_id.unwrap_or(self.root_frame_id); - let current_pipeline_id = self.frames.get(&frame_id) - .map(|frame| frame.pipeline_id); - let pipeline_id_loaded = self.pending_frames.iter().rev() - .find(|x| x.frame_id == frame_id) + let browsing_context_id = browsing_context_id.unwrap_or(self.root_browsing_context_id); + let current_pipeline_id = self.browsing_contexts.get(&browsing_context_id) + .map(|browsing_context| browsing_context.pipeline_id); + let pipeline_id_loaded = self.pending_changes.iter().rev() + .find(|x| x.browsing_context_id == browsing_context_id) .map(|x| x.new_pipeline_id) .or(current_pipeline_id); if let Err(e) = resp_chan.send(pipeline_id_loaded) { @@ -1840,18 +1884,18 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } } - fn handle_get_frame(&mut self, - pipeline_id: PipelineId, - resp_chan: IpcSender<Option<FrameId>>) { - let frame_id = self.pipelines.get(&pipeline_id).map(|pipeline| pipeline.frame_id); - if let Err(e) = resp_chan.send(frame_id) { - warn!("Failed get_frame response ({}).", e); + fn handle_get_browsing_context(&mut self, + pipeline_id: PipelineId, + resp_chan: IpcSender<Option<BrowsingContextId>>) { + let browsing_context_id = self.pipelines.get(&pipeline_id).map(|pipeline| pipeline.browsing_context_id); + if let Err(e) = resp_chan.send(browsing_context_id) { + warn!("Failed get_browsing_context response ({}).", e); } } fn focus_parent_pipeline(&mut self, pipeline_id: PipelineId) { - let (frame_id, parent_info) = match self.pipelines.get(&pipeline_id) { - Some(pipeline) => (pipeline.frame_id, pipeline.parent_info), + let (browsing_context_id, parent_info) = match self.pipelines.get(&pipeline_id) { + Some(pipeline) => (pipeline.browsing_context_id, pipeline.parent_info), None => return warn!("Pipeline {:?} focus parent after closure.", pipeline_id), }; let (parent_pipeline_id, _) = match parent_info { @@ -1861,7 +1905,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> // Send a message to the parent of the provided pipeline (if it exists) // telling it to mark the iframe element as focused. - let msg = ConstellationControlMsg::FocusIFrame(parent_pipeline_id, frame_id); + let msg = ConstellationControlMsg::FocusIFrame(parent_pipeline_id, browsing_context_id); let result = match self.pipelines.get(&parent_pipeline_id) { Some(pipeline) => pipeline.event_loop.send(msg), None => return warn!("Pipeline {:?} focus after closure.", parent_pipeline_id), @@ -1879,26 +1923,26 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> self.focus_parent_pipeline(pipeline_id); } - fn handle_remove_iframe_msg(&mut self, frame_id: FrameId) -> Vec<PipelineId> { - let result = self.full_frame_tree_iter(frame_id) - .flat_map(|frame| frame.next.iter().chain(frame.prev.iter()) + fn handle_remove_iframe_msg(&mut self, browsing_context_id: BrowsingContextId) -> Vec<PipelineId> { + let result = self.all_browsing_contexts_iter(browsing_context_id) + .flat_map(|browsing_context| browsing_context.next.iter().chain(browsing_context.prev.iter()) .filter_map(|entry| entry.pipeline_id) - .chain(once(frame.pipeline_id))) + .chain(once(browsing_context.pipeline_id))) .collect(); - self.close_frame(frame_id, ExitPipelineMode::Normal); + self.close_browsing_context(browsing_context_id, ExitPipelineMode::Normal); result } fn handle_set_visible_msg(&mut self, pipeline_id: PipelineId, visible: bool) { - let frame_id = match self.pipelines.get(&pipeline_id) { - Some(pipeline) => pipeline.frame_id, - None => return warn!("No frame associated with pipeline {:?}", pipeline_id), + let browsing_context_id = match self.pipelines.get(&pipeline_id) { + Some(pipeline) => pipeline.browsing_context_id, + None => return warn!("No browsing context associated with pipeline {:?}", pipeline_id), }; - let child_pipeline_ids: Vec<PipelineId> = self.full_frame_tree_iter(frame_id) - .flat_map(|frame| frame.prev.iter().chain(frame.next.iter()) + let child_pipeline_ids: Vec<PipelineId> = self.all_browsing_contexts_iter(browsing_context_id) + .flat_map(|browsing_context| browsing_context.prev.iter().chain(browsing_context.next.iter()) .filter_map(|entry| entry.pipeline_id) - .chain(once(frame.pipeline_id))) + .chain(once(browsing_context.pipeline_id))) .collect(); for id in child_pipeline_ids { @@ -1909,13 +1953,13 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } fn handle_visibility_change_complete(&mut self, pipeline_id: PipelineId, visibility: bool) { - let (frame_id, parent_pipeline_info) = match self.pipelines.get(&pipeline_id) { + let (browsing_context_id, parent_pipeline_info) = match self.pipelines.get(&pipeline_id) { None => return warn!("Visibity change for closed pipeline {:?}.", pipeline_id), - Some(pipeline) => (pipeline.frame_id, pipeline.parent_info), + Some(pipeline) => (pipeline.browsing_context_id, pipeline.parent_info), }; if let Some((parent_pipeline_id, _)) = parent_pipeline_info { let visibility_msg = ConstellationControlMsg::NotifyVisibilityChange(parent_pipeline_id, - frame_id, + browsing_context_id, visibility); let result = match self.pipelines.get(&parent_pipeline_id) { None => return warn!("Parent pipeline {:?} closed", parent_pipeline_id), @@ -1998,8 +2042,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } }, WebDriverCommandMsg::TakeScreenshot(pipeline_id, reply) => { - let current_pipeline_id = self.frames.get(&self.root_frame_id) - .map(|root_frame| root_frame.pipeline_id); + let current_pipeline_id = self.browsing_contexts.get(&self.root_browsing_context_id) + .map(|root_browsing_context| root_browsing_context.pipeline_id); if Some(pipeline_id) == current_pipeline_id { self.compositor_proxy.send(ToCompositorMsg::CreatePng(reply)); } else { @@ -2012,9 +2056,9 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } // https://html.spec.whatwg.org/multipage/#traverse-the-history - fn traverse_to_entry(&mut self, entry: FrameState) { + fn traverse_to_entry(&mut self, entry: SessionHistoryEntry) { // Step 1. - let frame_id = entry.frame_id; + let browsing_context_id = entry.browsing_context_id; let pipeline_id = match entry.pipeline_id { Some(pipeline_id) => pipeline_id, None => { @@ -2022,22 +2066,22 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> // entry has been discarded, so we navigate to the entry // URL instead. When the document has activated, it will // traverse to the entry, but with the new pipeline id. - debug!("Reloading document {} for frame {}.", entry.load_data.url, entry.frame_id); + debug!("Reloading document {} in browsing context {}.", entry.load_data.url, entry.browsing_context_id); // TODO: save the sandbox state so it can be restored here. let sandbox = IFrameSandboxState::IFrameUnsandboxed; let new_pipeline_id = PipelineId::new(); let load_data = entry.load_data; - let (parent_info, window_size, is_private) = match self.frames.get(&frame_id) { - Some(frame) => match self.pipelines.get(&frame.pipeline_id) { - Some(pipeline) => (pipeline.parent_info, frame.size, pipeline.is_private), - None => (None, frame.size, false), + let (parent_info, window_size, is_private) = match self.browsing_contexts.get(&browsing_context_id) { + Some(browsing_context) => match self.pipelines.get(&browsing_context.pipeline_id) { + Some(pipeline) => (pipeline.parent_info, browsing_context.size, pipeline.is_private), + None => (None, browsing_context.size, false), }, - None => return warn!("no frame to traverse"), + None => return warn!("no browsing context to traverse"), }; - self.new_pipeline(new_pipeline_id, frame_id, parent_info, + self.new_pipeline(new_pipeline_id, browsing_context_id, parent_info, window_size, load_data.clone(), sandbox, is_private); - self.pending_frames.push(FrameChange { - frame_id: frame_id, + self.pending_changes.push(SessionHistoryChange { + browsing_context_id: browsing_context_id, new_pipeline_id: new_pipeline_id, load_data: load_data, replace_instant: Some(entry.instant), @@ -2049,24 +2093,24 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> // Check if the currently focused pipeline is the pipeline being replaced // (or a child of it). This has to be done here, before the current // frame tree is modified below. - let update_focus_pipeline = self.focused_pipeline_in_tree(entry.frame_id); + let update_focus_pipeline = self.focused_pipeline_is_descendant_of(entry.browsing_context_id); - let (old_pipeline_id, replaced_pipeline_id) = match self.frames.get_mut(&frame_id) { - Some(frame) => { - let old_pipeline_id = frame.pipeline_id; - let mut curr_entry = frame.current(); + let (old_pipeline_id, replaced_pipeline_id) = match self.browsing_contexts.get_mut(&browsing_context_id) { + Some(browsing_context) => { + let old_pipeline_id = browsing_context.pipeline_id; + let mut curr_entry = browsing_context.current(); - if entry.instant > frame.instant { + if entry.instant > browsing_context.instant { // We are traversing to the future. - while let Some(next) = frame.next.pop() { - frame.prev.push(curr_entry); + while let Some(next) = browsing_context.next.pop() { + browsing_context.prev.push(curr_entry); curr_entry = next; if entry.instant <= curr_entry.instant { break; } } - } else if entry.instant < frame.instant { + } else if entry.instant < browsing_context.instant { // We are traversing to the past. - while let Some(prev) = frame.prev.pop() { - frame.next.push(curr_entry); + while let Some(prev) = browsing_context.prev.pop() { + browsing_context.next.push(curr_entry); curr_entry = prev; if entry.instant >= curr_entry.instant { break; } } @@ -2076,11 +2120,11 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> let replaced_pipeline_id = curr_entry.pipeline_id; - frame.update_current(pipeline_id, entry); + browsing_context.update_current(pipeline_id, entry); (old_pipeline_id, replaced_pipeline_id) }, - None => return warn!("no frame to traverse"), + None => return warn!("no browsing context to traverse"), }; let parent_info = self.pipelines.get(&old_pipeline_id) @@ -2112,7 +2156,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> // This makes things like contentDocument work correctly. if let Some((parent_pipeline_id, _)) = parent_info { let msg = ConstellationControlMsg::UpdatePipelineId(parent_pipeline_id, - frame_id, pipeline_id, UpdatePipelineIdReason::Traversal); + browsing_context_id, pipeline_id, UpdatePipelineIdReason::Traversal); let result = match self.pipelines.get(&parent_pipeline_id) { None => return warn!("Pipeline {:?} child traversed after closure.", parent_pipeline_id), Some(pipeline) => pipeline.event_loop.send(msg), @@ -2133,17 +2177,17 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> // the current entry and the future entries. // LoadData of inner frames are ignored and replaced with the LoadData of the parent. - let top_level_frame_id = self.get_top_level_frame_for_pipeline(pipeline_id); + let top_level_browsing_context_id = self.get_top_level_browsing_context_for_pipeline(pipeline_id); - // Ignore LoadData of non-top-level frames. - let keep_load_data_if_top_frame = |state: &FrameState| { - match state.pipeline_id { - None => Some(state.load_data.clone()), + // Ignore LoadData of non-top-level browsing contexts. + let keep_load_data_if_top_browsing_context = |entry: &SessionHistoryEntry| { + match entry.pipeline_id { + None => Some(entry.load_data.clone()), Some(pipeline_id) => { match self.pipelines.get(&pipeline_id) { - None => Some(state.load_data.clone()), + None => Some(entry.load_data.clone()), Some(pipeline) => match pipeline.parent_info { - None => Some(state.load_data.clone()), + None => Some(entry.load_data.clone()), Some(_) => None, } } @@ -2151,8 +2195,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } }; - // If LoadData was ignored, use the LoadData of the previous FrameState, which - // is the LoadData of the parent frame. + // If LoadData was ignored, use the LoadData of the previous SessionHistoryEntry, which + // is the LoadData of the parent browsing context. let resolve_load_data = |previous_load_data: &mut LoadData, load_data| { let load_data = match load_data { None => previous_load_data.clone(), @@ -2162,13 +2206,13 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> Some(load_data) }; - let current_load_data = match self.frames.get(&top_level_frame_id) { - Some(frame) => frame.load_data.clone(), - None => return warn!("notify_history_changed error after top-level frame closed."), + let current_load_data = match self.browsing_contexts.get(&top_level_browsing_context_id) { + Some(browsing_context) => browsing_context.load_data.clone(), + None => return warn!("notify_history_changed error after top-level browsing context closed."), }; - let mut entries: Vec<LoadData> = self.joint_session_past(top_level_frame_id) - .map(&keep_load_data_if_top_frame) + let mut entries: Vec<LoadData> = self.joint_session_past(top_level_browsing_context_id) + .map(&keep_load_data_if_top_browsing_context) .scan(current_load_data.clone(), &resolve_load_data) .collect(); @@ -2178,31 +2222,31 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> entries.push(current_load_data.clone()); - entries.extend(self.joint_session_future(top_level_frame_id) - .map(&keep_load_data_if_top_frame) + entries.extend(self.joint_session_future(top_level_browsing_context_id) + .map(&keep_load_data_if_top_browsing_context) .scan(current_load_data.clone(), &resolve_load_data)); self.compositor_proxy.send(ToCompositorMsg::HistoryChanged(entries, current_index)); } - fn get_top_level_frame_for_pipeline(&self, mut pipeline_id: PipelineId) -> FrameId { + fn get_top_level_browsing_context_for_pipeline(&self, mut pipeline_id: PipelineId) -> BrowsingContextId { if PREFS.is_mozbrowser_enabled() { loop { match self.pipelines.get(&pipeline_id) { Some(pipeline) => match pipeline.parent_info { - Some((_, FrameType::MozBrowserIFrame)) => return pipeline.frame_id, + Some((_, FrameType::MozBrowserIFrame)) => return pipeline.browsing_context_id, Some((parent_id, _)) => pipeline_id = parent_id, - None => return self.root_frame_id, + None => return self.root_browsing_context_id, }, None => { warn!("Finding top-level ancestor for pipeline {} after closure.", pipeline_id); - return self.root_frame_id; + return self.root_browsing_context_id; }, } } } else { - // If mozbrowser is not enabled, the root frame is the only top-level frame - self.root_frame_id + // If mozbrowser is not enabled, the root browsing context is the only top-level browsing context + self.root_browsing_context_id } } @@ -2217,37 +2261,37 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } } - fn add_or_replace_pipeline_in_frame_tree(&mut self, frame_change: FrameChange) { - debug!("Setting frame {} to be pipeline {}.", frame_change.frame_id, frame_change.new_pipeline_id); + fn change_session_history(&mut self, change: SessionHistoryChange) { + debug!("Setting browsing context {} to be pipeline {}.", change.browsing_context_id, change.new_pipeline_id); // If the currently focused pipeline is the one being changed (or a child // of the pipeline being changed) then update the focus pipeline to be // the replacement. - if self.focused_pipeline_in_tree(frame_change.frame_id) { - self.focus_pipeline_id = Some(frame_change.new_pipeline_id); + if self.focused_pipeline_is_descendant_of(change.browsing_context_id) { + self.focus_pipeline_id = Some(change.new_pipeline_id); } - let (evicted_id, new_frame, navigated, location_changed) = if let Some(instant) = frame_change.replace_instant { - debug!("Replacing pipeline in existing frame with timestamp {:?}.", instant); - let entry = FrameState { - frame_id: frame_change.frame_id, - pipeline_id: Some(frame_change.new_pipeline_id), - load_data: frame_change.load_data.clone(), + let (evicted_id, new_context, navigated, location_changed) = if let Some(instant) = change.replace_instant { + debug!("Replacing pipeline in existing browsing context with timestamp {:?}.", instant); + let entry = SessionHistoryEntry { + browsing_context_id: change.browsing_context_id, + pipeline_id: Some(change.new_pipeline_id), + load_data: change.load_data.clone(), instant: instant, }; self.traverse_to_entry(entry); (None, false, None, false) - } else if let Some(frame) = self.frames.get_mut(&frame_change.frame_id) { - debug!("Adding pipeline to existing frame."); - let old_pipeline_id = frame.pipeline_id; - frame.load(frame_change.new_pipeline_id, frame_change.load_data.clone()); - let evicted_id = frame.prev.len() + } else if let Some(browsing_context) = self.browsing_contexts.get_mut(&change.browsing_context_id) { + debug!("Adding pipeline to existing browsing context."); + let old_pipeline_id = browsing_context.pipeline_id; + browsing_context.load(change.new_pipeline_id, change.load_data.clone()); + let evicted_id = browsing_context.prev.len() .checked_sub(PREFS.get("session-history.max-length").as_u64().unwrap_or(20) as usize) - .and_then(|index| frame.prev.get_mut(index)) + .and_then(|index| browsing_context.prev.get_mut(index)) .and_then(|entry| entry.pipeline_id.take()); (evicted_id, false, Some(old_pipeline_id), true) } else { - debug!("Adding pipeline to new frame."); + debug!("Adding pipeline to new browsing context."); (None, true, None, true) }; @@ -2255,26 +2299,26 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> self.close_pipeline(evicted_id, DiscardBrowsingContext::No, ExitPipelineMode::Normal); } - if new_frame { - self.new_frame(frame_change.frame_id, - frame_change.new_pipeline_id, - frame_change.load_data); - self.update_activity(frame_change.new_pipeline_id); - self.notify_history_changed(frame_change.new_pipeline_id); + if new_context { + self.new_browsing_context(change.browsing_context_id, + change.new_pipeline_id, + change.load_data); + self.update_activity(change.new_pipeline_id); + self.notify_history_changed(change.new_pipeline_id); }; if let Some(old_pipeline_id) = navigated { // Deactivate the old pipeline, and activate the new one. self.update_activity(old_pipeline_id); - self.update_activity(frame_change.new_pipeline_id); + self.update_activity(change.new_pipeline_id); // Clear the joint session future - let top_level_frame_id = self.get_top_level_frame_for_pipeline(frame_change.new_pipeline_id); - self.clear_joint_session_future(top_level_frame_id); - self.notify_history_changed(frame_change.new_pipeline_id); + let top_level_id = self.get_top_level_browsing_context_for_pipeline(change.new_pipeline_id); + self.clear_joint_session_future(top_level_id); + self.notify_history_changed(change.new_pipeline_id); } if location_changed { - self.trigger_mozbrowserlocationchange(frame_change.new_pipeline_id); + self.trigger_mozbrowserlocationchange(change.new_pipeline_id); } // Build frame tree @@ -2289,22 +2333,22 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> if let Some((parent_pipeline_id, _)) = pipeline.parent_info { if let Some(parent_pipeline) = self.pipelines.get(&parent_pipeline_id) { let msg = ConstellationControlMsg::UpdatePipelineId(parent_pipeline_id, - pipeline.frame_id, pipeline_id, UpdatePipelineIdReason::Navigation); + pipeline.browsing_context_id, pipeline_id, UpdatePipelineIdReason::Navigation); let _ = parent_pipeline.event_loop.send(msg); } } } - // Find the pending frame change whose new pipeline id is pipeline_id. - let pending_index = self.pending_frames.iter().rposition(|frame_change| { - frame_change.new_pipeline_id == pipeline_id + // Find the pending change whose new pipeline id is pipeline_id. + let pending_index = self.pending_changes.iter().rposition(|change| { + change.new_pipeline_id == pipeline_id }); - // If it is found, remove it from the pending frames, and make it + // If it is found, remove it from the pending changes, and make it // the active document of its frame. if let Some(pending_index) = pending_index { - let frame_change = self.pending_frames.swap_remove(pending_index); - self.add_or_replace_pipeline_in_frame_tree(frame_change); + let change = self.pending_changes.swap_remove(pending_index); + self.change_session_history(change); } } @@ -2312,8 +2356,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> fn handle_window_size_msg(&mut self, new_size: WindowSizeData, size_type: WindowSizeType) { debug!("handle_window_size_msg: {:?}", new_size.initial_viewport.to_untyped()); - let frame_id = self.root_frame_id; - self.resize_frame(new_size, size_type, frame_id); + let browsing_context_id = self.root_browsing_context_id; + self.resize_frame(new_size, size_type, browsing_context_id); if let Some(resize_channel) = self.webdriver.resize_channel.take() { let _ = resize_channel.send(new_size); @@ -2340,28 +2384,28 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> // avoiding this panic would require a mechanism for dealing // with low-resource scenarios. // - // If there is no root frame yet, the initial page has + // If there is no root browsing context yet, the initial page has // not loaded, so there is nothing to save yet. - if !self.frames.contains_key(&self.root_frame_id) { - return ReadyToSave::NoRootFrame; + if !self.browsing_contexts.contains_key(&self.root_browsing_context_id) { + return ReadyToSave::NoRootBrowsingContext; } // If there are pending loads, wait for those to complete. - if !self.pending_frames.is_empty() { - return ReadyToSave::PendingFrames; + if !self.pending_changes.is_empty() { + return ReadyToSave::PendingChanges; } let (state_sender, state_receiver) = ipc::channel().expect("Failed to create IPC channel!"); let (epoch_sender, epoch_receiver) = ipc::channel().expect("Failed to create IPC channel!"); - // Step through the current frame tree, checking that the script + // Step through the fully active browsing contexts, checking that the script // thread is idle, and that the current epoch of the layout thread // matches what the compositor has painted. If all these conditions // are met, then the output image should not change and a reftest // screenshot can safely be written. - for frame in self.current_frame_tree_iter(self.root_frame_id) { - let pipeline_id = frame.pipeline_id; - debug!("Checking readiness of frame {}, pipeline {}.", frame.id, pipeline_id); + for browsing_context in self.fully_active_browsing_contexts_iter(self.root_browsing_context_id) { + let pipeline_id = browsing_context.pipeline_id; + debug!("Checking readiness of browsing context {}, pipeline {}.", browsing_context.id, pipeline_id); let pipeline = match self.pipelines.get(&pipeline_id) { None => { @@ -2388,7 +2432,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } // See if this pipeline has reached idle script state yet. - match self.document_states.get(&frame.pipeline_id) { + match self.document_states.get(&browsing_context.pipeline_id) { Some(&DocumentState::Idle) => {} Some(&DocumentState::Pending) | None => { return ReadyToSave::DocumentLoading; @@ -2399,7 +2443,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> // size for the pipeline, then its painting should be up to date. If the constellation // *hasn't* received a size, it could be that the layer was hidden by script before the // compositor discovered it, so we just don't check the layer. - if let Some(size) = frame.size { + if let Some(size) = browsing_context.size { // If the rectangle for this pipeline is zero sized, it will // never be painted. In this case, don't query the layout // thread as it won't contribute to the final output image. @@ -2408,7 +2452,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } // Get the epoch that the compositor has drawn for this pipeline. - let compositor_epoch = pipeline_states.get(&frame.pipeline_id); + let compositor_epoch = pipeline_states.get(&browsing_context.pipeline_id); match compositor_epoch { Some(compositor_epoch) => { // Synchronously query the layout thread to see if the current @@ -2444,8 +2488,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> let mut ancestor_id = pipeline_id; loop { if let Some(ancestor) = self.pipelines.get(&ancestor_id) { - if let Some(frame) = self.frames.get(&ancestor.frame_id) { - if frame.pipeline_id == ancestor_id { + if let Some(browsing_context) = self.browsing_contexts.get(&ancestor.browsing_context_id) { + if browsing_context.pipeline_id == ancestor_id { if let Some((parent_id, FrameType::IFrame)) = ancestor.parent_info { ancestor_id = parent_id; continue; @@ -2474,7 +2518,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> activity }; for child_id in &pipeline.children { - if let Some(child) = self.frames.get(child_id) { + if let Some(child) = self.browsing_contexts.get(child_id) { self.set_activity(child.pipeline_id, child_activity); } } @@ -2488,15 +2532,19 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> /// Handle updating the size of a frame. This notifies every pipeline in the frame of the new /// size. - fn resize_frame(&mut self, new_size: WindowSizeData, size_type: WindowSizeType, frame_id: FrameId) { - if let Some(frame) = self.frames.get_mut(&frame_id) { - frame.size = Some(new_size.initial_viewport); + fn resize_frame(&mut self, + new_size: WindowSizeData, + size_type: WindowSizeType, + browsing_context_id: BrowsingContextId) + { + if let Some(browsing_context) = self.browsing_contexts.get_mut(&browsing_context_id) { + browsing_context.size = Some(new_size.initial_viewport); } - if let Some(frame) = self.frames.get(&frame_id) { + if let Some(browsing_context) = self.browsing_contexts.get(&browsing_context_id) { // Send Resize (or ResizeInactive) messages to each // pipeline in the frame tree. - let pipeline_id = frame.pipeline_id; + let pipeline_id = browsing_context.pipeline_id; let pipeline = match self.pipelines.get(&pipeline_id) { None => return warn!("Pipeline {:?} resized after closing.", pipeline_id), Some(pipeline) => pipeline, @@ -2506,7 +2554,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> new_size, size_type )); - let pipelines = frame.prev.iter().chain(frame.next.iter()) + let pipelines = browsing_context.prev.iter().chain(browsing_context.next.iter()) .filter_map(|entry| entry.pipeline_id) .filter_map(|pipeline_id| self.pipelines.get(&pipeline_id)); for pipeline in pipelines { @@ -2518,13 +2566,13 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } // Send resize message to any pending pipelines that aren't loaded yet. - for pending_frame in &self.pending_frames { - let pipeline_id = pending_frame.new_pipeline_id; + for change in &self.pending_changes { + let pipeline_id = change.new_pipeline_id; let pipeline = match self.pipelines.get(&pipeline_id) { None => { warn!("Pending pipeline {:?} is closed", pipeline_id); continue; } Some(pipeline) => pipeline, }; - if pipeline.frame_id == frame_id { + if pipeline.browsing_context_id == browsing_context_id { let _ = pipeline.event_loop.send(ConstellationControlMsg::Resize( pipeline.id, new_size, @@ -2534,13 +2582,13 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } } - fn clear_joint_session_future(&mut self, frame_id: FrameId) { - let frame_ids: Vec<FrameId> = self.full_frame_tree_iter(frame_id) - .map(|frame| frame.id) + fn clear_joint_session_future(&mut self, browsing_context_id: BrowsingContextId) { + let browsing_context_ids: Vec<BrowsingContextId> = self.all_browsing_contexts_iter(browsing_context_id) + .map(|browsing_context| browsing_context.id) .collect(); - for frame_id in frame_ids { - let evicted = match self.frames.get_mut(&frame_id) { - Some(frame) => frame.remove_forward_entries(), + for browsing_context_id in browsing_context_ids { + let evicted = match self.browsing_contexts.get_mut(&browsing_context_id) { + Some(browsing_context) => browsing_context.remove_forward_entries(), None => continue, }; for entry in evicted { @@ -2551,18 +2599,18 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } } - // Close a frame (and all children) - fn close_frame(&mut self, frame_id: FrameId, exit_mode: ExitPipelineMode) { - debug!("Closing frame {}.", frame_id); - let parent_info = self.frames.get(&frame_id) - .and_then(|frame| self.pipelines.get(&frame.pipeline_id)) + // Close a browsing context (and all children) + fn close_browsing_context(&mut self, browsing_context_id: BrowsingContextId, exit_mode: ExitPipelineMode) { + debug!("Closing browsing context {}.", browsing_context_id); + let parent_info = self.browsing_contexts.get(&browsing_context_id) + .and_then(|browsing_context| self.pipelines.get(&browsing_context.pipeline_id)) .and_then(|pipeline| pipeline.parent_info); - self.close_frame_children(frame_id, DiscardBrowsingContext::Yes, exit_mode); + self.close_browsing_context_children(browsing_context_id, DiscardBrowsingContext::Yes, exit_mode); - self.event_loops.remove(&frame_id); - if self.frames.remove(&frame_id).is_none() { - warn!("Closing frame {:?} twice.", frame_id); + self.event_loops.remove(&browsing_context_id); + if self.browsing_contexts.remove(&browsing_context_id).is_none() { + warn!("Closing browsing context {:?} twice.", browsing_context_id); } if let Some((parent_pipeline_id, _)) = parent_info { @@ -2570,56 +2618,60 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> None => return warn!("Pipeline {:?} child closed after parent.", parent_pipeline_id), Some(parent_pipeline) => parent_pipeline, }; - parent_pipeline.remove_child(frame_id); + parent_pipeline.remove_child(browsing_context_id); } - debug!("Closed frame {:?}.", frame_id); + debug!("Closed browsing context {:?}.", browsing_context_id); } - // Close the children of a frame - fn close_frame_children(&mut self, frame_id: FrameId, dbc: DiscardBrowsingContext, exit_mode: ExitPipelineMode) { - debug!("Closing frame children {}.", frame_id); + // Close the children of a browsing context + fn close_browsing_context_children(&mut self, + browsing_context_id: BrowsingContextId, + dbc: DiscardBrowsingContext, + exit_mode: ExitPipelineMode) + { + debug!("Closing browsing context children {}.", browsing_context_id); // Store information about the pipelines to be closed. Then close the - // pipelines, before removing ourself from the frames hash map. This + // pipelines, before removing ourself from the browsing_contexts hash map. This // ordering is vital - so that if close_pipeline() ends up closing - // any child frames, they can be removed from the parent frame correctly. - let mut pipelines_to_close: Vec<PipelineId> = self.pending_frames.iter() - .filter(|frame_change| frame_change.frame_id == frame_id) - .map(|frame_change| frame_change.new_pipeline_id) + // any child browsing contexts, they can be removed from the parent browsing context correctly. + let mut pipelines_to_close: Vec<PipelineId> = self.pending_changes.iter() + .filter(|change| change.browsing_context_id == browsing_context_id) + .map(|change| change.new_pipeline_id) .collect(); - if let Some(frame) = self.frames.get(&frame_id) { - pipelines_to_close.extend(frame.next.iter().filter_map(|state| state.pipeline_id)); - pipelines_to_close.push(frame.pipeline_id); - pipelines_to_close.extend(frame.prev.iter().filter_map(|state| state.pipeline_id)); + if let Some(browsing_context) = self.browsing_contexts.get(&browsing_context_id) { + pipelines_to_close.extend(browsing_context.next.iter().filter_map(|state| state.pipeline_id)); + pipelines_to_close.push(browsing_context.pipeline_id); + pipelines_to_close.extend(browsing_context.prev.iter().filter_map(|state| state.pipeline_id)); } for pipeline_id in pipelines_to_close { self.close_pipeline(pipeline_id, dbc, exit_mode); } - debug!("Closed frame children {}.", frame_id); + debug!("Closed browsing context children {}.", browsing_context_id); } - // Close all pipelines at and beneath a given frame + // Close all pipelines at and beneath a given browsing context fn close_pipeline(&mut self, pipeline_id: PipelineId, dbc: DiscardBrowsingContext, exit_mode: ExitPipelineMode) { debug!("Closing pipeline {:?}.", pipeline_id); - // Store information about the frames to be closed. Then close the - // frames, before removing ourself from the pipelines hash map. This - // ordering is vital - so that if close_frame() ends up closing + // Store information about the browsing contexts to be closed. Then close the + // browsing contexts, before removing ourself from the pipelines hash map. This + // ordering is vital - so that if close_browsing_context() ends up closing // any child pipelines, they can be removed from the parent pipeline correctly. - let frames_to_close = { - let mut frames_to_close = vec!(); + let browsing_contexts_to_close = { + let mut browsing_contexts_to_close = vec!(); if let Some(pipeline) = self.pipelines.get(&pipeline_id) { - frames_to_close.extend_from_slice(&pipeline.children); + browsing_contexts_to_close.extend_from_slice(&pipeline.children); } - frames_to_close + browsing_contexts_to_close }; - // Remove any child frames - for child_frame in &frames_to_close { - self.close_frame(*child_frame, exit_mode); + // Remove any child browsing contexts + for child_browsing_context in &browsing_contexts_to_close { + self.close_browsing_context(*child_browsing_context, exit_mode); } // Note, we don't remove the pipeline now, we wait for the message to come back from @@ -2629,12 +2681,12 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> None => return warn!("Closing pipeline {:?} twice.", pipeline_id), }; - // Remove this pipeline from pending frames if it hasn't loaded yet. - let pending_index = self.pending_frames.iter().position(|frame_change| { - frame_change.new_pipeline_id == pipeline_id + // Remove this pipeline from pending changes if it hasn't loaded yet. + let pending_index = self.pending_changes.iter().position(|change| { + change.new_pipeline_id == pipeline_id }); if let Some(pending_index) = pending_index { - self.pending_frames.remove(pending_index); + self.pending_changes.remove(pending_index); } // Inform script, compositor that this pipeline has exited. @@ -2671,19 +2723,19 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } } - // Convert a frame to a sendable form to pass to the compositor - fn frame_to_sendable(&self, frame_id: FrameId) -> Option<SendableFrameTree> { - self.frames.get(&frame_id).and_then(|frame: &Frame| { - self.pipelines.get(&frame.pipeline_id).map(|pipeline: &Pipeline| { + // Convert a browsing context to a sendable form to pass to the compositor + fn browsing_context_to_sendable(&self, browsing_context_id: BrowsingContextId) -> Option<SendableFrameTree> { + self.browsing_contexts.get(&browsing_context_id).and_then(|browsing_context| { + self.pipelines.get(&browsing_context.pipeline_id).map(|pipeline| { let mut frame_tree = SendableFrameTree { pipeline: pipeline.to_sendable(), - size: frame.size, + size: browsing_context.size, children: vec!(), }; - for child_frame_id in &pipeline.children { - if let Some(frame) = self.frame_to_sendable(*child_frame_id) { - frame_tree.children.push(frame); + for child_browsing_context_id in &pipeline.children { + if let Some(child) = self.browsing_context_to_sendable(*child_browsing_context_id) { + frame_tree.children.push(child); } } @@ -2697,8 +2749,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> // Note that this function can panic, due to ipc-channel creation failure. // avoiding this panic would require a mechanism for dealing // with low-resource scenarios. - debug!("Sending frame tree for frame {}.", self.root_frame_id); - if let Some(frame_tree) = self.frame_to_sendable(self.root_frame_id) { + debug!("Sending frame tree for browsing context {}.", self.root_browsing_context_id); + if let Some(frame_tree) = self.browsing_context_to_sendable(self.root_browsing_context_id) { let (chan, port) = ipc::channel().expect("Failed to create IPC channel!"); self.compositor_proxy.send(ToCompositorMsg::SetFrameTree(frame_tree, chan)); @@ -2716,11 +2768,11 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> Some(pipeline) => if let Some((parent_id, FrameType::MozBrowserIFrame)) = pipeline.parent_info { match self.pipelines.get(&parent_id) { Some(parent) => { - let can_go_forward = !self.joint_session_future_is_empty(pipeline.frame_id); - let can_go_back = !self.joint_session_past_is_empty(pipeline.frame_id); + let can_go_forward = !self.joint_session_future_is_empty(pipeline.browsing_context_id); + let can_go_back = !self.joint_session_past_is_empty(pipeline.browsing_context_id); let url = pipeline.url.to_string(); let event = MozBrowserEvent::LocationChange(url, can_go_back, can_go_forward); - parent.trigger_mozbrowser_event(Some(pipeline.frame_id), event); + parent.trigger_mozbrowser_event(Some(pipeline.browsing_context_id), event); }, None => warn!("triggered mozbrowser location change on closed parent {}", parent_id), } @@ -2731,7 +2783,11 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> // https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowsererror // Note that this does not require the pipeline to be an immediate child of the root - fn trigger_mozbrowsererror(&mut self, top_level_frame_id: FrameId, reason: String, backtrace: Option<String>) { + fn trigger_mozbrowsererror(&mut self, + top_level_browsing_context_id: BrowsingContextId, + reason: String, + backtrace: Option<String>) + { if !PREFS.is_mozbrowser_enabled() { return; } let mut report = String::new(); @@ -2753,36 +2809,25 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> let event = MozBrowserEvent::Error(MozBrowserErrorType::Fatal, reason, report); - match self.frames.get(&top_level_frame_id) { - None => warn!("Mozbrowser error after top-level frame closed."), - Some(frame) => match self.pipelines.get(&frame.pipeline_id) { + match self.browsing_contexts.get(&top_level_browsing_context_id) { + None => warn!("Mozbrowser error after top-level browsing context closed."), + Some(browsing_context) => match self.pipelines.get(&browsing_context.pipeline_id) { None => warn!("Mozbrowser error after top-level pipeline closed."), Some(pipeline) => match pipeline.parent_info { None => pipeline.trigger_mozbrowser_event(None, event), Some((parent_id, _)) => match self.pipelines.get(&parent_id) { None => warn!("Mozbrowser error after root pipeline closed."), - Some(parent) => parent.trigger_mozbrowser_event(Some(top_level_frame_id), event), + Some(parent) => parent.trigger_mozbrowser_event(Some(top_level_browsing_context_id), event), }, }, }, }; } - fn focused_pipeline_in_tree(&self, frame_id: FrameId) -> bool { + fn focused_pipeline_is_descendant_of(&self, browsing_context_id: BrowsingContextId) -> bool { self.focus_pipeline_id.map_or(false, |pipeline_id| { - self.pipeline_exists_in_tree(pipeline_id, frame_id) + self.fully_active_browsing_contexts_iter(browsing_context_id) + .any(|browsing_context| browsing_context.pipeline_id == pipeline_id) }) } - - fn pipeline_is_in_current_frame(&self, pipeline_id: PipelineId) -> bool { - self.pipeline_exists_in_tree(pipeline_id, self.root_frame_id) - } - - fn pipeline_exists_in_tree(&self, - pipeline_id: PipelineId, - root_frame_id: FrameId) -> bool { - self.current_frame_tree_iter(root_frame_id) - .any(|current_frame| current_frame.pipeline_id == pipeline_id) - } - } diff --git a/components/constellation/lib.rs b/components/constellation/lib.rs index 536775de0ef..6958a5b7c39 100644 --- a/components/constellation/lib.rs +++ b/components/constellation/lib.rs @@ -41,9 +41,9 @@ extern crate style_traits; extern crate webrender_traits; extern crate webvr_traits; +mod browsingcontext; mod constellation; mod event_loop; -mod frame; mod pipeline; #[cfg(not(target_os = "windows"))] mod sandboxing; diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index 60401f4313c..01c386779bb 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -15,7 +15,7 @@ use ipc_channel::Error; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use layout_traits::LayoutThreadFactory; -use msg::constellation_msg::{FrameId, FrameType, PipelineId, PipelineNamespaceId}; +use msg::constellation_msg::{BrowsingContextId, FrameType, PipelineId, PipelineNamespaceId}; use net::image_cache::ImageCacheImpl; use net_traits::{IpcSend, ResourceThreads}; use net_traits::image_cache::ImageCache; @@ -49,14 +49,14 @@ pub struct Pipeline { /// The ID of the pipeline. pub id: PipelineId, - /// The ID of the frame that contains this Pipeline. - pub frame_id: FrameId, + /// The ID of the browsing context that contains this Pipeline. + pub browsing_context_id: BrowsingContextId, /// The parent pipeline of this one. `None` if this is a root pipeline. /// Note that because of mozbrowser iframes, even top-level pipelines /// may have a parent (in which case the frame type will be /// `MozbrowserIFrame`). - /// TODO: move this field to `Frame`. + /// TODO: move this field to `BrowsingContext`. pub parent_info: Option<(PipelineId, FrameType)>, /// The event loop handling this pipeline. @@ -80,11 +80,11 @@ pub struct Pipeline { /// animations cause composites to be continually scheduled. pub running_animations: bool, - /// The child frames of this pipeline (these are iframes in the document). - pub children: Vec<FrameId>, + /// The child browsing contexts of this pipeline (these are iframes in the document). + pub children: Vec<BrowsingContextId>, /// Whether this pipeline is in private browsing mode. - /// TODO: move this field to `Frame`. + /// TODO: move this field to `BrowsingContext`. pub is_private: bool, /// Whether this pipeline should be treated as visible for the purposes of scheduling and @@ -100,11 +100,11 @@ pub struct InitialPipelineState { /// The ID of the pipeline to create. pub id: PipelineId, - /// The ID of the frame that contains this Pipeline. - pub frame_id: FrameId, + /// The ID of the browsing context that contains this Pipeline. + pub browsing_context_id: BrowsingContextId, - /// The ID of the top-level frame that contains this Pipeline. - pub top_level_frame_id: FrameId, + /// The ID of the top-level browsing context that contains this Pipeline. + pub top_level_browsing_context_id: BrowsingContextId, /// The ID of the parent pipeline and frame type, if any. /// If `None`, this is the root. @@ -200,7 +200,7 @@ impl Pipeline { let new_layout_info = NewLayoutInfo { parent_info: state.parent_info, new_pipeline_id: state.id, - frame_id: state.frame_id, + browsing_context_id: state.browsing_context_id, load_data: state.load_data, window_size: window_size, pipeline_port: pipeline_port, @@ -237,8 +237,8 @@ impl Pipeline { let unprivileged_pipeline_content = UnprivilegedPipelineContent { id: state.id, - frame_id: state.frame_id, - top_level_frame_id: state.top_level_frame_id, + browsing_context_id: state.browsing_context_id, + top_level_browsing_context_id: state.top_level_browsing_context_id, parent_info: state.parent_info, constellation_chan: state.constellation_chan, scheduler_chan: state.scheduler_chan, @@ -280,7 +280,7 @@ impl Pipeline { }; Ok(Pipeline::new(state.id, - state.frame_id, + state.browsing_context_id, state.parent_info, script_chan, pipeline_chan, @@ -293,7 +293,7 @@ impl Pipeline { /// Creates a new `Pipeline`, after the script and layout threads have been /// spawned. pub fn new(id: PipelineId, - frame_id: FrameId, + browsing_context_id: BrowsingContextId, parent_info: Option<(PipelineId, FrameType)>, event_loop: Rc<EventLoop>, layout_chan: IpcSender<LayoutControlMsg>, @@ -304,7 +304,7 @@ impl Pipeline { -> Pipeline { let pipeline = Pipeline { id: id, - frame_id: frame_id, + browsing_context_id: browsing_context_id, parent_info: parent_info, event_loop: event_loop, layout_chan: layout_chan, @@ -376,15 +376,15 @@ impl Pipeline { } } - /// Add a new child frame. - pub fn add_child(&mut self, frame_id: FrameId) { - self.children.push(frame_id); + /// Add a new child browsing context. + pub fn add_child(&mut self, browsing_context_id: BrowsingContextId) { + self.children.push(browsing_context_id); } - /// Remove a child frame. - pub fn remove_child(&mut self, frame_id: FrameId) { - match self.children.iter().position(|id| *id == frame_id) { - None => return warn!("Pipeline remove child already removed ({:?}).", frame_id), + /// Remove a child browsing context. + pub fn remove_child(&mut self, browsing_context_id: BrowsingContextId) { + match self.children.iter().position(|id| *id == browsing_context_id) { + None => return warn!("Pipeline remove child already removed ({:?}).", browsing_context_id), Some(index) => self.children.remove(index), }; } @@ -393,7 +393,7 @@ impl Pipeline { /// This will cause an event to be fired on an iframe in the document, /// or on the `Window` if no frame is given. pub fn trigger_mozbrowser_event(&self, - child_id: Option<FrameId>, + child_id: Option<BrowsingContextId>, event: MozBrowserEvent) { assert!(PREFS.is_mozbrowser_enabled()); @@ -433,8 +433,8 @@ impl Pipeline { #[derive(Deserialize, Serialize)] pub struct UnprivilegedPipelineContent { id: PipelineId, - frame_id: FrameId, - top_level_frame_id: FrameId, + browsing_context_id: BrowsingContextId, + top_level_browsing_context_id: BrowsingContextId, parent_info: Option<(PipelineId, FrameType)>, constellation_chan: IpcSender<ScriptMsg>, layout_to_constellation_chan: IpcSender<LayoutMsg>, @@ -470,8 +470,8 @@ impl UnprivilegedPipelineContent { let image_cache = Arc::new(ImageCacheImpl::new(self.webrender_api_sender.create_api())); let layout_pair = STF::create(InitialScriptState { id: self.id, - frame_id: self.frame_id, - top_level_frame_id: self.top_level_frame_id, + browsing_context_id: self.browsing_context_id, + top_level_browsing_context_id: self.top_level_browsing_context_id, parent_info: self.parent_info, control_chan: self.script_chan.clone(), control_port: self.script_port, @@ -491,7 +491,7 @@ impl UnprivilegedPipelineContent { }, self.load_data.clone()); LTF::create(self.id, - Some(self.top_level_frame_id), + Some(self.top_level_browsing_context_id), self.load_data.url, self.parent_info.is_some(), layout_pair, diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 626c3f56b3d..8ca587bfca4 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -30,11 +30,11 @@ use std::cmp::{self, Ordering}; use std::collections::HashMap; use std::fmt; use std::sync::Arc; -use style::computed_values::{border_style, filter, image_rendering, mix_blend_mode}; +use style::computed_values::{border_style, filter, image_rendering}; use style_traits::cursor::Cursor; use text::TextRun; use text::glyph::ByteIndex; -use webrender_traits::{self, ClipId, ColorF, GradientStop, ScrollPolicy, WebGLContextId}; +use webrender_traits::{self, ClipId, ColorF, GradientStop, MixBlendMode, ScrollPolicy, TransformStyle, WebGLContextId}; pub use style::dom::OpaqueNode; @@ -424,11 +424,14 @@ pub struct StackingContext { pub filters: filter::T, /// The blend mode with which this stacking context blends with its backdrop. - pub blend_mode: mix_blend_mode::T, + pub mix_blend_mode: MixBlendMode, /// A transform to be applied to this stacking context. pub transform: Option<Matrix4D<f32>>, + /// The transform style of this stacking context. + pub transform_style: TransformStyle, + /// The perspective matrix to be applied to children. pub perspective: Option<Matrix4D<f32>>, @@ -448,8 +451,9 @@ impl StackingContext { overflow: &Rect<Au>, z_index: i32, filters: filter::T, - blend_mode: mix_blend_mode::T, + mix_blend_mode: MixBlendMode, transform: Option<Matrix4D<f32>>, + transform_style: TransformStyle, perspective: Option<Matrix4D<f32>>, scroll_policy: ScrollPolicy, parent_scroll_id: ClipId) @@ -461,8 +465,9 @@ impl StackingContext { overflow: *overflow, z_index: z_index, filters: filters, - blend_mode: blend_mode, + mix_blend_mode: mix_blend_mode, transform: transform, + transform_style: transform_style, perspective: perspective, scroll_policy: scroll_policy, parent_scroll_id: parent_scroll_id, @@ -477,8 +482,9 @@ impl StackingContext { &Rect::zero(), 0, filter::T::new(Vec::new()), - mix_blend_mode::T::normal, + MixBlendMode::Normal, None, + TransformStyle::Flat, None, ScrollPolicy::Scrollable, pipeline_id.root_scroll_node()) diff --git a/components/layout/block.rs b/components/layout/block.rs index 5e30c6f08ee..adeb04730a3 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -46,7 +46,6 @@ use gfx_traits::print_tree::PrintTree; use incremental::RelayoutMode; use layout_debug; use model::{AdjoiningMargins, CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo, MaybeAuto}; -use model::{specified, specified_or_none}; use sequential; use serde::{Serialize, Serializer}; use servo_geometry::max_rect; @@ -326,7 +325,7 @@ impl CandidateBSizeIterator { MaybeAuto::Specified(block_container_block_size.scale_by(percent)) } (LengthOrPercentageOrAuto::Calc(calc), _) => { - MaybeAuto::from_option(calc.to_computed(block_container_block_size)) + MaybeAuto::from_option(calc.to_used_value(block_container_block_size)) } (LengthOrPercentageOrAuto::Percentage(_), None) | (LengthOrPercentageOrAuto::Auto, _) => MaybeAuto::Auto, @@ -337,7 +336,7 @@ impl CandidateBSizeIterator { Some(block_container_block_size.scale_by(percent)) } (LengthOrPercentageOrNone::Calc(calc), _) => { - calc.to_computed(block_container_block_size) + calc.to_used_value(block_container_block_size) } (LengthOrPercentageOrNone::Percentage(_), None) | (LengthOrPercentageOrNone::None, _) => None, @@ -348,7 +347,7 @@ impl CandidateBSizeIterator { block_container_block_size.scale_by(percent) } (LengthOrPercentage::Calc(calc), _) => { - calc.to_computed(block_container_block_size).unwrap_or(Au(0)) + calc.to_used_value(block_container_block_size).unwrap_or(Au(0)) } (LengthOrPercentage::Percentage(_), None) => Au(0), (LengthOrPercentage::Length(length), _) => length, @@ -657,7 +656,7 @@ impl BlockFlow { pub fn containing_block_size(&self, viewport_size: &Size2D<Au>, descendant: OpaqueFlow) -> LogicalSize<Au> { debug_assert!(self.base.flags.contains(IS_ABSOLUTELY_POSITIONED)); - if self.is_fixed() { + if self.is_fixed() || self.is_root() { // Initial containing block is the CB for the root LogicalSize::from_physical(self.base.writing_mode, *viewport_size) } else { @@ -1167,7 +1166,7 @@ impl BlockFlow { match (content_block_size, containing_block_size) { (LengthOrPercentageOrAuto::Calc(calc), _) => { - calc.to_computed(containing_block_size) + calc.to_used_value(containing_block_size) } (LengthOrPercentageOrAuto::Length(length), _) => Some(length), (LengthOrPercentageOrAuto::Percentage(percent), Some(container_size)) => { @@ -1417,8 +1416,8 @@ impl BlockFlow { // we know. if kid.is_inline_flow() { kid.as_mut_inline().first_line_indentation = - specified(self.fragment.style().get_inheritedtext().text_indent, - containing_block_size); + self.fragment.style().get_inheritedtext().text_indent + .to_used_value(containing_block_size); } } } @@ -1512,14 +1511,12 @@ impl BlockFlow { } else { content_box.size.inline } - self.fragment.margin.inline_start_end(); - let max_inline_size = specified_or_none( - self.fragment.style().max_inline_size(), - self.base.block_container_inline_size - ).unwrap_or(MAX_AU); - let min_inline_size = specified( - self.fragment.style().min_inline_size(), - self.base.block_container_inline_size - ); + let max_inline_size = + self.fragment.style().max_inline_size() + .to_used_value(self.base.block_container_inline_size) + .unwrap_or(MAX_AU); + let min_inline_size = + self.fragment.style().min_inline_size().to_used_value(self.base.block_container_inline_size); let specified_inline_size = self.fragment.style().content_inline_size(); let container_size = self.base.block_container_inline_size; let inline_size = @@ -2413,8 +2410,7 @@ pub trait ISizeAndMarginsComputer { // If the tentative used inline-size is greater than 'max-inline-size', inline-size should // be recalculated, but this time using the computed value of 'max-inline-size' as the // computed value for 'inline-size'. - match specified_or_none(block.fragment().style().max_inline_size(), - containing_block_inline_size) { + match block.fragment().style().max_inline_size().to_used_value(containing_block_inline_size) { Some(max_inline_size) if max_inline_size < solution.inline_size => { input.computed_inline_size = MaybeAuto::Specified(max_inline_size); solution = self.solve_inline_size_constraints(block, &input); @@ -2425,8 +2421,8 @@ pub trait ISizeAndMarginsComputer { // If the resulting inline-size is smaller than 'min-inline-size', inline-size should be // recalculated, but this time using the value of 'min-inline-size' as the computed value // for 'inline-size'. - let computed_min_inline_size = specified(block.fragment().style().min_inline_size(), - containing_block_inline_size); + let computed_min_inline_size = + block.fragment().style().min_inline_size().to_used_value(containing_block_inline_size); if computed_min_inline_size > solution.inline_size { input.computed_inline_size = MaybeAuto::Specified(computed_min_inline_size); solution = self.solve_inline_size_constraints(block, &input); diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index c3ac7f09048..63ec70b7096 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -33,8 +33,8 @@ use gfx_traits::{combine_id_with_fragment_type, FragmentType, StackingContextId} use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, LAST_FRAGMENT_OF_ELEMENT}; use ipc_channel::ipc; use list_item::ListItemFlow; -use model::{self, MaybeAuto, specified}; -use msg::constellation_msg::FrameId; +use model::{self, MaybeAuto}; +use msg::constellation_msg::BrowsingContextId; use net_traits::image::base::PixelFormat; use net_traits::image_cache::UsePlaceholder; use range::Range; @@ -68,7 +68,8 @@ use style::values::specified::position::{X, Y}; use style_traits::CSSPixel; use style_traits::cursor::Cursor; use table_cell::CollapsedBordersForCell; -use webrender_traits::{ColorF, ClipId, GradientStop, RepeatMode, ScrollPolicy}; +use webrender_helpers::{ToMixBlendMode, ToTransformStyle}; +use webrender_traits::{ColorF, ClipId, GradientStop, RepeatMode, ScrollPolicy, TransformStyle}; trait ResolvePercentage { fn resolve(&self, length: u32) -> u32; @@ -175,7 +176,7 @@ pub struct DisplayListBuildState<'a> { /// Vector containing iframe sizes, used to inform the constellation about /// new iframe sizes - pub iframe_sizes: Vec<(FrameId, TypedSize2D<f32, CSSPixel>)>, + pub iframe_sizes: Vec<(BrowsingContextId, TypedSize2D<f32, CSSPixel>)>, /// A stack of clips used to cull display list entries that are outside the /// rendered region. @@ -184,6 +185,9 @@ pub struct DisplayListBuildState<'a> { /// A stack of clips used to cull display list entries that are outside the /// rendered region, but only collected at containing block boundaries. pub containing_block_clip_stack: Vec<Rect<Au>>, + + /// The current transform style of the stacking context. + current_transform_style: TransformStyle, } impl<'a> DisplayListBuildState<'a> { @@ -201,6 +205,7 @@ impl<'a> DisplayListBuildState<'a> { iframe_sizes: Vec::new(), clip_stack: Vec::new(), containing_block_clip_stack: Vec::new(), + current_transform_style: TransformStyle::Flat, } } @@ -212,6 +217,7 @@ impl<'a> DisplayListBuildState<'a> { fn add_stacking_context(&mut self, parent_id: StackingContextId, stacking_context: StackingContext) { + self.current_transform_style = stacking_context.transform_style; let info = self.stacking_context_info .entry(parent_id) .or_insert(StackingContextInfo::new()); @@ -1020,10 +1026,8 @@ impl FragmentDisplayListBuilding for Fragment { let horiz_position = *get_cyclic(&background.background_position_x.0, index); let vert_position = *get_cyclic(&background.background_position_y.0, index); // Use `background-position` to get the offset. - let horizontal_position = model::specified(horiz_position, - bounds.size.width - image_size.width); - let vertical_position = model::specified(vert_position, - bounds.size.height - image_size.height); + let horizontal_position = horiz_position.to_used_value(bounds.size.width - image_size.width); + let vertical_position = vert_position.to_used_value(bounds.size.height - image_size.height); // The anchor position for this background, based on both the background-attachment // and background-position properties. @@ -1179,8 +1183,8 @@ impl FragmentDisplayListBuilding for Fragment { repeating: bool, style: &ServoComputedValues) -> display_list::RadialGradient { - let center = Point2D::new(specified(center.horizontal, bounds.size.width), - specified(center.vertical, bounds.size.height)); + let center = Point2D::new(center.horizontal.to_used_value(bounds.size.width), + center.vertical.to_used_value(bounds.size.height)); let radius = match *shape { GenericEndingShape::Circle(Circle::Radius(length)) => { Size2D::new(length, length) @@ -1189,7 +1193,7 @@ impl FragmentDisplayListBuilding for Fragment { convert_circle_size_keyword(extent, &bounds.size, ¢er) }, GenericEndingShape::Ellipse(Ellipse::Radii(x, y)) => { - Size2D::new(specified(x, bounds.size.width), specified(y, bounds.size.height)) + Size2D::new(x.to_used_value(bounds.size.width), y.to_used_value(bounds.size.height)) }, GenericEndingShape::Ellipse(Ellipse::Extent(extent)) => { convert_ellipse_size_keyword(extent, &bounds.size, ¢er) @@ -1823,7 +1827,7 @@ impl FragmentDisplayListBuilding for Fragment { let size = Size2D::new(item.bounds().size.width.to_f32_px(), item.bounds().size.height.to_f32_px()); - state.iframe_sizes.push((fragment_info.frame_id, TypedSize2D::from_untyped(&size))); + state.iframe_sizes.push((fragment_info.browsing_context_id, TypedSize2D::from_untyped(&size))); state.add_display_item(item); } @@ -1947,8 +1951,9 @@ impl FragmentDisplayListBuilding for Fragment { &overflow, self.effective_z_index(), filters, - self.style().get_effects().mix_blend_mode, + self.style().get_effects().mix_blend_mode.to_mix_blend_mode(), self.transform_matrix(&border_box), + self.style().get_used_transform_style().to_transform_style(), self.perspective_matrix(&border_box), scroll_policy, parent_scroll_id) @@ -2153,6 +2158,7 @@ pub struct PreservedDisplayListState { containing_block_scroll_root_id: ClipId, clips_pushed: usize, containing_block_clips_pushed: usize, + transform_style: TransformStyle, } impl PreservedDisplayListState { @@ -2163,6 +2169,7 @@ impl PreservedDisplayListState { containing_block_scroll_root_id: state.containing_block_scroll_root_id, clips_pushed: 0, containing_block_clips_pushed: 0, + transform_style: state.current_transform_style, } } @@ -2183,6 +2190,8 @@ impl PreservedDisplayListState { let truncate_length = state.containing_block_clip_stack.len() - self.containing_block_clips_pushed; state.containing_block_clip_stack.truncate(truncate_length); + + state.current_transform_style = self.transform_style; } fn push_clip(&mut self, @@ -2236,6 +2245,14 @@ impl BlockFlowDisplayListBuilding for BlockFlow { } match transform { + Some(transform) if transform.m13 != 0.0 || transform.m23 != 0.0 => { + // We cannot properly handle perspective transforms, because there may be a + // situation where an element is transformed from outside the clip into the + // clip region. Here we don't have enough information to detect when that is + // happening. For the moment we just punt on trying to optimize the display + // list for those cases. + max_rect() + } Some(transform) => { let clip = Rect::new(Point2D::new((clip.origin.x - origin.x).to_f32_px(), (clip.origin.y - origin.y).to_f32_px()), @@ -2614,14 +2631,14 @@ impl InlineFlowDisplayListBuilding for InlineFlow { fragment.stacking_context_id = fragment.stacking_context_id(); let current_stacking_context_id = state.current_stacking_context_id; - let current_scroll_root_id = state.current_scroll_root_id; + let stacking_context = fragment.create_stacking_context(fragment.stacking_context_id, + &self.base, + ScrollPolicy::Scrollable, + StackingContextCreationMode::Normal, + state.current_scroll_root_id); + state.add_stacking_context(current_stacking_context_id, - fragment.create_stacking_context( - fragment.stacking_context_id, - &self.base, - ScrollPolicy::Scrollable, - StackingContextCreationMode::Normal, - current_scroll_root_id)); + stacking_context); } _ => fragment.stacking_context_id = state.current_stacking_context_id, } @@ -2786,12 +2803,13 @@ struct StopRun { stop_count: usize, } -fn position_to_offset(position: LengthOrPercentage, Au(total_length): Au) -> f32 { +fn position_to_offset(position: LengthOrPercentage, total_length: Au) -> f32 { match position { - LengthOrPercentage::Length(Au(length)) => length as f32 / total_length as f32, + LengthOrPercentage::Length(Au(length)) => length as f32 / total_length.0 as f32, LengthOrPercentage::Percentage(percentage) => percentage as f32, - LengthOrPercentage::Calc(calc) => - calc.percentage() + (calc.length().0 as f32) / (total_length as f32), + LengthOrPercentage::Calc(calc) => { + calc.to_used_value(Some(total_length)).unwrap().0 as f32 / total_length.0 as f32 + }, } } diff --git a/components/layout/flex.rs b/components/layout/flex.rs index dd990c61510..0abd32b6dc0 100644 --- a/components/layout/flex.rs +++ b/components/layout/flex.rs @@ -18,7 +18,6 @@ use flow::{INLINE_POSITION_IS_STATIC, IS_ABSOLUTELY_POSITIONED}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; use layout_debug; use model::{IntrinsicISizes, MaybeAuto, SizeConstraint}; -use model::{specified, specified_or_none}; use std::cmp::{max, min}; use std::ops::Range; use style::computed_values::{align_content, align_self, flex_direction, flex_wrap, justify_content}; @@ -52,7 +51,7 @@ impl AxisSize { } } LengthOrPercentageOrAuto::Calc(calc) => { - match calc.to_computed(content_size) { + match calc.to_used_value(content_size) { Some(length) => AxisSize::Definite(length), None => AxisSize::Infinite, } @@ -79,7 +78,7 @@ fn from_flex_basis(flex_basis: LengthOrPercentageOrAutoOrContent, (LengthOrPercentageOrAutoOrContent::Percentage(_), None) => MaybeAuto::Auto, (LengthOrPercentageOrAutoOrContent::Calc(calc), _) => - MaybeAuto::from_option(calc.to_computed(containing_length)), + MaybeAuto::from_option(calc.to_used_value(containing_length)), (LengthOrPercentageOrAutoOrContent::Content, _) => MaybeAuto::Auto, (LengthOrPercentageOrAutoOrContent::Auto, Some(size)) => @@ -169,10 +168,11 @@ impl FlexItem { - margin + block.fragment.box_sizing_boundary(direction); self.base_size = basis.specified_or_default(content_size); - self.max_size = specified_or_none(block.fragment.style.max_inline_size(), - containing_length).unwrap_or(MAX_AU); - self.min_size = specified(block.fragment.style.min_inline_size(), - containing_length); + self.max_size = + block.fragment.style.max_inline_size() + .to_used_value(containing_length) + .unwrap_or(MAX_AU); + self.min_size = block.fragment.style.min_inline_size().to_used_value(containing_length); } Direction::Block => { let basis = from_flex_basis(block.fragment.style.get_position().flex_basis, @@ -182,10 +182,11 @@ impl FlexItem { - block.fragment.border_padding.block_start_end() + block.fragment.box_sizing_boundary(direction); self.base_size = basis.specified_or_default(content_size); - self.max_size = specified_or_none(block.fragment.style.max_block_size(), - containing_length).unwrap_or(MAX_AU); - self.min_size = specified(block.fragment.style.min_block_size(), - containing_length); + self.max_size = + block.fragment.style.max_block_size() + .to_used_value(containing_length) + .unwrap_or(MAX_AU); + self.min_size = block.fragment.style.min_block_size().to_used_value(containing_length); } } } diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index c4dfefcc696..c0991ea2535 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -26,7 +26,7 @@ use ipc_channel::ipc::IpcSender; use layout_debug; use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, SizeConstraint}; use model::{style_length, ToGfxMatrix}; -use msg::constellation_msg::{FrameId, PipelineId}; +use msg::constellation_msg::{BrowsingContextId, PipelineId}; use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder}; use range::*; @@ -472,7 +472,7 @@ impl ImageFragmentInfo { #[derive(Clone)] pub struct IframeFragmentInfo { /// The frame ID of this iframe. - pub frame_id: FrameId, + pub browsing_context_id: BrowsingContextId, /// The pipelineID of this iframe. pub pipeline_id: PipelineId, } @@ -480,10 +480,10 @@ pub struct IframeFragmentInfo { impl IframeFragmentInfo { /// Creates the information specific to an iframe fragment. pub fn new<N: ThreadSafeLayoutNode>(node: &N) -> IframeFragmentInfo { - let frame_id = node.iframe_frame_id(); + let browsing_context_id = node.iframe_browsing_context_id(); let pipeline_id = node.iframe_pipeline_id(); IframeFragmentInfo { - frame_id: frame_id, + browsing_context_id: browsing_context_id, pipeline_id: pipeline_id, } } @@ -907,8 +907,8 @@ impl Fragment { // cascading. let padding = if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_PADDING) { let padding = style.logical_padding(); - (model::specified(padding.inline_start, Au(0)) + - model::specified(padding.inline_end, Au(0))) + (padding.inline_start.to_used_value(Au(0)) + + padding.inline_end.to_used_value(Au(0))) } else { Au(0) }; @@ -935,8 +935,8 @@ impl Fragment { if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED) { specified = MaybeAuto::from_style(style.content_inline_size(), Au(0)).specified_or_zero(); - specified = max(model::specified(style.min_inline_size(), Au(0)), specified); - if let Some(max) = model::specified_or_none(style.max_inline_size(), Au(0)) { + specified = max(style.min_inline_size().to_used_value(Au(0)), specified); + if let Some(max) = style.max_inline_size().to_used_value(Au(0)) { specified = min(specified, max) } @@ -1159,10 +1159,10 @@ impl Fragment { let border_width = self.border_width(); SpeculatedInlineContentEdgeOffsets { start: MaybeAuto::from_style(logical_margin.inline_start, Au(0)).specified_or_zero() + - model::specified(logical_padding.inline_start, Au(0)) + + logical_padding.inline_start.to_used_value(Au(0)) + border_width.inline_start, end: MaybeAuto::from_style(logical_margin.inline_end, Au(0)).specified_or_zero() + - model::specified(logical_padding.inline_end, Au(0)) + + logical_padding.inline_end.to_used_value(Au(0)) + border_width.inline_end, } } @@ -1491,10 +1491,10 @@ impl Fragment { // the size constraints work properly. // TODO(stshine): Find a cleaner way to do this. let padding = self.style.logical_padding(); - self.border_padding.inline_start = model::specified(padding.inline_start, Au(0)); - self.border_padding.inline_end = model::specified(padding.inline_end, Au(0)); - self.border_padding.block_start = model::specified(padding.block_start, Au(0)); - self.border_padding.block_end = model::specified(padding.block_end, Au(0)); + self.border_padding.inline_start = padding.inline_start.to_used_value(Au(0)); + self.border_padding.inline_end = padding.inline_end.to_used_value(Au(0)); + self.border_padding.block_start = padding.block_start.to_used_value(Au(0)); + self.border_padding.block_end = padding.block_end.to_used_value(Au(0)); let border = self.border_width(); self.border_padding.inline_start += border.inline_start; self.border_padding.inline_end += border.inline_end; @@ -1504,7 +1504,11 @@ impl Fragment { result_inline } LengthOrPercentageOrAuto::Length(length) => length, - LengthOrPercentageOrAuto::Calc(calc) => calc.length(), + LengthOrPercentageOrAuto::Calc(calc) => { + // TODO(nox): This is probably wrong, because it accounts neither for + // clamping (not sure if necessary here) nor percentage. + calc.unclamped_length() + }, }; let size_constraint = self.size_constraint(None, Direction::Inline); @@ -2233,8 +2237,7 @@ impl Fragment { offset -= minimum_line_metrics.space_needed().scale_by(percentage) } vertical_align::T::LengthOrPercentage(LengthOrPercentage::Calc(formula)) => { - offset -= minimum_line_metrics.space_needed().scale_by(formula.percentage()) + - formula.length() + offset -= formula.to_used_value(Some(minimum_line_metrics.space_needed())).unwrap() } } } @@ -2470,7 +2473,10 @@ impl Fragment { if self.style().get_effects().mix_blend_mode != mix_blend_mode::T::normal { return true } - if self.style().get_box().transform.0.is_some() { + + if self.style().get_box().transform.0.is_some() || + self.style().get_box().transform_style == transform_style::T::preserve_3d || + self.style().overrides_transform_style() { return true } @@ -2485,13 +2491,6 @@ impl Fragment { return true } - match self.style().get_used_transform_style() { - transform_style::T::flat | transform_style::T::preserve_3d => { - return true - } - transform_style::T::auto => {} - } - match (self.style().get_box().position, self.style().get_position().z_index, self.style().get_box().overflow_x, @@ -2851,12 +2850,14 @@ impl Fragment { let mut transform = Matrix4D::identity(); let transform_origin = &self.style.get_box().transform_origin; - let transform_origin_x = model::specified(transform_origin.horizontal, - stacking_relative_border_box.size - .width).to_f32_px(); - let transform_origin_y = model::specified(transform_origin.vertical, - stacking_relative_border_box.size - .height).to_f32_px(); + let transform_origin_x = + transform_origin.horizontal + .to_used_value(stacking_relative_border_box.size.width) + .to_f32_px(); + let transform_origin_y = + transform_origin.vertical + .to_used_value(stacking_relative_border_box.size.height) + .to_f32_px(); let transform_origin_z = transform_origin.depth.to_f32_px(); let pre_transform = Matrix4D::create_translation(transform_origin_x, @@ -2879,10 +2880,8 @@ impl Fragment { Matrix4D::create_scale(sx, sy, sz) } transform::ComputedOperation::Translate(tx, ty, tz) => { - let tx = - model::specified(tx, stacking_relative_border_box.size.width).to_f32_px(); - let ty = - model::specified(ty, stacking_relative_border_box.size.height).to_f32_px(); + let tx = tx.to_used_value(stacking_relative_border_box.size.width).to_f32_px(); + let ty = ty.to_used_value(stacking_relative_border_box.size.height).to_f32_px(); let tz = tz.to_f32_px(); Matrix4D::create_translation(tx, ty, tz) } @@ -2911,10 +2910,13 @@ impl Fragment { Either::First(length) => { let perspective_origin = self.style().get_box().perspective_origin; let perspective_origin = - Point2D::new(model::specified(perspective_origin.horizontal, - stacking_relative_border_box.size.width).to_f32_px(), - model::specified(perspective_origin.vertical, - stacking_relative_border_box.size.height).to_f32_px()); + Point2D::new( + perspective_origin.horizontal + .to_used_value(stacking_relative_border_box.size.width) + .to_f32_px(), + perspective_origin.vertical + .to_used_value(stacking_relative_border_box.size.height) + .to_f32_px()); let pre_transform = Matrix4D::create_translation(perspective_origin.x, perspective_origin.y, diff --git a/components/layout/model.rs b/components/layout/model.rs index aba7c3f3927..a762c1d59c8 100644 --- a/components/layout/model.rs +++ b/components/layout/model.rs @@ -412,7 +412,7 @@ impl MaybeAuto { MaybeAuto::Specified(containing_length.scale_by(percent)) } LengthOrPercentageOrAuto::Calc(calc) => { - MaybeAuto::from_option(calc.to_computed(Some(containing_length))) + MaybeAuto::from_option(calc.to_used_value(Some(containing_length))) } LengthOrPercentageOrAuto::Length(length) => MaybeAuto::Specified(length) } @@ -463,24 +463,6 @@ pub fn style_length(style_length: LengthOrPercentageOrAuto, } } -pub fn specified_or_none(length: LengthOrPercentageOrNone, containing_length: Au) -> Option<Au> { - match length { - LengthOrPercentageOrNone::None => None, - LengthOrPercentageOrNone::Percentage(percent) => Some(containing_length.scale_by(percent)), - LengthOrPercentageOrNone::Calc(calc) => calc.to_computed(Some(containing_length)), - LengthOrPercentageOrNone::Length(length) => Some(length), - } -} - -pub fn specified(length: LengthOrPercentage, containing_length: Au) -> Au { - match length { - LengthOrPercentage::Length(length) => length, - LengthOrPercentage::Percentage(p) => containing_length.scale_by(p), - LengthOrPercentage::Calc(calc) => - containing_length.scale_by(calc.percentage()) + calc.length(), - } -} - /// Computes a border radius size against the containing size. /// /// Note that percentages in `border-radius` are resolved against the relevant @@ -495,8 +477,8 @@ pub fn specified_border_radius( -> Size2D<Au> { let generics::BorderRadiusSize(size) = radius; - let w = specified(size.width, containing_size.width); - let h = specified(size.height, containing_size.height); + let w = size.width.to_used_value(containing_size.width); + let h = size.height.to_used_value(containing_size.height); Size2D::new(w, h) } @@ -507,10 +489,10 @@ pub fn padding_from_style(style: &ServoComputedValues, -> LogicalMargin<Au> { let padding_style = style.get_padding(); LogicalMargin::from_physical(writing_mode, SideOffsets2D::new( - specified(padding_style.padding_top, containing_block_inline_size), - specified(padding_style.padding_right, containing_block_inline_size), - specified(padding_style.padding_bottom, containing_block_inline_size), - specified(padding_style.padding_left, containing_block_inline_size))) + padding_style.padding_top.to_used_value(containing_block_inline_size), + padding_style.padding_right.to_used_value(containing_block_inline_size), + padding_style.padding_bottom.to_used_value(containing_block_inline_size), + padding_style.padding_left.to_used_value(containing_block_inline_size))) } /// Returns the explicitly-specified margin lengths from the given style. Percentage and auto @@ -559,7 +541,7 @@ impl SizeConstraint { max_size: LengthOrPercentageOrNone, border: Option<Au>) -> SizeConstraint { let mut min_size = match container_size { - Some(container_size) => specified(min_size, container_size), + Some(container_size) => min_size.to_used_value(container_size), None => if let LengthOrPercentage::Length(length) = min_size { length } else { @@ -568,7 +550,7 @@ impl SizeConstraint { }; let mut max_size = match container_size { - Some(container_size) => specified_or_none(max_size, container_size), + Some(container_size) => max_size.to_used_value(container_size), None => if let LengthOrPercentageOrNone::Length(length) = max_size { Some(length) } else { diff --git a/components/layout/webrender_helpers.rs b/components/layout/webrender_helpers.rs index ca7cdb27a3f..537d5f04267 100644 --- a/components/layout/webrender_helpers.rs +++ b/components/layout/webrender_helpers.rs @@ -12,7 +12,7 @@ use euclid::{Point2D, Rect, SideOffsets2D, Size2D}; use gfx::display_list::{BorderDetails, BorderRadii, BoxShadowClipMode, ClippingRegion}; use gfx::display_list::{DisplayItem, DisplayList, DisplayListTraversal, StackingContextType}; use msg::constellation_msg::PipelineId; -use style::computed_values::{image_rendering, mix_blend_mode}; +use style::computed_values::{image_rendering, mix_blend_mode, transform_style}; use style::computed_values::filter::{self, Filter}; use style::values::computed::BorderStyle; use webrender_traits::{self, DisplayListBuilder, ExtendMode}; @@ -146,12 +146,12 @@ impl ToBorderRadius for BorderRadii<Au> { } } -trait ToBlendMode { - fn to_blend_mode(&self) -> webrender_traits::MixBlendMode; +pub trait ToMixBlendMode { + fn to_mix_blend_mode(&self) -> webrender_traits::MixBlendMode; } -impl ToBlendMode for mix_blend_mode::T { - fn to_blend_mode(&self) -> webrender_traits::MixBlendMode { +impl ToMixBlendMode for mix_blend_mode::T { + fn to_mix_blend_mode(&self) -> webrender_traits::MixBlendMode { match *self { mix_blend_mode::T::normal => webrender_traits::MixBlendMode::Normal, mix_blend_mode::T::multiply => webrender_traits::MixBlendMode::Multiply, @@ -211,6 +211,19 @@ impl ToFilterOps for filter::T { } } +pub trait ToTransformStyle { + fn to_transform_style(&self) -> webrender_traits::TransformStyle; +} + +impl ToTransformStyle for transform_style::T { + fn to_transform_style(&self) -> webrender_traits::TransformStyle { + match *self { + transform_style::T::auto | transform_style::T::flat => webrender_traits::TransformStyle::Flat, + transform_style::T::preserve_3d => webrender_traits::TransformStyle::Preserve3D, + } + } +} + impl WebRenderDisplayListConverter for DisplayList { fn convert_to_webrender(&self, pipeline_id: PipelineId) -> DisplayListBuilder { let traversal = DisplayListTraversal::new(self); @@ -464,9 +477,9 @@ impl WebRenderDisplayItemConverter for DisplayItem { builder.push_stacking_context(stacking_context.scroll_policy, stacking_context.bounds.to_rectf(), transform, - webrender_traits::TransformStyle::Flat, + stacking_context.transform_style, perspective, - stacking_context.blend_mode.to_blend_mode(), + stacking_context.mix_blend_mode, stacking_context.filters.to_filter_ops()); } DisplayItem::PopStackingContext(_) => builder.pop_stacking_context(), diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index eed95ecb9ea..8185aa6e409 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -74,7 +74,7 @@ use layout::webrender_helpers::WebRenderDisplayListConverter; use layout::wrapper::LayoutNodeLayoutData; use layout::wrapper::drop_style_and_layout_data; use layout_traits::LayoutThreadFactory; -use msg::constellation_msg::{FrameId, PipelineId}; +use msg::constellation_msg::{BrowsingContextId, PipelineId}; use net_traits::image_cache::{ImageCache, UsePlaceholder}; use parking_lot::RwLock; use profile_traits::mem::{self, Report, ReportKind, ReportsChan}; @@ -244,7 +244,7 @@ impl LayoutThreadFactory for LayoutThread { /// Spawns a new layout thread. fn create(id: PipelineId, - top_level_frame_id: Option<FrameId>, + top_level_browsing_context_id: Option<BrowsingContextId>, url: ServoUrl, is_iframe: bool, chan: (Sender<Msg>, Receiver<Msg>), @@ -261,8 +261,8 @@ impl LayoutThreadFactory for LayoutThread { thread::Builder::new().name(format!("LayoutThread {:?}", id)).spawn(move || { thread_state::initialize(thread_state::LAYOUT); - if let Some(top_level_frame_id) = top_level_frame_id { - FrameId::install(top_level_frame_id); + if let Some(top_level_browsing_context_id) = top_level_browsing_context_id { + BrowsingContextId::install(top_level_browsing_context_id); } { // Ensures layout thread is destroyed before we send shutdown message @@ -732,7 +732,7 @@ impl LayoutThread { fn create_layout_thread(&self, info: NewLayoutThreadInfo) { LayoutThread::create(info.id, - FrameId::installed(), + BrowsingContextId::installed(), info.url.clone(), info.is_parent, info.layout_pair, @@ -930,7 +930,7 @@ impl LayoutThread { // build_state.iframe_sizes is only used here, so its okay to replace // it with an empty vector let iframe_sizes = std::mem::replace(&mut build_state.iframe_sizes, vec![]); - let msg = ConstellationMsg::FrameSizes(iframe_sizes); + let msg = ConstellationMsg::IFrameSizes(iframe_sizes); if let Err(e) = self.constellation_chan.send(msg) { warn!("Layout resize to constellation failed ({}).", e); } diff --git a/components/layout_traits/lib.rs b/components/layout_traits/lib.rs index 9d97a89a53c..53aa2529cb0 100644 --- a/components/layout_traits/lib.rs +++ b/components/layout_traits/lib.rs @@ -20,7 +20,7 @@ extern crate webrender_traits; use gfx::font_cache_thread::FontCacheThread; use ipc_channel::ipc::{IpcReceiver, IpcSender}; -use msg::constellation_msg::{FrameId, PipelineId}; +use msg::constellation_msg::{BrowsingContextId, PipelineId}; use net_traits::image_cache::ImageCache; use profile_traits::{mem, time}; use script_traits::{ConstellationControlMsg, LayoutControlMsg}; @@ -34,7 +34,7 @@ use std::sync::mpsc::{Receiver, Sender}; pub trait LayoutThreadFactory { type Message; fn create(id: PipelineId, - top_level_frame_id: Option<FrameId>, + top_level_browsing_context_id: Option<BrowsingContextId>, url: ServoUrl, is_iframe: bool, chan: (Sender<Self::Message>, Receiver<Self::Message>), diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index 02d4157f20c..edec5fb5231 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -207,10 +207,10 @@ impl PipelineNamespace { } } - fn next_frame_id(&mut self) -> FrameId { - FrameId { + fn next_browsing_context_id(&mut self) -> BrowsingContextId { + BrowsingContextId { namespace_id: self.id, - index: FrameIndex(self.next_index()), + index: BrowsingContextIndex(self.next_index()), } } } @@ -258,42 +258,41 @@ impl fmt::Display for PipelineId { } } -thread_local!(pub static TOP_LEVEL_FRAME_ID: Cell<Option<FrameId>> = Cell::new(None)); +thread_local!(pub static TOP_LEVEL_BROWSING_CONTEXT_ID: Cell<Option<BrowsingContextId>> = Cell::new(None)); #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)] -pub struct FrameIndex(pub u32); +pub struct BrowsingContextIndex(pub u32); #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)] -pub struct FrameId { +pub struct BrowsingContextId { pub namespace_id: PipelineNamespaceId, - pub index: FrameIndex + pub index: BrowsingContextIndex } -impl FrameId { - pub fn new() -> FrameId { +impl BrowsingContextId { + pub fn new() -> BrowsingContextId { PIPELINE_NAMESPACE.with(|tls| { let mut namespace = tls.get().expect("No namespace set for this thread!"); - let new_frame_id = namespace.next_frame_id(); + let new_browsing_context_id = namespace.next_browsing_context_id(); tls.set(Some(namespace)); - new_frame_id + new_browsing_context_id }) } - - /// Each script and layout thread should have the top-level frame id installed, + /// Each script and layout thread should have the top-level browsing context id installed, /// since it is used by crash reporting. - pub fn install(id: FrameId) { - TOP_LEVEL_FRAME_ID.with(|tls| tls.set(Some(id))) + pub fn install(id: BrowsingContextId) { + TOP_LEVEL_BROWSING_CONTEXT_ID.with(|tls| tls.set(Some(id))) } - pub fn installed() -> Option<FrameId> { - TOP_LEVEL_FRAME_ID.with(|tls| tls.get()) + pub fn installed() -> Option<BrowsingContextId> { + TOP_LEVEL_BROWSING_CONTEXT_ID.with(|tls| tls.get()) } } -impl fmt::Display for FrameId { +impl fmt::Display for BrowsingContextId { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let PipelineNamespaceId(namespace_id) = self.namespace_id; - let FrameIndex(index) = self.index; + let BrowsingContextIndex(index) = self.index; write!(fmt, "({},{})", namespace_id, index) } } @@ -302,8 +301,9 @@ impl fmt::Display for FrameId { pub const TEST_NAMESPACE: PipelineNamespaceId = PipelineNamespaceId(1234); pub const TEST_PIPELINE_INDEX: PipelineIndex = PipelineIndex(5678); pub const TEST_PIPELINE_ID: PipelineId = PipelineId { namespace_id: TEST_NAMESPACE, index: TEST_PIPELINE_INDEX }; -pub const TEST_FRAME_INDEX: FrameIndex = FrameIndex(8765); -pub const TEST_FRAME_ID: FrameId = FrameId { namespace_id: TEST_NAMESPACE, index: TEST_FRAME_INDEX }; +pub const TEST_BROWSING_CONTEXT_INDEX: BrowsingContextIndex = BrowsingContextIndex(8765); +pub const TEST_BROWSING_CONTEXT_ID: BrowsingContextId = + BrowsingContextId { namespace_id: TEST_NAMESPACE, index: TEST_BROWSING_CONTEXT_INDEX }; #[derive(Clone, PartialEq, Eq, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)] pub enum FrameType { diff --git a/components/profile/time.rs b/components/profile/time.rs index 928eca4a263..dfe5c37068d 100644 --- a/components/profile/time.rs +++ b/components/profile/time.rs @@ -150,6 +150,7 @@ impl Formattable for ProfilerCategory { ProfilerCategory::ScriptEnterFullscreen => "Script Enter Fullscreen", ProfilerCategory::ScriptExitFullscreen => "Script Exit Fullscreen", ProfilerCategory::ScriptWebVREvent => "Script WebVR Event", + ProfilerCategory::ScriptWorkletEvent => "Script Worklet Event", ProfilerCategory::ApplicationHeartbeat => "Application Heartbeat", }; format!("{}{}", padding, name) diff --git a/components/profile_traits/time.rs b/components/profile_traits/time.rs index 7bbebf5f465..369c2fa366d 100644 --- a/components/profile_traits/time.rs +++ b/components/profile_traits/time.rs @@ -89,6 +89,7 @@ pub enum ProfilerCategory { ScriptEnterFullscreen = 0x77, ScriptExitFullscreen = 0x78, ScriptWebVREvent = 0x79, + ScriptWorkletEvent = 0x7a, ApplicationHeartbeat = 0x90, } diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index 306dd2a748d..388801aa011 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -83,6 +83,7 @@ servo_url = {path = "../url"} smallvec = "0.3" style = {path = "../style"} style_traits = {path = "../style_traits"} +swapper = "0.0.4" time = "0.1.12" unicode-segmentation = "1.1.0" url = {version = "1.2", features = ["heap_size", "query_encoding"]} diff --git a/components/script/dom/attr.rs b/components/script/dom/attr.rs index 3c775ffdf57..af5f0107ae4 100644 --- a/components/script/dom/attr.rs +++ b/components/script/dom/attr.rs @@ -10,6 +10,8 @@ use dom::bindings::js::{LayoutJS, MutNullableJS, Root, RootedReference}; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::element::{AttributeMutation, Element}; +use dom::mutationobserver::{Mutation, MutationObserver}; +use dom::node::Node; use dom::virtualmethods::vtable_for; use dom::window::Window; use dom_struct::dom_struct; @@ -170,6 +172,12 @@ impl AttrMethods for Attr { impl Attr { pub fn set_value(&self, mut value: AttrValue, owner: &Element) { + let name = self.local_name().clone(); + let namespace = self.namespace().clone(); + let old_value = DOMString::from(&**self.value()); + let mutation = Mutation::Attribute { name, namespace, old_value }; + MutationObserver::queue_a_mutation_record(owner.upcast::<Node>(), mutation); + assert!(Some(owner) == self.owner().r()); owner.will_mutate_attr(self); self.swap_value(&mut value); diff --git a/components/script/dom/bindings/refcounted.rs b/components/script/dom/bindings/refcounted.rs index dcf11626543..f22074d5fef 100644 --- a/components/script/dom/bindings/refcounted.rs +++ b/components/script/dom/bindings/refcounted.rs @@ -23,12 +23,17 @@ //! as JS roots. use core::nonzero::NonZero; +use dom::bindings::conversions::ToJSValConvertible; +use dom::bindings::error::Error; use dom::bindings::js::Root; use dom::bindings::reflector::{DomObject, Reflector}; use dom::bindings::trace::trace_reflector; use dom::promise::Promise; +use js::jsapi::JSAutoCompartment; use js::jsapi::JSTracer; use libc; +use script_thread::Runnable; +use script_thread::ScriptThread; use std::cell::RefCell; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::hash_map::HashMap; @@ -115,6 +120,40 @@ impl TrustedPromise { promise }) } + + /// A runnable which will reject the promise. + #[allow(unrooted_must_root)] + pub fn reject_runnable(self, error: Error) -> impl Runnable + Send { + struct RejectPromise(TrustedPromise, Error); + impl Runnable for RejectPromise { + fn main_thread_handler(self: Box<Self>, script_thread: &ScriptThread) { + let this = *self; + let cx = script_thread.get_cx(); + let promise = this.0.root(); + let _ac = JSAutoCompartment::new(cx, promise.reflector().get_jsobject().get()); + promise.reject_error(cx, this.1); + } + } + RejectPromise(self, error) + } + + /// A runnable which will resolve the promise. + #[allow(unrooted_must_root)] + pub fn resolve_runnable<T>(self, value: T) -> impl Runnable + Send where + T: ToJSValConvertible + Send + { + struct ResolvePromise<T>(TrustedPromise, T); + impl<T: ToJSValConvertible> Runnable for ResolvePromise<T> { + fn main_thread_handler(self: Box<Self>, script_thread: &ScriptThread) { + let this = *self; + let cx = script_thread.get_cx(); + let promise = this.0.root(); + let _ac = JSAutoCompartment::new(cx, promise.reflector().get_jsobject().get()); + promise.resolve_native(cx, &this.1); + } + } + ResolvePromise(self, value) + } } /// A safe wrapper around a raw pointer to a DOM object that can be diff --git a/components/script/dom/bindings/structuredclone.rs b/components/script/dom/bindings/structuredclone.rs index b3a9df32695..53f3b63e085 100644 --- a/components/script/dom/bindings/structuredclone.rs +++ b/components/script/dom/bindings/structuredclone.rs @@ -67,16 +67,44 @@ unsafe fn read_length(r: *mut JSStructuredCloneReader) return length as usize; } +struct StructuredCloneWriter { + w: *mut JSStructuredCloneWriter, +} + +impl StructuredCloneWriter { + unsafe fn write_slice(&self, v: &[u8]) { + let type_length = v.len(); + write_length(self.w, type_length); + assert!(JS_WriteBytes(self.w, v.as_ptr() as *const raw::c_void, type_length)); + } + unsafe fn write_str(&self, s: &str) { + self.write_slice(s.as_bytes()); + } +} + +struct StructuredCloneReader { + r: *mut JSStructuredCloneReader, +} + +impl StructuredCloneReader { + unsafe fn read_bytes(&self) -> Vec<u8> { + let mut bytes = vec![0u8; read_length(self.r)]; + let blob_length = bytes.len(); + assert!(JS_ReadBytes(self.r, bytes.as_mut_ptr() as *mut raw::c_void, blob_length)); + return bytes; + } + unsafe fn read_str(&self) -> String { + let str_buffer = self.read_bytes(); + return String::from_utf8_unchecked(str_buffer); + } +} + unsafe fn read_blob(cx: *mut JSContext, r: *mut JSStructuredCloneReader) -> *mut JSObject { - let blob_length = read_length(r); - let type_str_length = read_length(r); - let mut blob_buffer = vec![0u8; blob_length]; - assert!(JS_ReadBytes(r, blob_buffer.as_mut_ptr() as *mut raw::c_void, blob_length)); - let mut type_str_buffer = vec![0u8; type_str_length]; - assert!(JS_ReadBytes(r, type_str_buffer.as_mut_ptr() as *mut raw::c_void, type_str_length)); - let type_str = String::from_utf8_unchecked(type_str_buffer); + let structured_reader = StructuredCloneReader { r: r }; + let blob_buffer = structured_reader.read_bytes(); + let type_str = structured_reader.read_str(); let target_global = GlobalScope::from_context(cx); let blob = Blob::new(&target_global, BlobImpl::new_from_bytes(blob_buffer), type_str); return blob.reflector().get_jsobject().get() @@ -85,15 +113,11 @@ unsafe fn read_blob(cx: *mut JSContext, unsafe fn write_blob(blob: Root<Blob>, w: *mut JSStructuredCloneWriter) -> Result<(), ()> { + let structured_writer = StructuredCloneWriter { w: w }; let blob_vec = try!(blob.get_bytes()); - let blob_length = blob_vec.len(); - let type_string_bytes = blob.type_string().as_bytes().to_vec(); - let type_string_length = type_string_bytes.len(); assert!(JS_WriteUint32Pair(w, StructuredCloneTags::DomBlob as u32, 0)); - write_length(w, blob_length); - write_length(w, type_string_length); - assert!(JS_WriteBytes(w, blob_vec.as_ptr() as *const raw::c_void, blob_length)); - assert!(JS_WriteBytes(w, type_string_bytes.as_ptr() as *const raw::c_void, type_string_length)); + structured_writer.write_slice(&blob_vec); + structured_writer.write_str(&blob.type_string()); return Ok(()) } diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index a0f26d12c23..2b3bfceceb2 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -58,7 +58,7 @@ use js::glue::{CallObjectTracer, CallValueTracer}; use js::jsapi::{GCTraceKindToAscii, Heap, JSObject, JSTracer, TraceKind}; use js::jsval::JSVal; use js::rust::Runtime; -use msg::constellation_msg::{FrameId, FrameType, PipelineId}; +use msg::constellation_msg::{BrowsingContextId, FrameType, PipelineId}; use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads}; use net_traits::filemanager_thread::RelativePos; use net_traits::image::base::{Image, ImageMetadata}; @@ -336,7 +336,7 @@ unsafe_no_jsmanaged_fields!(TrustedPromise); unsafe_no_jsmanaged_fields!(PropertyDeclarationBlock); // These three are interdependent, if you plan to put jsmanaged data // in one of these make sure it is propagated properly to containing structs -unsafe_no_jsmanaged_fields!(DocumentActivity, FrameId, FrameType, WindowSizeData, WindowSizeType, PipelineId); +unsafe_no_jsmanaged_fields!(DocumentActivity, BrowsingContextId, FrameType, WindowSizeData, WindowSizeType, PipelineId); unsafe_no_jsmanaged_fields!(TimerEventId, TimerSource); unsafe_no_jsmanaged_fields!(TimelineMarkerType); unsafe_no_jsmanaged_fields!(WorkerId); diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index 14dd070f44d..6b73df12dfc 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -27,7 +27,7 @@ use js::jsapi::{HandleValue, JS_SetInterruptCallback}; use js::jsapi::{JSAutoCompartment, JSContext}; use js::jsval::UndefinedValue; use js::rust::Runtime; -use msg::constellation_msg::FrameId; +use msg::constellation_msg::BrowsingContextId; use net_traits::{IpcSend, load_whole_resource}; use net_traits::request::{CredentialsMode, Destination, RequestInit, Type as RequestType}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, StackRootTLS, get_reports, new_rt_and_cx}; @@ -159,13 +159,13 @@ impl DedicatedWorkerGlobalScope { closing: Arc<AtomicBool>) { let serialized_worker_url = worker_url.to_string(); let name = format!("WebWorker for {}", serialized_worker_url); - let top_level_frame_id = FrameId::installed(); + let top_level_browsing_context_id = BrowsingContextId::installed(); thread::Builder::new().name(name).spawn(move || { thread_state::initialize(thread_state::SCRIPT | thread_state::IN_WORKER); - if let Some(top_level_frame_id) = top_level_frame_id { - FrameId::install(top_level_frame_id); + if let Some(top_level_browsing_context_id) = top_level_browsing_context_id { + BrowsingContextId::install(top_level_browsing_context_id); } let roots = RootCollection::new(); diff --git a/components/script/dom/dissimilaroriginwindow.rs b/components/script/dom/dissimilaroriginwindow.rs index 7d5bef36344..a4e230e90a2 100644 --- a/components/script/dom/dissimilaroriginwindow.rs +++ b/components/script/dom/dissimilaroriginwindow.rs @@ -184,7 +184,9 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow { impl DissimilarOriginWindow { pub fn post_message(&self, origin: Option<ImmutableOrigin>, data: StructuredCloneData) { - let msg = ConstellationMsg::PostMessage(self.window_proxy.frame_id(), origin, data.move_to_arraybuffer()); + let msg = ConstellationMsg::PostMessage(self.window_proxy.browsing_context_id(), + origin, + data.move_to_arraybuffer()); let _ = self.upcast::<GlobalScope>().constellation_chan().send(msg); } } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index e44de8be727..81ea073ab41 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -100,7 +100,7 @@ use ipc_channel::ipc::{self, IpcSender}; use js::jsapi::{JSContext, JSObject, JSRuntime}; use js::jsapi::JS_GetRuntime; use msg::constellation_msg::{ALT, CONTROL, SHIFT, SUPER}; -use msg::constellation_msg::{FrameId, Key, KeyModifiers, KeyState}; +use msg::constellation_msg::{BrowsingContextId, Key, KeyModifiers, KeyState}; use net_traits::{FetchResponseMsg, IpcSend, ReferrerPolicy}; use net_traits::CookieSource::NonHTTP; use net_traits::CoreResourceMsg::{GetCookiesForUrl, SetCookiesForUrl}; @@ -1897,9 +1897,9 @@ impl Document { } /// Find an iframe element in the document. - pub fn find_iframe(&self, frame_id: FrameId) -> Option<Root<HTMLIFrameElement>> { + pub fn find_iframe(&self, browsing_context_id: BrowsingContextId) -> Option<Root<HTMLIFrameElement>> { self.iter_iframes() - .find(|node| node.frame_id() == frame_id) + .find(|node| node.browsing_context_id() == browsing_context_id) } pub fn get_dom_loading(&self) -> u64 { diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index aa32370127c..55175fd45c6 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -63,6 +63,7 @@ use dom::htmltablerowelement::{HTMLTableRowElement, HTMLTableRowElementLayoutHel use dom::htmltablesectionelement::{HTMLTableSectionElement, HTMLTableSectionElementLayoutHelpers}; use dom::htmltemplateelement::HTMLTemplateElement; use dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers}; +use dom::mutationobserver::{Mutation, MutationObserver}; use dom::namednodemap::NamedNodeMap; use dom::node::{CLICK_IN_PROGRESS, ChildrenMutation, LayoutNodeHelpers, Node}; use dom::node::{NodeDamage, SEQUENTIALLY_FOCUSABLE, UnbindContext}; @@ -85,9 +86,9 @@ use net_traits::request::CorsSettings; use ref_filter_map::ref_filter_map; use script_layout_interface::message::ReflowQueryType; use script_thread::Runnable; -use selectors::matching::{ElementSelectorFlags, StyleRelations, matches_selector_list}; +use selectors::attr::{AttrSelectorOperation, NamespaceConstraint}; +use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, matches_selector_list}; use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS}; -use selectors::parser::{AttrSelector, NamespaceConstraint}; use servo_atoms::Atom; use std::ascii::AsciiExt; use std::borrow::Cow; @@ -103,7 +104,7 @@ use style::properties::{Importance, PropertyDeclaration, PropertyDeclarationBloc use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size, overflow_x}; use style::restyle_hints::RESTYLE_SELF; use style::rule_tree::CascadeLevel; -use style::selector_parser::{NonTSPseudoClass, RestyleDamage, SelectorImpl, SelectorParser}; +use style::selector_parser::{NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser}; use style::shared_lock::{SharedRwLock, Locked}; use style::sink::Push; use style::stylearc::Arc; @@ -287,7 +288,7 @@ pub trait RawLayoutElementHelpers { -> Option<&'a AttrValue>; unsafe fn get_attr_val_for_layout<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str>; - unsafe fn get_attr_vals_for_layout<'a>(&'a self, name: &LocalName) -> Vec<&'a str>; + unsafe fn get_attr_vals_for_layout<'a>(&'a self, name: &LocalName) -> Vec<&'a AttrValue>; } #[inline] @@ -313,6 +314,7 @@ impl RawLayoutElementHelpers for Element { }) } + #[inline] unsafe fn get_attr_val_for_layout<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str> { get_attr_for_layout(self, namespace, name).map(|attr| { @@ -321,12 +323,12 @@ impl RawLayoutElementHelpers for Element { } #[inline] - unsafe fn get_attr_vals_for_layout<'a>(&'a self, name: &LocalName) -> Vec<&'a str> { + unsafe fn get_attr_vals_for_layout<'a>(&'a self, name: &LocalName) -> Vec<&'a AttrValue> { let attrs = self.attrs.borrow_for_layout(); attrs.iter().filter_map(|attr| { let attr = attr.to_layout(); if *name == attr.local_name_atom_forever() { - Some(attr.value_ref_forever()) + Some(attr.value_forever()) } else { None } @@ -1003,6 +1005,12 @@ impl Element { } pub fn push_attribute(&self, attr: &Attr) { + let name = attr.local_name().clone(); + let namespace = attr.namespace().clone(); + let old_value = DOMString::from(&**attr.value()); + let mutation = Mutation::Attribute { name, namespace, old_value }; + MutationObserver::queue_a_mutation_record(&self.node, mutation); + assert!(attr.GetOwnerElement().r() == Some(self)); self.will_mutate_attr(attr); self.attrs.borrow_mut().push(JS::from_ref(attr)); @@ -1125,13 +1133,18 @@ impl Element { } fn remove_first_matching_attribute<F>(&self, find: F) -> Option<Root<Attr>> - where F: Fn(&Attr) -> bool - { + where F: Fn(&Attr) -> bool { let idx = self.attrs.borrow().iter().position(|attr| find(&attr)); - idx.map(|idx| { let attr = Root::from_ref(&*(*self.attrs.borrow())[idx]); self.will_mutate_attr(&attr); + + let name = attr.local_name().clone(); + let namespace = attr.namespace().clone(); + let old_value = DOMString::from(&**attr.value()); + let mutation = Mutation::Attribute { name, namespace, old_value, }; + MutationObserver::queue_a_mutation_record(&self.node, mutation); + self.attrs.borrow_mut().remove(idx); attr.set_owner(None); if attr.namespace() == &ns!() { @@ -2046,7 +2059,8 @@ impl ElementMethods for Element { match SelectorParser::parse_author_origin_no_namespace(&selectors) { Err(()) => Err(Error::Syntax), Ok(selectors) => { - Ok(matches_selector_list(&selectors.0, &Root::from_ref(self), None)) + let mut ctx = MatchingContext::new(MatchingMode::Normal, None); + Ok(matches_selector_list(&selectors.0, &Root::from_ref(self), &mut ctx)) } } } @@ -2064,8 +2078,8 @@ impl ElementMethods for Element { let root = self.upcast::<Node>(); for element in root.inclusive_ancestors() { if let Some(element) = Root::downcast::<Element>(element) { - if matches_selector_list(&selectors.0, &element, None) - { + let mut ctx = MatchingContext::new(MatchingMode::Normal, None); + if matches_selector_list(&selectors.0, &element, &mut ctx) { return Ok(Some(element)); } } @@ -2339,41 +2353,22 @@ impl VirtualMethods for Element { } } -impl<'a> ::selectors::MatchAttrGeneric for Root<Element> { +impl<'a> ::selectors::Element for Root<Element> { type Impl = SelectorImpl; - fn match_attr<F>(&self, attr: &AttrSelector<SelectorImpl>, test: F) -> bool - where F: Fn(&str) -> bool - { - use ::selectors::Element; - let local_name = { - if self.is_html_element_in_html_document() { - &attr.lower_name - } else { - &attr.name - } - }; - match attr.namespace { - NamespaceConstraint::Specific(ref ns) => { - self.get_attribute(&ns.url, local_name) - .map_or(false, |attr| { - test(&attr.value()) - }) - }, - NamespaceConstraint::Any => { - self.attrs.borrow().iter().any(|attr| { - attr.local_name() == local_name && test(&attr.value()) - }) - } - } - } -} - -impl<'a> ::selectors::Element for Root<Element> { fn parent_element(&self) -> Option<Root<Element>> { self.upcast::<Node>().GetParentElement() } + fn match_pseudo_element(&self, + _pseudo: &PseudoElement, + _context: &mut MatchingContext) + -> bool + { + false + } + + fn first_child_element(&self) -> Option<Root<Element>> { self.node.child_elements().next() } @@ -2390,6 +2385,25 @@ impl<'a> ::selectors::Element for Root<Element> { self.node.following_siblings().filter_map(Root::downcast).next() } + fn attr_matches(&self, + ns: &NamespaceConstraint<&Namespace>, + local_name: &LocalName, + operation: &AttrSelectorOperation<&String>) + -> bool { + match *ns { + NamespaceConstraint::Specific(ref ns) => { + self.get_attribute(ns, local_name) + .map_or(false, |attr| attr.value().eval_selector(operation)) + } + NamespaceConstraint::Any => { + self.attrs.borrow().iter().any(|attr| { + attr.local_name() == local_name && + attr.value().eval_selector(operation) + }) + } + } + } + fn is_root(&self) -> bool { match self.node.GetParentNode() { None => false, @@ -2414,7 +2428,7 @@ impl<'a> ::selectors::Element for Root<Element> { fn match_non_ts_pseudo_class<F>(&self, pseudo_class: &NonTSPseudoClass, - _: &mut StyleRelations, + _: &mut MatchingContext, _: &mut F) -> bool where F: FnMut(&Self, ElementSelectorFlags), @@ -2437,6 +2451,11 @@ impl<'a> ::selectors::Element for Root<Element> { } }, + NonTSPseudoClass::ServoCaseSensitiveTypeAttr(ref expected_value) => { + self.get_attribute(&ns!(), &local_name!("type")) + .map_or(false, |attr| attr.value().eq(expected_value)) + } + // FIXME(#15746): This is wrong, we need to instead use extended filtering as per RFC4647 // https://tools.ietf.org/html/rfc4647#section-3.3.2 NonTSPseudoClass::Lang(ref lang) => extended_filtering(&*self.get_lang(), &*lang), @@ -2467,18 +2486,6 @@ impl<'a> ::selectors::Element for Root<Element> { Element::has_class(&**self, name) } - fn each_class<F>(&self, mut callback: F) - where F: FnMut(&Atom) - { - if let Some(ref attr) = self.get_attribute(&ns!(), &local_name!("class")) { - let tokens = attr.value(); - let tokens = tokens.as_tokens(); - for token in tokens { - callback(token); - } - } - } - fn is_html_element_in_html_document(&self) -> bool { self.html_element_in_html_document() } diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 931d1f576aa..ce81958a2cb 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -19,6 +19,7 @@ use dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; use dom::eventtarget::EventTarget; use dom::window::Window; use dom::workerglobalscope::WorkerGlobalScope; +use dom::workletglobalscope::WorkletGlobalScope; use dom_struct::dom_struct; use ipc_channel::ipc::IpcSender; use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL}; @@ -259,6 +260,10 @@ impl GlobalScope { // https://html.spec.whatwg.org/multipage/#script-settings-for-workers:api-base-url return worker.get_url().clone(); } + if let Some(worker) = self.downcast::<WorkletGlobalScope>() { + // https://drafts.css-houdini.org/worklets/#script-settings-for-worklets + return worker.base_url(); + } unreachable!(); } @@ -270,6 +275,10 @@ impl GlobalScope { if let Some(worker) = self.downcast::<WorkerGlobalScope>() { return worker.get_url().clone(); } + if let Some(worker) = self.downcast::<WorkletGlobalScope>() { + // TODO: is this the right URL to return? + return worker.base_url(); + } unreachable!(); } @@ -349,14 +358,14 @@ impl GlobalScope { /// Evaluate JS code on this global scope. pub fn evaluate_js_on_global_with_result( - &self, code: &str, rval: MutableHandleValue) { + &self, code: &str, rval: MutableHandleValue) -> bool { self.evaluate_script_on_global_with_result(code, "", rval, 1) } /// Evaluate a JS script on this global scope. #[allow(unsafe_code)] pub fn evaluate_script_on_global_with_result( - &self, code: &str, filename: &str, rval: MutableHandleValue, line_number: u32) { + &self, code: &str, filename: &str, rval: MutableHandleValue, line_number: u32) -> bool { let metadata = time::TimerMetadata { url: if filename.is_empty() { self.get_url().as_str().into() @@ -379,16 +388,21 @@ impl GlobalScope { let _ac = JSAutoCompartment::new(cx, globalhandle.get()); let _aes = AutoEntryScript::new(self); let options = CompileOptionsWrapper::new(cx, filename.as_ptr(), line_number); - unsafe { - if !Evaluate2(cx, options.ptr, code.as_ptr(), - code.len() as libc::size_t, - rval) { - debug!("error evaluating JS string"); - report_pending_exception(cx, true); - } + + debug!("evaluating JS string"); + let result = unsafe { + Evaluate2(cx, options.ptr, code.as_ptr(), + code.len() as libc::size_t, + rval) + }; + + if !result { + debug!("error evaluating JS string"); + unsafe { report_pending_exception(cx, true) }; } maybe_resume_unwind(); + result } ) } @@ -468,6 +482,9 @@ impl GlobalScope { if let Some(worker) = self.downcast::<WorkerGlobalScope>() { return worker.perform_a_microtask_checkpoint(); } + if let Some(worker) = self.downcast::<WorkletGlobalScope>() { + return worker.perform_a_microtask_checkpoint(); + } unreachable!(); } @@ -479,6 +496,9 @@ impl GlobalScope { if let Some(worker) = self.downcast::<WorkerGlobalScope>() { return worker.enqueue_microtask(job); } + if let Some(worker) = self.downcast::<WorkletGlobalScope>() { + return worker.enqueue_microtask(job); + } unreachable!(); } diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 1e6615ce36a..8334b4cb421 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -40,7 +40,7 @@ use html5ever::{LocalName, Prefix}; use ipc_channel::ipc; use js::jsapi::{JSAutoCompartment, JSContext, MutableHandleValue}; use js::jsval::{NullValue, UndefinedValue}; -use msg::constellation_msg::{FrameType, FrameId, PipelineId, TraversalDirection}; +use msg::constellation_msg::{FrameType, BrowsingContextId, PipelineId, TraversalDirection}; use net_traits::response::HttpsState; use script_layout_interface::message::ReflowQueryType; use script_thread::{ScriptThread, Runnable}; @@ -84,7 +84,7 @@ enum ProcessingMode { #[dom_struct] pub struct HTMLIFrameElement { htmlelement: HTMLElement, - frame_id: FrameId, + browsing_context_id: BrowsingContextId, pipeline_id: Cell<Option<PipelineId>>, pending_pipeline_id: Cell<Option<PipelineId>>, sandbox: MutNullableJS<DOMTokenList>, @@ -115,7 +115,7 @@ impl HTMLIFrameElement { pub fn generate_new_pipeline_id(&self) -> (Option<PipelineId>, PipelineId) { let old_pipeline_id = self.pipeline_id.get(); let new_pipeline_id = PipelineId::new(); - debug!("Frame {} created pipeline {}.", self.frame_id, new_pipeline_id); + debug!("Frame {} created pipeline {}.", self.browsing_context_id, new_pipeline_id); (old_pipeline_id, new_pipeline_id) } @@ -152,7 +152,7 @@ impl HTMLIFrameElement { let global_scope = window.upcast::<GlobalScope>(); let load_info = IFrameLoadInfo { parent_pipeline_id: global_scope.pipeline_id(), - frame_id: self.frame_id, + browsing_context_id: self.browsing_context_id, new_pipeline_id: new_pipeline_id, is_private: private_iframe, frame_type: frame_type, @@ -171,7 +171,7 @@ impl HTMLIFrameElement { let new_layout_info = NewLayoutInfo { parent_info: Some((global_scope.pipeline_id(), frame_type)), new_pipeline_id: new_pipeline_id, - frame_id: self.frame_id, + browsing_context_id: self.browsing_context_id, load_data: load_data.unwrap(), pipeline_port: pipeline_receiver, content_process_shutdown_chan: None, @@ -277,7 +277,7 @@ impl HTMLIFrameElement { document: &Document) -> HTMLIFrameElement { HTMLIFrameElement { htmlelement: HTMLElement::new_inherited(local_name, prefix, document), - frame_id: FrameId::new(), + browsing_context_id: BrowsingContextId::new(), pipeline_id: Cell::new(None), pending_pipeline_id: Cell::new(None), sandbox: Default::default(), @@ -302,8 +302,8 @@ impl HTMLIFrameElement { } #[inline] - pub fn frame_id(&self) -> FrameId { - self.frame_id + pub fn browsing_context_id(&self) -> BrowsingContextId { + self.browsing_context_id } pub fn change_visibility_status(&self, visibility: bool) { @@ -364,7 +364,7 @@ impl HTMLIFrameElement { pub trait HTMLIFrameElementLayoutMethods { fn pipeline_id(&self) -> Option<PipelineId>; - fn frame_id(&self) -> FrameId; + fn browsing_context_id(&self) -> BrowsingContextId; fn get_width(&self) -> LengthOrPercentageOrAuto; fn get_height(&self) -> LengthOrPercentageOrAuto; } @@ -380,9 +380,9 @@ impl HTMLIFrameElementLayoutMethods for LayoutJS<HTMLIFrameElement> { #[inline] #[allow(unsafe_code)] - fn frame_id(&self) -> FrameId { + fn browsing_context_id(&self) -> BrowsingContextId { unsafe { - (*self.unsafe_get()).frame_id + (*self.unsafe_get()).browsing_context_id } } @@ -541,7 +541,7 @@ impl HTMLIFrameElementMethods for HTMLIFrameElement { // https://html.spec.whatwg.org/multipage/#dom-iframe-contentwindow fn GetContentWindow(&self) -> Option<Root<WindowProxy>> { - self.pipeline_id.get().and_then(|_| ScriptThread::find_window_proxy(self.frame_id)) + self.pipeline_id.get().and_then(|_| ScriptThread::find_window_proxy(self.browsing_context_id)) } // https://html.spec.whatwg.org/multipage/#dom-iframe-contentdocument @@ -711,7 +711,7 @@ impl VirtualMethods for HTMLIFrameElement { // is in a document tree and has a browsing context, which is what causes // the child browsing context to be created. if self.upcast::<Node>().is_in_doc_with_browsing_context() { - debug!("iframe {} src set while in browsing context.", self.frame_id); + debug!("iframe {} src set while in browsing context.", self.browsing_context_id); self.process_the_iframe_attributes(ProcessingMode::NotFirstTime); } }, @@ -740,7 +740,7 @@ impl VirtualMethods for HTMLIFrameElement { // to the newly-created browsing context, and then process the // iframe attributes for the "first time"." if self.upcast::<Node>().is_in_doc_with_browsing_context() { - debug!("iframe {} bound to browsing context.", self.frame_id); + debug!("iframe {} bound to browsing context.", self.browsing_context_id); debug_assert!(tree_in_doc, "is_in_doc_with_bc, but not tree_in_doc"); self.create_nested_browsing_context(); self.process_the_iframe_attributes(ProcessingMode::FirstTime); @@ -754,13 +754,13 @@ impl VirtualMethods for HTMLIFrameElement { LoadBlocker::terminate(&mut blocker); // https://html.spec.whatwg.org/multipage/#a-browsing-context-is-discarded - debug!("Unbinding frame {}.", self.frame_id); + debug!("Unbinding frame {}.", self.browsing_context_id); let window = window_from_node(self); let (sender, receiver) = ipc::channel().unwrap(); // Ask the constellation to remove the iframe, and tell us the // pipeline ids of the closed pipelines. - let msg = ConstellationMsg::RemoveIFrame(self.frame_id, sender); + let msg = ConstellationMsg::RemoveIFrame(self.browsing_context_id, sender); window.upcast::<GlobalScope>().constellation_chan().send(msg).unwrap(); let exited_pipeline_ids = receiver.recv().unwrap(); diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index d155b8b6a47..62bffbdcbcd 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -423,6 +423,8 @@ pub mod testbindingiterable; pub mod testbindingpairiterable; pub mod testbindingproxy; pub mod testrunner; +pub mod testworklet; +pub mod testworkletglobalscope; pub mod text; pub mod textdecoder; pub mod textencoder; @@ -469,6 +471,8 @@ pub mod worker; pub mod workerglobalscope; pub mod workerlocation; pub mod workernavigator; +pub mod worklet; +pub mod workletglobalscope; pub mod xmldocument; pub mod xmlhttprequest; pub mod xmlhttprequesteventtarget; diff --git a/components/script/dom/mutationobserver.rs b/components/script/dom/mutationobserver.rs index 4dc745c1ce8..48c8fe24b87 100644 --- a/components/script/dom/mutationobserver.rs +++ b/components/script/dom/mutationobserver.rs @@ -2,13 +2,22 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use dom::bindings::callback::ExceptionHandling; +use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::MutationObserverBinding; use dom::bindings::codegen::Bindings::MutationObserverBinding::MutationCallback; -use dom::bindings::error::Fallible; +use dom::bindings::codegen::Bindings::MutationObserverBinding::MutationObserverBinding::MutationObserverMethods; +use dom::bindings::codegen::Bindings::MutationObserverBinding::MutationObserverInit; +use dom::bindings::error::{Error, Fallible}; use dom::bindings::js::Root; use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::str::DOMString; +use dom::mutationrecord::MutationRecord; +use dom::node::Node; use dom::window::Window; use dom_struct::dom_struct; +use html5ever::{Namespace, LocalName}; +use microtask::Microtask; use script_thread::ScriptThread; use std::rc::Rc; @@ -17,6 +26,29 @@ pub struct MutationObserver { reflector_: Reflector, #[ignore_heap_size_of = "can't measure Rc values"] callback: Rc<MutationCallback>, + record_queue: DOMRefCell<Vec<Root<MutationRecord>>>, +} + +#[derive(Clone)] +pub enum Mutation { + Attribute { name: LocalName, namespace: Namespace, old_value: DOMString } +} + +#[derive(HeapSizeOf, JSTraceable)] +pub struct RegisteredObserver { + observer: Root<MutationObserver>, + options: ObserverOptions, +} + +#[derive(HeapSizeOf, JSTraceable)] +pub struct ObserverOptions { + attribute_old_value: bool, + attributes: bool, + character_data: bool, + character_data_old_value: bool, + child_list: bool, + subtree: bool, + attribute_filter: Vec<DOMString>, } impl MutationObserver { @@ -29,6 +61,7 @@ impl MutationObserver { MutationObserver { reflector_: Reflector::new(), callback: callback, + record_queue: DOMRefCell::new(vec![]), } } @@ -37,4 +70,185 @@ impl MutationObserver { ScriptThread::add_mutation_observer(&*observer); Ok(observer) } + + /// https://dom.spec.whatwg.org/#queue-a-mutation-observer-compound-microtask + pub fn queue_mutation_observer_compound_microtask() { + // Step 1 + if ScriptThread::is_mutation_observer_compound_microtask_queued() { + return; + } + // Step 2 + ScriptThread::set_mutation_observer_compound_microtask_queued(true); + // Step 3 + ScriptThread::enqueue_microtask(Microtask::NotifyMutationObservers); + } + + /// https://dom.spec.whatwg.org/#notify-mutation-observers + pub fn notify_mutation_observers() { + // Step 1 + ScriptThread::set_mutation_observer_compound_microtask_queued(false); + // Step 2 + let notify_list = ScriptThread::get_mutation_observers(); + // TODO: steps 3-4 (slots) + // Step 5 + for mo in ¬ify_list { + let queue: Vec<Root<MutationRecord>> = mo.record_queue.borrow().clone(); + mo.record_queue.borrow_mut().clear(); + // TODO: Step 5.3 Remove all transient registered observers whose observer is mo. + if !queue.is_empty() { + let _ = mo.callback.Call_(&**mo, queue, &**mo, ExceptionHandling::Report); + } + } + // TODO: Step 6 (slot signals) + } + + /// https://dom.spec.whatwg.org/#queueing-a-mutation-record + pub fn queue_a_mutation_record(target: &Node, attr_type: Mutation) { + // Step 1 + let mut interestedObservers: Vec<(Root<MutationObserver>, Option<DOMString>)> = vec![]; + // Step 2 & 3 + for node in target.inclusive_ancestors() { + for registered in &*node.registered_mutation_observers() { + if &*node != target && !registered.options.subtree { + continue; + } + + match attr_type { + Mutation::Attribute { ref name, ref namespace, ref old_value } => { + // Step 3.1 + if !registered.options.attributes { + continue; + } + if !registered.options.attribute_filter.is_empty() { + if *namespace != ns!() { + continue; + } + if registered.options.attribute_filter.iter() + .find(|s| &**s == &**name).is_some() { + continue; + } + } + // Step 3.1.2 + let paired_string = if registered.options.attribute_old_value { + Some(old_value.clone()) + } else { + None + }; + // Step 3.1.1 + let idx = interestedObservers.iter().position(|&(ref o, _)| + &**o as *const _ == &*registered.observer as *const _); + if let Some(idx) = idx { + interestedObservers[idx].1 = paired_string; + } else { + interestedObservers.push((Root::from_ref(&*registered.observer), + paired_string)); + } + } + } + } + } + + // Step 4 + for &(ref observer, ref paired_string) in &interestedObservers { + // Steps 4.1-4.7 + let record = match attr_type { + Mutation::Attribute { ref name, ref namespace, .. } => { + let namespace = if *namespace != ns!() { + Some(namespace) + } else { + None + }; + MutationRecord::attribute_mutated(target, name, namespace, paired_string.clone()) + } + }; + // Step 4.8 + observer.record_queue.borrow_mut().push(record); + } + + // Step 5 + MutationObserver::queue_mutation_observer_compound_microtask(); + } + +} + +impl MutationObserverMethods for MutationObserver { + /// https://dom.spec.whatwg.org/#dom-mutationobserver-observe + fn Observe(&self, target: &Node, options: &MutationObserverInit) -> Fallible<()> { + let attribute_filter = options.attributeFilter.clone().unwrap_or(vec![]); + let attribute_old_value = options.attributeOldValue.unwrap_or(false); + let mut attributes = options.attributes.unwrap_or(false); + let mut character_data = options.characterData.unwrap_or(false); + let character_data_old_value = options.characterDataOldValue.unwrap_or(false); + let child_list = options.childList; + let subtree = options.subtree; + + // Step 1 + if (options.attributeOldValue.is_some() || options.attributeFilter.is_some()) && + options.attributes.is_none() { + attributes = true; + } + + // Step 2 + if options.characterDataOldValue.is_some() && options.characterData.is_none() { + character_data = true; + } + + // Step 3 + if !child_list && !attributes && !character_data { + return Err(Error::Type("One of childList, attributes, or characterData must be true".into())); + } + + // Step 4 + if attribute_old_value && !attributes { + return Err(Error::Type("attributeOldValue is true but attributes is false".into())); + } + + // Step 5 + if options.attributeFilter.is_some() && !attributes { + return Err(Error::Type("attributeFilter is present but attributes is false".into())); + } + + // Step 6 + if character_data_old_value && !character_data { + return Err(Error::Type("characterDataOldValue is true but characterData is false".into())); + } + + // Step 7 + let add_new_observer = { + let mut replaced = false; + for registered in &mut *target.registered_mutation_observers() { + if &*registered.observer as *const MutationObserver != self as *const MutationObserver { + continue; + } + // TODO: remove matching transient registered observers + registered.options.attribute_old_value = attribute_old_value; + registered.options.attributes = attributes; + registered.options.character_data = character_data; + registered.options.character_data_old_value = character_data_old_value; + registered.options.child_list = child_list; + registered.options.subtree = subtree; + registered.options.attribute_filter = attribute_filter.clone(); + replaced = true; + } + !replaced + }; + + // Step 8 + if add_new_observer { + target.registered_mutation_observers().push(RegisteredObserver { + observer: Root::from_ref(self), + options: ObserverOptions { + attributes, + attribute_old_value, + character_data, + character_data_old_value, + subtree, + attribute_filter, + child_list + }, + }); + } + + Ok(()) + } } diff --git a/components/script/dom/mutationrecord.rs b/components/script/dom/mutationrecord.rs index c39d61ef18a..439f4ae02b5 100644 --- a/components/script/dom/mutationrecord.rs +++ b/components/script/dom/mutationrecord.rs @@ -2,22 +2,54 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use dom::bindings::codegen::Bindings::MutationRecordBinding::MutationRecordBinding; use dom::bindings::codegen::Bindings::MutationRecordBinding::MutationRecordBinding::MutationRecordMethods; use dom::bindings::js::{JS, Root}; -use dom::bindings::reflector::Reflector; +use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; -use dom::node::Node; +use dom::node::{Node, window_from_node}; +use dom::nodelist::NodeList; use dom_struct::dom_struct; +use html5ever::{LocalName, Namespace}; #[dom_struct] pub struct MutationRecord { reflector_: Reflector, - - //property for record type record_type: DOMString, - - //property for target node target: JS<Node>, + attribute_name: Option<DOMString>, + attribute_namespace: Option<DOMString>, + old_value: Option<DOMString>, +} + +impl MutationRecord { + #[allow(unrooted_must_root)] + pub fn attribute_mutated(target: &Node, + attribute_name: &LocalName, + attribute_namespace: Option<&Namespace>, + old_value: Option<DOMString>) -> Root<MutationRecord> { + let record = box MutationRecord::new_inherited("attributes", + target, + Some(DOMString::from(&**attribute_name)), + attribute_namespace.map(|n| DOMString::from(&**n)), + old_value); + reflect_dom_object(record, &*window_from_node(target), MutationRecordBinding::Wrap) + } + + fn new_inherited(record_type: &str, + target: &Node, + attribute_name: Option<DOMString>, + attribute_namespace: Option<DOMString>, + old_value: Option<DOMString>) -> MutationRecord { + MutationRecord { + reflector_: Reflector::new(), + record_type: DOMString::from(record_type), + target: JS::from_ref(target), + attribute_name: attribute_name, + attribute_namespace: attribute_namespace, + old_value: old_value, + } + } } impl MutationRecordMethods for MutationRecord { @@ -28,7 +60,44 @@ impl MutationRecordMethods for MutationRecord { // https://dom.spec.whatwg.org/#dom-mutationrecord-target fn Target(&self) -> Root<Node> { - return Root::from_ref(&*self.target); + Root::from_ref(&*self.target) + } + + // https://dom.spec.whatwg.org/#dom-mutationrecord-attributename + fn GetAttributeName(&self) -> Option<DOMString> { + self.attribute_name.clone() + } + + // https://dom.spec.whatwg.org/#dom-mutationrecord-attributenamespace + fn GetAttributeNamespace(&self) -> Option<DOMString> { + self.attribute_namespace.clone() + } + + // https://dom.spec.whatwg.org/#dom-mutationrecord-oldvalue + fn GetOldValue(&self) -> Option<DOMString> { + self.old_value.clone() + } + + // https://dom.spec.whatwg.org/#dom-mutationrecord-addednodes + fn AddedNodes(&self) -> Root<NodeList> { + let window = window_from_node(&*self.target); + NodeList::empty(&window) + } + + // https://dom.spec.whatwg.org/#dom-mutationrecord-removednodes + fn RemovedNodes(&self) -> Root<NodeList> { + let window = window_from_node(&*self.target); + NodeList::empty(&window) + } + + // https://dom.spec.whatwg.org/#dom-mutationrecord-previoussibling + fn GetPreviousSibling(&self) -> Option<Root<Node>> { + None + } + + // https://dom.spec.whatwg.org/#dom-mutationrecord-previoussibling + fn GetNextSibling(&self) -> Option<Root<Node>> { + None } } diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 6d16cac6731..1ad28974f3d 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -7,6 +7,7 @@ use app_units::Au; use devtools_traits::NodeInfo; use document_loader::DocumentLoader; +use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; @@ -46,6 +47,7 @@ use dom::htmllinkelement::HTMLLinkElement; use dom::htmlmetaelement::HTMLMetaElement; use dom::htmlstyleelement::HTMLStyleElement; use dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers}; +use dom::mutationobserver::RegisteredObserver; use dom::nodelist::NodeList; use dom::processinginstruction::ProcessingInstruction; use dom::range::WeakRangeVec; @@ -61,18 +63,18 @@ use heapsize::{HeapSizeOf, heap_size_of}; use html5ever::{Prefix, Namespace, QualName}; use js::jsapi::{JSContext, JSObject, JSRuntime}; use libc::{self, c_void, uintptr_t}; -use msg::constellation_msg::{FrameId, PipelineId}; +use msg::constellation_msg::{BrowsingContextId, PipelineId}; use ref_slice::ref_slice; use script_layout_interface::{HTMLCanvasData, OpaqueStyleAndLayoutData, SVGSVGData}; use script_layout_interface::{LayoutElementType, LayoutNodeType, TrustedNodeAddress}; use script_layout_interface::message::Msg; use script_traits::DocumentActivity; use script_traits::UntrustedNodeAddress; -use selectors::matching::matches_selector_list; +use selectors::matching::{matches_selector_list, MatchingContext, MatchingMode}; use selectors::parser::SelectorList; use servo_url::ServoUrl; use std::borrow::ToOwned; -use std::cell::{Cell, UnsafeCell}; +use std::cell::{Cell, UnsafeCell, RefMut}; use std::cmp::max; use std::default::Default; use std::iter; @@ -138,6 +140,9 @@ pub struct Node { /// node is finalized. style_and_layout_data: Cell<Option<OpaqueStyleAndLayoutData>>, + /// Registered observers for this node. + mutation_observers: DOMRefCell<Vec<RegisteredObserver>>, + unique_id: UniqueId, } @@ -341,11 +346,14 @@ impl<'a> Iterator for QuerySelectorIterator { fn next(&mut self) -> Option<Root<Node>> { let selectors = &self.selectors.0; + // TODO(cgaebel): Is it worth it to build a bloom filter here // (instead of passing `None`)? Probably. + let mut ctx = MatchingContext::new(MatchingMode::Normal, None); + self.iterator.by_ref().filter_map(|node| { if let Some(element) = Root::downcast(node) { - if matches_selector_list(selectors, &element, None) { + if matches_selector_list(selectors, &element, &mut ctx) { return Some(Root::upcast(element)); } } @@ -363,6 +371,11 @@ impl Node { } } + /// Return all registered mutation observers for this node. + pub fn registered_mutation_observers(&self) -> RefMut<Vec<RegisteredObserver>> { + self.mutation_observers.borrow_mut() + } + /// Dumps the subtree rooted at this node, for debugging. pub fn dump(&self) { self.dump_indent(0); @@ -707,8 +720,9 @@ impl Node { Err(()) => Err(Error::Syntax), // Step 3. Ok(selectors) => { + let mut ctx = MatchingContext::new(MatchingMode::Normal, None); Ok(self.traverse_preorder().filter_map(Root::downcast).find(|element| { - matches_selector_list(&selectors.0, element, None) + matches_selector_list(&selectors.0, element, &mut ctx) })) } } @@ -968,7 +982,7 @@ pub trait LayoutNodeHelpers { fn image_url(&self) -> Option<ServoUrl>; fn canvas_data(&self) -> Option<HTMLCanvasData>; fn svg_data(&self) -> Option<SVGSVGData>; - fn iframe_frame_id(&self) -> FrameId; + fn iframe_browsing_context_id(&self) -> BrowsingContextId; fn iframe_pipeline_id(&self) -> PipelineId; fn opaque(&self) -> OpaqueNode; } @@ -1119,10 +1133,10 @@ impl LayoutNodeHelpers for LayoutJS<Node> { .map(|svg| svg.data()) } - fn iframe_frame_id(&self) -> FrameId { + fn iframe_browsing_context_id(&self) -> BrowsingContextId { let iframe_element = self.downcast::<HTMLIFrameElement>() .expect("not an iframe element!"); - iframe_element.frame_id() + iframe_element.browsing_context_id() } fn iframe_pipeline_id(&self) -> PipelineId { @@ -1411,6 +1425,8 @@ impl Node { style_and_layout_data: Cell::new(None), + mutation_observers: Default::default(), + unique_id: UniqueId::new(), } } diff --git a/components/script/dom/promise.rs b/components/script/dom/promise.rs index 13689023462..5595e999c9d 100644 --- a/components/script/dom/promise.rs +++ b/components/script/dom/promise.rs @@ -296,3 +296,4 @@ fn create_native_handler_function(cx: *mut JSContext, obj.get() } } + diff --git a/components/script/dom/testworklet.rs b/components/script/dom/testworklet.rs new file mode 100644 index 00000000000..ea032e66faa --- /dev/null +++ b/components/script/dom/testworklet.rs @@ -0,0 +1,61 @@ +/* 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/. */ + +// check-tidy: no specs after this line + +use dom::bindings::codegen::Bindings::TestWorkletBinding::TestWorkletMethods; +use dom::bindings::codegen::Bindings::TestWorkletBinding::Wrap; +use dom::bindings::codegen::Bindings::WorkletBinding::WorkletBinding::WorkletMethods; +use dom::bindings::codegen::Bindings::WorkletBinding::WorkletOptions; +use dom::bindings::error::Fallible; +use dom::bindings::js::JS; +use dom::bindings::js::Root; +use dom::bindings::reflector::Reflector; +use dom::bindings::reflector::reflect_dom_object; +use dom::bindings::str::DOMString; +use dom::bindings::str::USVString; +use dom::promise::Promise; +use dom::window::Window; +use dom::worklet::Worklet; +use dom::workletglobalscope::WorkletGlobalScopeType; +use dom_struct::dom_struct; +use script_thread::ScriptThread; +use std::rc::Rc; + +#[dom_struct] +pub struct TestWorklet { + reflector: Reflector, + worklet: JS<Worklet>, +} + +impl TestWorklet { + fn new_inherited(worklet: &Worklet) -> TestWorklet { + TestWorklet { + reflector: Reflector::new(), + worklet: JS::from_ref(worklet), + } + } + + fn new(window: &Window) -> Root<TestWorklet> { + let worklet = Worklet::new(window, WorkletGlobalScopeType::Test); + reflect_dom_object(box TestWorklet::new_inherited(&*worklet), window, Wrap) + } + + pub fn Constructor(window: &Window) -> Fallible<Root<TestWorklet>> { + Ok(TestWorklet::new(window)) + } +} + +impl TestWorkletMethods for TestWorklet { + #[allow(unrooted_must_root)] + fn AddModule(&self, moduleURL: USVString, options: &WorkletOptions) -> Rc<Promise> { + self.worklet.AddModule(moduleURL, options) + } + + fn Lookup(&self, key: DOMString) -> Option<DOMString> { + let id = self.worklet.worklet_id(); + let pool = ScriptThread::worklet_thread_pool(); + pool.test_worklet_lookup(id, String::from(key)).map(DOMString::from) + } +} diff --git a/components/script/dom/testworkletglobalscope.rs b/components/script/dom/testworkletglobalscope.rs new file mode 100644 index 00000000000..dfd000ac5c1 --- /dev/null +++ b/components/script/dom/testworkletglobalscope.rs @@ -0,0 +1,66 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::TestWorkletGlobalScopeBinding; +use dom::bindings::codegen::Bindings::TestWorkletGlobalScopeBinding::TestWorkletGlobalScopeMethods; +use dom::bindings::js::Root; +use dom::bindings::str::DOMString; +use dom::workletglobalscope::WorkletGlobalScope; +use dom::workletglobalscope::WorkletGlobalScopeInit; +use dom_struct::dom_struct; +use js::rust::Runtime; +use msg::constellation_msg::PipelineId; +use servo_url::ServoUrl; +use std::collections::HashMap; +use std::sync::mpsc::Sender; + +// check-tidy: no specs after this line + +#[dom_struct] +pub struct TestWorkletGlobalScope { + // The worklet global for this object + worklet_global: WorkletGlobalScope, + // The key/value pairs + lookup_table: DOMRefCell<HashMap<String, String>>, +} + +impl TestWorkletGlobalScope { + #[allow(unsafe_code)] + pub fn new(runtime: &Runtime, + pipeline_id: PipelineId, + base_url: ServoUrl, + init: &WorkletGlobalScopeInit) + -> Root<TestWorkletGlobalScope> + { + debug!("Creating test worklet global scope for pipeline {}.", pipeline_id); + let global = box TestWorkletGlobalScope { + worklet_global: WorkletGlobalScope::new_inherited(pipeline_id, base_url, init), + lookup_table: Default::default(), + }; + unsafe { TestWorkletGlobalScopeBinding::Wrap(runtime.cx(), global) } + } + + pub fn perform_a_worklet_task(&self, task: TestWorkletTask) { + match task { + TestWorkletTask::Lookup(key, sender) => { + debug!("Looking up key {}.", key); + let result = self.lookup_table.borrow().get(&key).cloned(); + let _ = sender.send(result); + } + } + } +} + +impl TestWorkletGlobalScopeMethods for TestWorkletGlobalScope { + fn RegisterKeyValue(&self, key: DOMString, value: DOMString) { + debug!("Registering test worklet key/value {}/{}.", key, value); + self.lookup_table.borrow_mut().insert(String::from(key), String::from(value)); + } +} + +/// Tasks which can be performed by test worklets. +pub enum TestWorkletTask { + Lookup(String, Sender<Option<String>>), +} diff --git a/components/script/dom/webidls/Console.webidl b/components/script/dom/webidls/Console.webidl index 90f9bb9f58e..7c4c6906a27 100644 --- a/components/script/dom/webidls/Console.webidl +++ b/components/script/dom/webidls/Console.webidl @@ -10,7 +10,7 @@ */ [ClassString="Console", - Exposed=(Window,Worker), + Exposed=(Window,Worker,Worklet), ProtoObjectHack] namespace console { // These should be DOMString message, DOMString message2, ... diff --git a/components/script/dom/webidls/EventTarget.webidl b/components/script/dom/webidls/EventTarget.webidl index ee6e5d722a8..ad25712122a 100644 --- a/components/script/dom/webidls/EventTarget.webidl +++ b/components/script/dom/webidls/EventTarget.webidl @@ -5,7 +5,7 @@ * https://dom.spec.whatwg.org/#interface-eventtarget */ -[Abstract, Exposed=(Window,Worker)] +[Abstract, Exposed=(Window,Worker,Worklet)] interface EventTarget { void addEventListener(DOMString type, EventListener? listener, diff --git a/components/script/dom/webidls/GlobalScope.webidl b/components/script/dom/webidls/GlobalScope.webidl index 7dab4f3afa7..2681d236dbc 100644 --- a/components/script/dom/webidls/GlobalScope.webidl +++ b/components/script/dom/webidls/GlobalScope.webidl @@ -5,6 +5,6 @@ // This interface is entirely internal to Servo, and should not be accessible to // web pages. -[Exposed=(Window,Worker), +[Exposed=(Window,Worker,Worklet), Inline] interface GlobalScope : EventTarget {}; diff --git a/components/script/dom/webidls/MutationObserver.webidl b/components/script/dom/webidls/MutationObserver.webidl index dbcfa945d4a..738c711d8e7 100644 --- a/components/script/dom/webidls/MutationObserver.webidl +++ b/components/script/dom/webidls/MutationObserver.webidl @@ -9,7 +9,8 @@ // https://dom.spec.whatwg.org/#mutationobserver [Pref="dom.mutation_observer.enabled", Constructor(MutationCallback callback)] interface MutationObserver { - //void observe(Node target, optional MutationObserverInit options); + [Throws] + void observe(Node target, optional MutationObserverInit options); //void disconnect(); //sequence<MutationRecord> takeRecords(); }; diff --git a/components/script/dom/webidls/MutationRecord.webidl b/components/script/dom/webidls/MutationRecord.webidl index 286801bf2ab..1cd364091fc 100644 --- a/components/script/dom/webidls/MutationRecord.webidl +++ b/components/script/dom/webidls/MutationRecord.webidl @@ -12,13 +12,13 @@ interface MutationRecord { readonly attribute DOMString type; [SameObject] readonly attribute Node target; - //[SameObject] - //readonly attribute NodeList addedNodes; - //[SameObject] - //readonly attribute NodeList removedNodes; - //readonly attribute Node? previousSibling; - //readonly attribute Node? nextSibling; - //readonly attribute DOMString? attributeName; - //readonly attribute DOMString? attributeNamespace; - //readonly attribute DOMString? oldValue; + [SameObject] + readonly attribute NodeList addedNodes; + [SameObject] + readonly attribute NodeList removedNodes; + readonly attribute Node? previousSibling; + readonly attribute Node? nextSibling; + readonly attribute DOMString? attributeName; + readonly attribute DOMString? attributeNamespace; + readonly attribute DOMString? oldValue; }; diff --git a/components/script/dom/webidls/TestWorklet.webidl b/components/script/dom/webidls/TestWorklet.webidl new file mode 100644 index 00000000000..c1f1965a1e0 --- /dev/null +++ b/components/script/dom/webidls/TestWorklet.webidl @@ -0,0 +1,12 @@ +/* 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/. */ + +// This interface is entirely internal to Servo, and should not be accessible to +// web pages. + +[Pref="dom.worklet.testing.enabled", Exposed=(Window), Constructor] +interface TestWorklet { + [NewObject] Promise<void> addModule(USVString moduleURL, optional WorkletOptions options); + DOMString? lookup(DOMString key); +}; diff --git a/components/script/dom/webidls/TestWorkletGlobalScope.webidl b/components/script/dom/webidls/TestWorkletGlobalScope.webidl new file mode 100644 index 00000000000..44027ab8dc6 --- /dev/null +++ b/components/script/dom/webidls/TestWorkletGlobalScope.webidl @@ -0,0 +1,11 @@ +/* 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/. */ + +// This interface is entirely internal to Servo, and should not be accessible to +// web pages. + +[Global=(Worklet,TestWorklet), Exposed=TestWorklet] +interface TestWorkletGlobalScope : WorkletGlobalScope { + void registerKeyValue(DOMString key, DOMString value); +}; diff --git a/components/script/dom/webidls/VoidFunction.webidl b/components/script/dom/webidls/VoidFunction.webidl new file mode 100644 index 00000000000..82d4a666c51 --- /dev/null +++ b/components/script/dom/webidls/VoidFunction.webidl @@ -0,0 +1,13 @@ +/* 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/. */ +/* + * The origin of this IDL file is + * https://heycam.github.io/webidl/#VoidFunction + * + * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and + * Opera Software ASA. You are granted a license to use, reproduce + * and create derivative works of this document. + */ + +callback VoidFunction = void (); diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl index 47c753f43b1..548821ac971 100644 --- a/components/script/dom/webidls/Window.webidl +++ b/components/script/dom/webidls/Window.webidl @@ -201,3 +201,4 @@ partial interface Window { readonly attribute TestRunner testRunner; //readonly attribute EventSender eventSender; }; + diff --git a/components/script/dom/webidls/Worklet.webidl b/components/script/dom/webidls/Worklet.webidl new file mode 100644 index 00000000000..5bb39bebd96 --- /dev/null +++ b/components/script/dom/webidls/Worklet.webidl @@ -0,0 +1,13 @@ +/* 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/. */ + +// https://drafts.css-houdini.org/worklets/#worklet +[Exposed=(Window)] +interface Worklet { + [NewObject] Promise<void> addModule(USVString moduleURL, optional WorkletOptions options); +}; + +dictionary WorkletOptions { + RequestCredentials credentials = "omit"; +}; diff --git a/components/script/dom/webidls/WorkletGlobalScope.webidl b/components/script/dom/webidls/WorkletGlobalScope.webidl new file mode 100644 index 00000000000..ca29296a10e --- /dev/null +++ b/components/script/dom/webidls/WorkletGlobalScope.webidl @@ -0,0 +1,10 @@ +/* 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/. */ + +// https://drafts.css-houdini.org/worklets/#workletglobalscope +// TODO: The spec IDL doesn't make this a subclass of EventTarget +// https://github.com/whatwg/html/issues/2611 +[Exposed=Worklet] +interface WorkletGlobalScope: GlobalScope { +}; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 65d1a591adc..097f136e29c 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -49,6 +49,7 @@ use dom::screen::Screen; use dom::storage::Storage; use dom::testrunner::TestRunner; use dom::windowproxy::WindowProxy; +use dom::worklet::Worklet; use dom_struct::dom_struct; use euclid::{Point2D, Rect, Size2D}; use fetch; @@ -273,6 +274,9 @@ pub struct Window { /// Directory to store unminified scripts for this window if unminify-js /// opt is enabled. unminified_js_dir: DOMRefCell<Option<String>>, + + /// Worklets + test_worklet: MutNullableJS<Worklet>, } impl Window { @@ -1830,6 +1834,7 @@ impl Window { permission_state_invocation_results: DOMRefCell::new(HashMap::new()), pending_layout_images: DOMRefCell::new(HashMap::new()), unminified_js_dir: DOMRefCell::new(None), + test_worklet: Default::default(), }; unsafe { diff --git a/components/script/dom/windowproxy.rs b/components/script/dom/windowproxy.rs index 3290afa1f2c..e022e69a810 100644 --- a/components/script/dom/windowproxy.rs +++ b/components/script/dom/windowproxy.rs @@ -28,7 +28,7 @@ use js::jsapi::{MutableHandle, MutableHandleObject, MutableHandleValue}; use js::jsapi::{ObjectOpResult, PropertyDescriptor}; use js::jsval::{UndefinedValue, PrivateValue}; use js::rust::get_object_class; -use msg::constellation_msg::FrameId; +use msg::constellation_msg::BrowsingContextId; use msg::constellation_msg::PipelineId; use std::cell::Cell; use std::ptr; @@ -45,10 +45,10 @@ pub struct WindowProxy { /// changes Window. reflector: Reflector, - /// The frame id of the browsing context. - /// In the case that this is a nested browsing context, this is the frame id + /// The id of the browsing context. + /// In the case that this is a nested browsing context, this is the id /// of the container. - frame_id: FrameId, + browsing_context_id: BrowsingContextId, /// The pipeline id of the currently active document. /// May be None, when the currently active document is in another script thread. @@ -68,7 +68,7 @@ pub struct WindowProxy { } impl WindowProxy { - pub fn new_inherited(frame_id: FrameId, + pub fn new_inherited(browsing_context_id: BrowsingContextId, currently_active: Option<PipelineId>, frame_element: Option<&Element>, parent: Option<&WindowProxy>) @@ -76,7 +76,7 @@ impl WindowProxy { { WindowProxy { reflector: Reflector::new(), - frame_id: frame_id, + browsing_context_id: browsing_context_id, currently_active: Cell::new(currently_active), discarded: Cell::new(false), frame_element: frame_element.map(JS::from_ref), @@ -86,7 +86,7 @@ impl WindowProxy { #[allow(unsafe_code)] pub fn new(window: &Window, - frame_id: FrameId, + browsing_context_id: BrowsingContextId, frame_element: Option<&Element>, parent: Option<&WindowProxy>) -> Root<WindowProxy> @@ -107,7 +107,7 @@ impl WindowProxy { // Create a new browsing context. let current = Some(window.global().pipeline_id()); - let mut window_proxy = box WindowProxy::new_inherited(frame_id, current, frame_element, parent); + let mut window_proxy = box WindowProxy::new_inherited(browsing_context_id, current, frame_element, parent); // The window proxy owns the browsing context. // When we finalize the window proxy, it drops the browsing context it owns. @@ -125,7 +125,7 @@ impl WindowProxy { #[allow(unsafe_code)] pub fn new_dissimilar_origin(global_to_clone_from: &GlobalScope, - frame_id: FrameId, + browsing_context_id: BrowsingContextId, parent: Option<&WindowProxy>) -> Root<WindowProxy> { @@ -136,7 +136,7 @@ impl WindowProxy { let cx = global_to_clone_from.get_cx(); // Create a new browsing context. - let mut window_proxy = box WindowProxy::new_inherited(frame_id, None, None, parent); + let mut window_proxy = box WindowProxy::new_inherited(browsing_context_id, None, None, parent); // Create a new dissimilar-origin window. let window = DissimilarOriginWindow::new(global_to_clone_from, &*window_proxy); @@ -171,8 +171,8 @@ impl WindowProxy { self.discarded.get() } - pub fn frame_id(&self) -> FrameId { - self.frame_id + pub fn browsing_context_id(&self) -> BrowsingContextId { + self.browsing_context_id } pub fn frame_element(&self) -> Option<&Element> { diff --git a/components/script/dom/worklet.rs b/components/script/dom/worklet.rs new file mode 100644 index 00000000000..fa5b3950b51 --- /dev/null +++ b/components/script/dom/worklet.rs @@ -0,0 +1,637 @@ +/* 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/. */ + +//! An implementation of Houdini worklets. +//! +//! The goal of this implementation is to maximize responsiveness of worklets, +//! and in particular to ensure that the thread performing worklet tasks +//! is never busy GCing or loading worklet code. We do this by providing a custom +//! thread pool implementation, which only performs GC or code loading on +//! a backup thread, not on the primary worklet thread. + +use dom::bindings::codegen::Bindings::RequestBinding::RequestCredentials; +use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; +use dom::bindings::codegen::Bindings::WorkletBinding::WorkletMethods; +use dom::bindings::codegen::Bindings::WorkletBinding::WorkletOptions; +use dom::bindings::codegen::Bindings::WorkletBinding::Wrap; +use dom::bindings::error::Error; +use dom::bindings::inheritance::Castable; +use dom::bindings::js::JS; +use dom::bindings::js::Root; +use dom::bindings::js::RootCollection; +use dom::bindings::refcounted::TrustedPromise; +use dom::bindings::reflector::Reflector; +use dom::bindings::reflector::reflect_dom_object; +use dom::bindings::str::USVString; +use dom::bindings::trace::JSTraceable; +use dom::bindings::trace::RootedTraceableBox; +use dom::globalscope::GlobalScope; +use dom::promise::Promise; +use dom::testworkletglobalscope::TestWorkletTask; +use dom::window::Window; +use dom::workletglobalscope::WorkletGlobalScope; +use dom::workletglobalscope::WorkletGlobalScopeInit; +use dom::workletglobalscope::WorkletGlobalScopeType; +use dom::workletglobalscope::WorkletTask; +use dom_struct::dom_struct; +use js::jsapi::JSGCParamKey; +use js::jsapi::JSTracer; +use js::jsapi::JS_GC; +use js::jsapi::JS_GetGCParameter; +use js::rust::Runtime; +use msg::constellation_msg::PipelineId; +use net_traits::IpcSend; +use net_traits::load_whole_resource; +use net_traits::request::Destination; +use net_traits::request::RequestInit; +use net_traits::request::RequestMode; +use net_traits::request::Type as RequestType; +use script_runtime::CommonScriptMsg; +use script_runtime::ScriptThreadEventCategory; +use script_runtime::StackRootTLS; +use script_runtime::new_rt_and_cx; +use script_thread::MainThreadScriptMsg; +use script_thread::Runnable; +use script_thread::ScriptThread; +use servo_rand; +use servo_url::ImmutableOrigin; +use servo_url::ServoUrl; +use std::cmp::max; +use std::collections::HashMap; +use std::collections::hash_map; +use std::rc::Rc; +use std::sync::Arc; +use std::sync::atomic::AtomicIsize; +use std::sync::atomic::Ordering; +use std::sync::mpsc; +use std::sync::mpsc::Receiver; +use std::sync::mpsc::Sender; +use std::thread; +use style::thread_state; +use swapper::Swapper; +use swapper::swapper; +use uuid::Uuid; + +// Magic numbers +const WORKLET_THREAD_POOL_SIZE: u32 = 3; +const MIN_GC_THRESHOLD: u32 = 1_000_000; + +#[dom_struct] +/// https://drafts.css-houdini.org/worklets/#worklet +pub struct Worklet { + reflector: Reflector, + window: JS<Window>, + worklet_id: WorkletId, + global_type: WorkletGlobalScopeType, +} + +impl Worklet { + fn new_inherited(window: &Window, global_type: WorkletGlobalScopeType) -> Worklet { + Worklet { + reflector: Reflector::new(), + window: JS::from_ref(window), + worklet_id: WorkletId::new(), + global_type: global_type, + } + } + + pub fn new(window: &Window, global_type: WorkletGlobalScopeType) -> Root<Worklet> { + debug!("Creating worklet {:?}.", global_type); + reflect_dom_object(box Worklet::new_inherited(window, global_type), window, Wrap) + } + + pub fn worklet_id(&self) -> WorkletId { + self.worklet_id + } + + #[allow(dead_code)] + pub fn worklet_global_scope_type(&self) -> WorkletGlobalScopeType { + self.global_type + } +} + +impl WorkletMethods for Worklet { + #[allow(unrooted_must_root)] + /// https://drafts.css-houdini.org/worklets/#dom-worklet-addmodule + fn AddModule(&self, module_url: USVString, options: &WorkletOptions) -> Rc<Promise> { + // Step 1. + let promise = Promise::new(self.window.upcast()); + + // Step 3. + let module_url_record = match self.window.Document().base_url().join(&module_url.0) { + Ok(url) => url, + Err(err) => { + // Step 4. + debug!("URL {:?} parse error {:?}.", module_url.0, err); + promise.reject_error(self.window.get_cx(), Error::Syntax); + return promise; + } + }; + debug!("Adding Worklet module {}.", module_url_record); + + // Steps 6-12 in parallel. + let pending_tasks_struct = PendingTasksStruct::new(); + let global = self.window.upcast::<GlobalScope>(); + let pool = ScriptThread::worklet_thread_pool(); + + pool.fetch_and_invoke_a_worklet_script(global.pipeline_id(), + self.worklet_id, + self.global_type, + self.window.origin().immutable().clone(), + global.api_base_url(), + module_url_record, + options.credentials.clone(), + pending_tasks_struct, + &promise); + + // Step 5. + promise + } +} + +/// A guid for worklets. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, JSTraceable)] +pub struct WorkletId(Uuid); + +known_heap_size!(0, WorkletId); + +impl WorkletId { + fn new() -> WorkletId { + WorkletId(servo_rand::random()) + } +} + +/// https://drafts.css-houdini.org/worklets/#pending-tasks-struct +#[derive(Clone, Debug)] +struct PendingTasksStruct(Arc<AtomicIsize>); + +impl PendingTasksStruct { + fn new() -> PendingTasksStruct { + PendingTasksStruct(Arc::new(AtomicIsize::new(WORKLET_THREAD_POOL_SIZE as isize))) + } + + fn set_counter_to(&self, value: isize) -> isize { + self.0.swap(value, Ordering::AcqRel) + } + + fn decrement_counter_by(&self, offset: isize) -> isize { + self.0.fetch_sub(offset, Ordering::AcqRel) + } +} + +/// Worklets execute in a dedicated thread pool. +/// +/// The goal is to ensure that there is a primary worklet thread, +/// which is able to responsively execute worklet code. In particular, +/// worklet execution should not be delayed by GC, or by script +/// loading. +/// +/// To achieve this, we implement a three-thread pool, with the +/// threads cycling between three thread roles: +/// +/// * The primary worklet thread is the one available to execute +/// worklet code. +/// +/// * The hot backup thread may peform GC, but otherwise is expected +/// to take over the primary role. +/// +/// * The cold backup thread may peform script loading and other +/// long-running tasks. +/// +/// In the implementation, we use two kinds of messages: +/// +/// * Data messages are expected to be processed quickly, and include +/// the worklet tasks to be performed by the primary thread, as +/// well as requests to change role or quit execution. +/// +/// * Control messages are expected to be processed more slowly, and +/// include script loading. +/// +/// Data messages are targeted at a role, for example, task execution +/// is expected to be performed by whichever thread is currently +/// primary. Control messages are targeted at a thread, for example +/// adding a module is performed in every thread, even if they change roles +/// in the middle of module loading. +/// +/// The thread pool lives in the script thread, and is initialized +/// when a worklet adds a module. It is dropped when the script thread +/// is dropped, and asks each of the worklet threads to quit. + +#[derive(Clone, JSTraceable)] +pub struct WorkletThreadPool { + // Channels to send data messages to the three roles. + primary_sender: Sender<WorkletData>, + hot_backup_sender: Sender<WorkletData>, + cold_backup_sender: Sender<WorkletData>, + // Channels to send control messages to the three threads. + control_sender_0: Sender<WorkletControl>, + control_sender_1: Sender<WorkletControl>, + control_sender_2: Sender<WorkletControl>, +} + +impl Drop for WorkletThreadPool { + fn drop(&mut self) { + let _ = self.cold_backup_sender.send(WorkletData::Quit); + let _ = self.hot_backup_sender.send(WorkletData::Quit); + let _ = self.primary_sender.send(WorkletData::Quit); + } +} + +impl WorkletThreadPool { + /// Create a new thread pool and spawn the threads. + /// When the thread pool is dropped, the threads will be asked to quit. + pub fn spawn(script_sender: Sender<MainThreadScriptMsg>, global_init: WorkletGlobalScopeInit) -> WorkletThreadPool { + let primary_role = WorkletThreadRole::new(false, false); + let hot_backup_role = WorkletThreadRole::new(true, false); + let cold_backup_role = WorkletThreadRole::new(false, true); + let primary_sender = primary_role.sender.clone(); + let hot_backup_sender = hot_backup_role.sender.clone(); + let cold_backup_sender = cold_backup_role.sender.clone(); + let init = WorkletThreadInit { + hot_backup_sender: hot_backup_sender.clone(), + cold_backup_sender: cold_backup_sender.clone(), + script_sender: script_sender.clone(), + global_init: global_init, + }; + WorkletThreadPool { + primary_sender: primary_sender, + hot_backup_sender: hot_backup_sender, + cold_backup_sender: cold_backup_sender, + control_sender_0: WorkletThread::spawn(primary_role, init.clone()), + control_sender_1: WorkletThread::spawn(hot_backup_role, init.clone()), + control_sender_2: WorkletThread::spawn(cold_backup_role, init), + } + } + + /// Loads a worklet module into every worklet thread. + /// If all of the threads load successfully, the promise is resolved. + /// If any of the threads fails to load, the promise is rejected. + /// https://drafts.css-houdini.org/worklets/#fetch-and-invoke-a-worklet-script + fn fetch_and_invoke_a_worklet_script(&self, + pipeline_id: PipelineId, + worklet_id: WorkletId, + global_type: WorkletGlobalScopeType, + origin: ImmutableOrigin, + base_url: ServoUrl, + script_url: ServoUrl, + credentials: RequestCredentials, + pending_tasks_struct: PendingTasksStruct, + promise: &Rc<Promise>) + { + // Send each thread a control message asking it to load the script. + for sender in &[&self.control_sender_0, &self.control_sender_1, &self.control_sender_2] { + let _ = sender.send(WorkletControl::FetchAndInvokeAWorkletScript { + pipeline_id: pipeline_id, + worklet_id: worklet_id, + global_type: global_type, + origin: origin.clone(), + base_url: base_url.clone(), + script_url: script_url.clone(), + credentials: credentials, + pending_tasks_struct: pending_tasks_struct.clone(), + promise: TrustedPromise::new(promise.clone()), + }); + } + // If any of the threads are blocked waiting on data, wake them up. + let _ = self.cold_backup_sender.send(WorkletData::WakeUp); + let _ = self.hot_backup_sender.send(WorkletData::WakeUp); + let _ = self.primary_sender.send(WorkletData::WakeUp); + } + + /// For testing. + pub fn test_worklet_lookup(&self, id: WorkletId, key: String) -> Option<String> { + let (sender, receiver) = mpsc::channel(); + let msg = WorkletData::Task(id, WorkletTask::Test(TestWorkletTask::Lookup(key, sender))); + let _ = self.primary_sender.send(msg); + receiver.recv().expect("Test worklet has died?") + } +} + +/// The data messages sent to worklet threads +enum WorkletData { + Task(WorkletId, WorkletTask), + StartSwapRoles(Sender<WorkletData>), + FinishSwapRoles(Swapper<WorkletThreadRole>), + WakeUp, + Quit, +} + +/// The control message sent to worklet threads +enum WorkletControl { + FetchAndInvokeAWorkletScript { + pipeline_id: PipelineId, + worklet_id: WorkletId, + global_type: WorkletGlobalScopeType, + origin: ImmutableOrigin, + base_url: ServoUrl, + script_url: ServoUrl, + credentials: RequestCredentials, + pending_tasks_struct: PendingTasksStruct, + promise: TrustedPromise, + }, +} + +/// A role that a worklet thread can be playing. +/// +/// These roles are used as tokens or capabilities, we track unique +/// ownership using Rust's types, and use atomic swapping to exchange +/// them between worklet threads. This ensures that each thread pool has +/// exactly one primary, one hot backup and one cold backup. +struct WorkletThreadRole { + receiver: Receiver<WorkletData>, + sender: Sender<WorkletData>, + is_hot_backup: bool, + is_cold_backup: bool, +} + +impl WorkletThreadRole { + fn new(is_hot_backup: bool, is_cold_backup: bool) -> WorkletThreadRole { + let (sender, receiver) = mpsc::channel(); + WorkletThreadRole { + sender: sender, + receiver: receiver, + is_hot_backup: is_hot_backup, + is_cold_backup: is_cold_backup, + } + } +} + +/// Data to initialize a worklet thread. +#[derive(Clone)] +struct WorkletThreadInit { + /// Senders + hot_backup_sender: Sender<WorkletData>, + cold_backup_sender: Sender<WorkletData>, + script_sender: Sender<MainThreadScriptMsg>, + + /// Data for initializing new worklet global scopes + global_init: WorkletGlobalScopeInit, +} + +/// A thread for executing worklets. +#[must_root] +struct WorkletThread { + /// Which role the thread is currently playing + role: WorkletThreadRole, + + /// The thread's receiver for control messages + control_receiver: Receiver<WorkletControl>, + + /// Senders + hot_backup_sender: Sender<WorkletData>, + cold_backup_sender: Sender<WorkletData>, + script_sender: Sender<MainThreadScriptMsg>, + + /// Data for initializing new worklet global scopes + global_init: WorkletGlobalScopeInit, + + /// The global scopes created by this thread + global_scopes: HashMap<WorkletId, JS<WorkletGlobalScope>>, + + /// A one-place buffer for control messages + control_buffer: Option<WorkletControl>, + + /// The JS runtime + runtime: Runtime, + should_gc: bool, + gc_threshold: u32, +} + +#[allow(unsafe_code)] +unsafe impl JSTraceable for WorkletThread { + unsafe fn trace(&self, trc: *mut JSTracer) { + debug!("Tracing worklet thread."); + self.global_scopes.trace(trc); + } +} + +impl WorkletThread { + /// Spawn a new worklet thread, returning the channel to send it control messages. + #[allow(unsafe_code)] + #[allow(unrooted_must_root)] + fn spawn(role: WorkletThreadRole, init: WorkletThreadInit) -> Sender<WorkletControl> { + let (control_sender, control_receiver) = mpsc::channel(); + // TODO: name this thread + thread::spawn(move || { + // TODO: add a new IN_WORKLET thread state? + // TODO: set interrupt handler? + // TODO: configure the JS runtime (e.g. discourage GC, encourage agressive JIT) + debug!("Initializing worklet thread."); + thread_state::initialize(thread_state::SCRIPT | thread_state::IN_WORKER); + let roots = RootCollection::new(); + let _stack_roots_tls = StackRootTLS::new(&roots); + let mut thread = RootedTraceableBox::new(WorkletThread { + role: role, + control_receiver: control_receiver, + hot_backup_sender: init.hot_backup_sender, + cold_backup_sender: init.cold_backup_sender, + script_sender: init.script_sender, + global_init: init.global_init, + global_scopes: HashMap::new(), + control_buffer: None, + runtime: unsafe { new_rt_and_cx() }, + should_gc: false, + gc_threshold: MIN_GC_THRESHOLD, + }); + thread.run(); + }); + control_sender + } + + /// The main event loop for a worklet thread + fn run(&mut self) { + loop { + // The handler for data messages + let message = self.role.receiver.recv().unwrap(); + match message { + // The whole point of this thread pool is to perform tasks! + WorkletData::Task(id, task) => { + self.perform_a_worklet_task(id, task); + } + // To start swapping roles, get ready to perform an atomic swap, + // and block waiting for the other end to finish it. + // NOTE: the cold backup can block on the primary or the hot backup; + // the hot backup can block on the primary; + // the primary can block on nothing; + // this total ordering on thread roles is what guarantees deadlock-freedom. + WorkletData::StartSwapRoles(sender) => { + let (our_swapper, their_swapper) = swapper(); + sender.send(WorkletData::FinishSwapRoles(their_swapper)).unwrap(); + let _ = our_swapper.swap(&mut self.role); + } + // To finish swapping roles, perform the atomic swap. + // The other end should have already started the swap, so this shouldn't block. + WorkletData::FinishSwapRoles(swapper) => { + let _ = swapper.swap(&mut self.role); + } + // Wake up! There may be control messages to process. + WorkletData::WakeUp => { + } + // Quit! + WorkletData::Quit => { + return; + } + } + // Only process control messages if we're the cold backup, + // otherwise if there are outstanding control messages, + // try to become the cold backup. + if self.role.is_cold_backup { + if let Some(control) = self.control_buffer.take() { + self.process_control(control); + } + while let Ok(control) = self.control_receiver.try_recv() { + self.process_control(control); + } + self.gc(); + } else if self.control_buffer.is_none() { + if let Ok(control) = self.control_receiver.try_recv() { + self.control_buffer = Some(control); + let msg = WorkletData::StartSwapRoles(self.role.sender.clone()); + let _ = self.cold_backup_sender.send(msg); + } + } + // If we are tight on memory, and we're a backup then perform a gc. + // If we are tight on memory, and we're the primary then try to become the hot backup. + // Hopefully this happens soon! + if self.current_memory_usage() > self.gc_threshold { + if self.role.is_hot_backup || self.role.is_cold_backup { + self.should_gc = false; + self.gc(); + } else if !self.should_gc { + self.should_gc = true; + let msg = WorkletData::StartSwapRoles(self.role.sender.clone()); + let _ = self.hot_backup_sender.send(msg); + } + } + } + } + + /// The current memory usage of the thread + #[allow(unsafe_code)] + fn current_memory_usage(&self) -> u32 { + unsafe { JS_GetGCParameter(self.runtime.rt(), JSGCParamKey::JSGC_BYTES) } + } + + /// Perform a GC. + #[allow(unsafe_code)] + fn gc(&mut self) { + debug!("BEGIN GC (usage = {}, threshold = {}).", self.current_memory_usage(), self.gc_threshold); + unsafe { JS_GC(self.runtime.rt()) }; + self.gc_threshold = max(MIN_GC_THRESHOLD, self.current_memory_usage() * 2); + debug!("END GC (usage = {}, threshold = {}).", self.current_memory_usage(), self.gc_threshold); + } + + /// Get the worklet global scope for a given worklet. + /// Creates the worklet global scope if it doesn't exist. + fn get_worklet_global_scope(&mut self, + pipeline_id: PipelineId, + worklet_id: WorkletId, + global_type: WorkletGlobalScopeType, + base_url: ServoUrl) + -> Root<WorkletGlobalScope> + { + match self.global_scopes.entry(worklet_id) { + hash_map::Entry::Occupied(entry) => Root::from_ref(entry.get()), + hash_map::Entry::Vacant(entry) => { + let result = global_type.new(&self.runtime, pipeline_id, base_url, &self.global_init); + entry.insert(JS::from_ref(&*result)); + result + }, + } + } + + /// Fetch and invoke a worklet script. + /// https://drafts.css-houdini.org/worklets/#fetch-and-invoke-a-worklet-script + fn fetch_and_invoke_a_worklet_script(&self, + global_scope: &WorkletGlobalScope, + origin: ImmutableOrigin, + script_url: ServoUrl, + credentials: RequestCredentials, + pending_tasks_struct: PendingTasksStruct, + promise: TrustedPromise) + { + debug!("Fetching from {}.", script_url); + // Step 1. + // TODO: Settings object? + + // Step 2. + // TODO: Fetch a module graph, not just a single script. + // TODO: Fetch the script asynchronously? + // TODO: Caching. + // TODO: Avoid re-parsing the origin as a URL. + let resource_fetcher = self.global_init.resource_threads.sender(); + let origin_url = ServoUrl::parse(&*origin.unicode_serialization()).expect("Failed to parse origin as URL."); + let request = RequestInit { + url: script_url, + type_: RequestType::Script, + destination: Destination::Script, + mode: RequestMode::CorsMode, + origin: origin_url, + credentials_mode: credentials.into(), + .. RequestInit::default() + }; + let script = load_whole_resource(request, &resource_fetcher).ok() + .and_then(|(_, bytes)| String::from_utf8(bytes).ok()); + + // Step 4. + // NOTE: the spec parses and executes the script in separate steps, + // but our JS API doesn't separate these, so we do the steps out of order. + let ok = script.map(|script| global_scope.evaluate_js(&*script)).unwrap_or(false); + + if !ok { + // Step 3. + debug!("Failed to load script."); + let old_counter = pending_tasks_struct.set_counter_to(-1); + if old_counter > 0 { + self.run_in_script_thread(promise.reject_runnable(Error::Abort)); + } + } else { + // Step 5. + debug!("Finished adding script."); + let old_counter = pending_tasks_struct.decrement_counter_by(1); + if old_counter == 1 { + // TODO: trigger a reflow? + self.run_in_script_thread(promise.resolve_runnable(())); + } + } + } + + /// Perform a task. + fn perform_a_worklet_task(&self, worklet_id: WorkletId, task: WorkletTask) { + match self.global_scopes.get(&worklet_id) { + Some(global) => global.perform_a_worklet_task(task), + None => return warn!("No such worklet as {:?}.", worklet_id), + } + } + + /// Process a control message. + fn process_control(&mut self, control: WorkletControl) { + match control { + WorkletControl::FetchAndInvokeAWorkletScript { + pipeline_id, worklet_id, global_type, origin, base_url, + script_url, credentials, pending_tasks_struct, promise, + } => { + let global = self.get_worklet_global_scope(pipeline_id, + worklet_id, + global_type, + base_url); + self.fetch_and_invoke_a_worklet_script(&*global, + origin, + script_url, + credentials, + pending_tasks_struct, + promise) + } + } + } + + /// Run a runnable in the main script thread. + fn run_in_script_thread<R>(&self, runnable: R) where + R: 'static + Send + Runnable, + { + let msg = CommonScriptMsg::RunnableMsg(ScriptThreadEventCategory::WorkletEvent, box runnable); + let msg = MainThreadScriptMsg::Common(msg); + self.script_sender.send(msg).expect("Worklet thread outlived script thread."); + } +} diff --git a/components/script/dom/workletglobalscope.rs b/components/script/dom/workletglobalscope.rs new file mode 100644 index 00000000000..a2e2463ca27 --- /dev/null +++ b/components/script/dom/workletglobalscope.rs @@ -0,0 +1,143 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use devtools_traits::ScriptToDevtoolsControlMsg; +use dom::bindings::inheritance::Castable; +use dom::bindings::js::Root; +use dom::globalscope::GlobalScope; +use dom::testworkletglobalscope::TestWorkletGlobalScope; +use dom::testworkletglobalscope::TestWorkletTask; +use dom_struct::dom_struct; +use ipc_channel::ipc; +use ipc_channel::ipc::IpcSender; +use js::jsval::UndefinedValue; +use js::rust::Runtime; +use microtask::Microtask; +use microtask::MicrotaskQueue; +use msg::constellation_msg::PipelineId; +use net_traits::ResourceThreads; +use profile_traits::mem; +use profile_traits::time; +use script_traits::ScriptMsg; +use script_traits::TimerSchedulerMsg; +use servo_url::ImmutableOrigin; +use servo_url::MutableOrigin; +use servo_url::ServoUrl; + +#[dom_struct] +/// https://drafts.css-houdini.org/worklets/#workletglobalscope +pub struct WorkletGlobalScope { + /// The global for this worklet. + globalscope: GlobalScope, + /// The base URL for this worklet. + base_url: ServoUrl, + /// The microtask queue for this worklet + microtask_queue: MicrotaskQueue, +} + +impl WorkletGlobalScope { + /// Create a new stack-allocated `WorkletGlobalScope`. + pub fn new_inherited(pipeline_id: PipelineId, + base_url: ServoUrl, + init: &WorkletGlobalScopeInit) + -> WorkletGlobalScope { + // Any timer events fired on this global are ignored. + let (timer_event_chan, _) = ipc::channel().unwrap(); + WorkletGlobalScope { + globalscope: GlobalScope::new_inherited(pipeline_id, + init.devtools_chan.clone(), + init.mem_profiler_chan.clone(), + init.time_profiler_chan.clone(), + init.constellation_chan.clone(), + init.scheduler_chan.clone(), + init.resource_threads.clone(), + timer_event_chan, + MutableOrigin::new(ImmutableOrigin::new_opaque())), + base_url: base_url, + microtask_queue: MicrotaskQueue::default(), + } + } + + /// Evaluate a JS script in this global. + pub fn evaluate_js(&self, script: &str) -> bool { + debug!("Evaluating JS."); + rooted!(in (self.globalscope.get_cx()) let mut rval = UndefinedValue()); + self.globalscope.evaluate_js_on_global_with_result(&*script, rval.handle_mut()) + } + + /// The base URL of this global. + pub fn base_url(&self) -> ServoUrl { + self.base_url.clone() + } + + /// Queue up a microtask to be executed in this global. + pub fn enqueue_microtask(&self, job: Microtask) { + self.microtask_queue.enqueue(job); + } + + /// Perform any queued microtasks. + pub fn perform_a_microtask_checkpoint(&self) { + self.microtask_queue.checkpoint(|id| { + let global = self.upcast::<GlobalScope>(); + assert_eq!(global.pipeline_id(), id); + Some(Root::from_ref(global)) + }); + } + + /// Perform a worklet task + pub fn perform_a_worklet_task(&self, task: WorkletTask) { + match task { + WorkletTask::Test(task) => match self.downcast::<TestWorkletGlobalScope>() { + Some(global) => global.perform_a_worklet_task(task), + None => warn!("This is not a test worklet."), + }, + } + } +} + +/// Resources required by workletglobalscopes +#[derive(Clone)] +pub struct WorkletGlobalScopeInit { + /// Channel to a resource thread + pub resource_threads: ResourceThreads, + /// Channel to the memory profiler + pub mem_profiler_chan: mem::ProfilerChan, + /// Channel to the time profiler + pub time_profiler_chan: time::ProfilerChan, + /// Channel to devtools + pub devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>, + /// Messages to send to constellation + pub constellation_chan: IpcSender<ScriptMsg>, + /// Message to send to the scheduler + pub scheduler_chan: IpcSender<TimerSchedulerMsg>, +} + +/// https://drafts.css-houdini.org/worklets/#worklet-global-scope-type +#[derive(Clone, Copy, Debug, HeapSizeOf, JSTraceable)] +pub enum WorkletGlobalScopeType { + /// https://drafts.css-houdini.org/worklets/#examples + Test, +} + +impl WorkletGlobalScopeType { + /// Create a new heap-allocated `WorkletGlobalScope`. + pub fn new(&self, + runtime: &Runtime, + pipeline_id: PipelineId, + base_url: ServoUrl, + init: &WorkletGlobalScopeInit) + -> Root<WorkletGlobalScope> + { + match *self { + WorkletGlobalScopeType::Test => + Root::upcast(TestWorkletGlobalScope::new(runtime, pipeline_id, base_url, init)), + } + } +} + +/// A task which can be performed in the context of a worklet global. +pub enum WorkletTask { + Test(TestWorkletTask), +} + diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index 3d4b90eaeab..62e5158f321 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -44,14 +44,14 @@ use dom::node::{LayoutNodeHelpers, Node}; use dom::text::Text; use gfx_traits::ByteIndex; use html5ever::{LocalName, Namespace}; -use msg::constellation_msg::{FrameId, PipelineId}; +use msg::constellation_msg::{BrowsingContextId, PipelineId}; use range::Range; use script_layout_interface::{HTMLCanvasData, LayoutNodeType, SVGSVGData, TrustedNodeAddress}; use script_layout_interface::{OpaqueStyleAndLayoutData, PartialPersistentLayoutData}; use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutNode}; use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; -use selectors::matching::{ElementSelectorFlags, StyleRelations}; -use selectors::parser::{AttrSelector, NamespaceConstraint}; +use selectors::attr::{AttrSelectorOperation, NamespaceConstraint}; +use selectors::matching::{ElementSelectorFlags, MatchingContext}; use servo_atoms::Atom; use servo_url::ServoUrl; use std::fmt; @@ -402,11 +402,22 @@ impl<'le> TElement for ServoLayoutElement<'le> { self.get_attr(namespace, attr).map_or(false, |x| x == val) } + #[inline(always)] + fn each_class<F>(&self, mut callback: F) where F: FnMut(&Atom) { + unsafe { + if let Some(ref classes) = self.element.get_classes_for_layout() { + for class in *classes { + callback(class) + } + } + } + } + #[inline] fn existing_style_for_restyle_damage<'a>(&'a self, - current_cv: &'a Arc<ComputedValues>, + current_cv: &'a ComputedValues, _pseudo_element: Option<&PseudoElement>) - -> Option<&'a Arc<ComputedValues>> { + -> Option<&'a ComputedValues> { Some(current_cv) } @@ -510,6 +521,13 @@ impl<'le> ServoLayoutElement<'le> { } #[inline] + fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> { + unsafe { + (*self.element.unsafe_get()).get_attr_for_layout(namespace, name) + } + } + + #[inline] fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&str> { unsafe { (*self.element.unsafe_get()).get_attr_val_for_layout(namespace, name) @@ -558,32 +576,9 @@ fn as_element<'le>(node: LayoutJS<Node>) -> Option<ServoLayoutElement<'le>> { node.downcast().map(ServoLayoutElement::from_layout_js) } -impl<'le> ::selectors::MatchAttrGeneric for ServoLayoutElement<'le> { +impl<'le> ::selectors::Element for ServoLayoutElement<'le> { type Impl = SelectorImpl; - fn match_attr<F>(&self, attr: &AttrSelector<SelectorImpl>, test: F) -> bool - where F: Fn(&str) -> bool { - use ::selectors::Element; - let name = if self.is_html_element_in_html_document() { - &attr.lower_name - } else { - &attr.name - }; - match attr.namespace { - NamespaceConstraint::Specific(ref ns) => { - self.get_attr(&ns.url, name).map_or(false, |attr| test(attr)) - }, - NamespaceConstraint::Any => { - let attrs = unsafe { - (*self.element.unsafe_get()).get_attr_vals_for_layout(name) - }; - attrs.iter().any(|attr| test(*attr)) - } - } - } -} - -impl<'le> ::selectors::Element for ServoLayoutElement<'le> { fn parent_element(&self) -> Option<ServoLayoutElement<'le>> { unsafe { self.element.upcast().parent_node_ref().and_then(as_element) @@ -620,6 +615,25 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { None } + fn attr_matches(&self, + ns: &NamespaceConstraint<&Namespace>, + local_name: &LocalName, + operation: &AttrSelectorOperation<&String>) + -> bool { + match *ns { + NamespaceConstraint::Specific(ref ns) => { + self.get_attr_enum(ns, local_name) + .map_or(false, |value| value.eval_selector(operation)) + } + NamespaceConstraint::Any => { + let values = unsafe { + (*self.element.unsafe_get()).get_attr_vals_for_layout(local_name) + }; + values.iter().any(|value| value.eval_selector(operation)) + } + } + } + fn is_root(&self) -> bool { match self.as_node().parent_node() { None => false, @@ -652,9 +666,17 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { self.element.namespace() } + fn match_pseudo_element(&self, + _pseudo: &PseudoElement, + _context: &mut MatchingContext) + -> bool + { + false + } + fn match_non_ts_pseudo_class<F>(&self, pseudo_class: &NonTSPseudoClass, - _: &mut StyleRelations, + _: &mut MatchingContext, _: &mut F) -> bool where F: FnMut(&Self, ElementSelectorFlags), @@ -684,7 +706,10 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { _ => true, } }, - + NonTSPseudoClass::ServoCaseSensitiveTypeAttr(ref expected_value) => { + self.get_attr_enum(&ns!(), &local_name!("type")) + .map_or(false, |attr| attr == expected_value) + } NonTSPseudoClass::ReadOnly => !self.element.get_state_for_layout().contains(pseudo_class.state_flag()), @@ -717,17 +742,6 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { } } - #[inline(always)] - fn each_class<F>(&self, mut callback: F) where F: FnMut(&Atom) { - unsafe { - if let Some(ref classes) = self.element.get_classes_for_layout() { - for class in *classes { - callback(class) - } - } - } - } - fn is_html_element_in_html_document(&self) -> bool { unsafe { self.element.html_element_in_html_document_for_layout() @@ -908,9 +922,9 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> { this.svg_data() } - fn iframe_frame_id(&self) -> FrameId { + fn iframe_browsing_context_id(&self) -> BrowsingContextId { let this = unsafe { self.get_jsmanaged() }; - this.iframe_frame_id() + this.iframe_browsing_context_id() } fn iframe_pipeline_id(&self) -> PipelineId { @@ -1067,6 +1081,10 @@ impl<'le> ThreadSafeLayoutElement for ServoThreadSafeLayoutElement<'le> { self.element } + fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> { + self.element.get_attr_enum(namespace, name) + } + fn get_attr<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str> { self.element.get_attr(namespace, name) } @@ -1083,30 +1101,14 @@ impl<'le> ThreadSafeLayoutElement for ServoThreadSafeLayoutElement<'le> { /// i.e., local_name, attributes, so they can only be used for **private** /// pseudo-elements (like `::-servo-details-content`). /// -/// Probably a few more of this functions can be implemented (like `has_class`, -/// `each_class`, etc), but they have no use right now. +/// Probably a few more of this functions can be implemented (like `has_class`, etc.), +/// but they have no use right now. /// /// Note that the element implementation is needed only for selector matching, /// not for inheritance (styles are inherited appropiately). -impl<'le> ::selectors::MatchAttrGeneric for ServoThreadSafeLayoutElement<'le> { +impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { type Impl = SelectorImpl; - fn match_attr<F>(&self, attr: &AttrSelector<SelectorImpl>, test: F) -> bool - where F: Fn(&str) -> bool { - match attr.namespace { - NamespaceConstraint::Specific(ref ns) => { - self.get_attr(&ns.url, &attr.name).map_or(false, |attr| test(attr)) - }, - NamespaceConstraint::Any => { - unsafe { - (*self.element.element.unsafe_get()).get_attr_vals_for_layout(&attr.name).iter() - .any(|attr| test(*attr)) - } - } - } - } -} -impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { fn parent_element(&self) -> Option<Self> { warn!("ServoThreadSafeLayoutElement::parent_element called"); None @@ -1150,9 +1152,36 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { self.element.get_namespace() } + fn match_pseudo_element(&self, + _pseudo: &PseudoElement, + _context: &mut MatchingContext) + -> bool + { + false + } + + fn attr_matches(&self, + ns: &NamespaceConstraint<&Namespace>, + local_name: &LocalName, + operation: &AttrSelectorOperation<&String>) + -> bool { + match *ns { + NamespaceConstraint::Specific(ref ns) => { + self.get_attr_enum(ns, local_name) + .map_or(false, |value| value.eval_selector(operation)) + } + NamespaceConstraint::Any => { + let values = unsafe { + (*self.element.element.unsafe_get()).get_attr_vals_for_layout(local_name) + }; + values.iter().any(|v| v.eval_selector(operation)) + } + } + } + fn match_non_ts_pseudo_class<F>(&self, _: &NonTSPseudoClass, - _: &mut StyleRelations, + _: &mut MatchingContext, _: &mut F) -> bool where F: FnMut(&Self, ElementSelectorFlags), @@ -1181,11 +1210,6 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { warn!("ServoThreadSafeLayoutElement::is_root called"); false } - - fn each_class<F>(&self, _callback: F) - where F: FnMut(&Atom) { - warn!("ServoThreadSafeLayoutElement::each_class called"); - } } impl<'le> PresentationalHintsSynthesizer for ServoThreadSafeLayoutElement<'le> { diff --git a/components/script/lib.rs b/components/script/lib.rs index c1bd886b927..703209604b0 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -10,10 +10,12 @@ #![feature(nonzero)] #![feature(on_unimplemented)] #![feature(optin_builtin_traits)] +#![feature(option_entry)] #![feature(plugin)] #![feature(proc_macro)] #![feature(stmt_expr_attributes)] #![feature(try_from)] +#![feature(unboxed_closures)] #![feature(untagged_unions)] #![deny(unsafe_code)] @@ -46,7 +48,7 @@ extern crate encoding; extern crate euclid; extern crate fnv; extern crate gfx_traits; -extern crate heapsize; +#[macro_use] extern crate heapsize; #[macro_use] extern crate heapsize_derive; #[macro_use] extern crate html5ever; #[macro_use] @@ -92,6 +94,7 @@ extern crate smallvec; #[macro_use] extern crate style; extern crate style_traits; +extern crate swapper; extern crate time; #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))] extern crate tinyfiledialogs; diff --git a/components/script/microtask.rs b/components/script/microtask.rs index da04d1ed6ab..7fadf111ddb 100644 --- a/components/script/microtask.rs +++ b/components/script/microtask.rs @@ -11,6 +11,7 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::PromiseBinding::PromiseJobCallback; use dom::bindings::js::Root; use dom::globalscope::GlobalScope; +use dom::mutationobserver::MutationObserver; use msg::constellation_msg::PipelineId; use std::cell::Cell; use std::mem; @@ -28,6 +29,7 @@ pub struct MicrotaskQueue { #[derive(JSTraceable, HeapSizeOf)] pub enum Microtask { Promise(EnqueuedPromiseCallback), + NotifyMutationObservers, } /// A promise callback scheduled to run during the next microtask checkpoint (#4283). @@ -71,6 +73,9 @@ impl MicrotaskQueue { let _ = job.callback.Call_(&*target, ExceptionHandling::Report); } } + Microtask::NotifyMutationObservers => { + MutationObserver::notify_mutation_observers(); + } } } } diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs index c4f90edf8ea..8c243b18695 100644 --- a/components/script/script_runtime.rs +++ b/components/script/script_runtime.rs @@ -74,6 +74,7 @@ pub enum ScriptThreadEventCategory { UpdateReplacedElement, WebSocketEvent, WorkerEvent, + WorkletEvent, ServiceWorkerEvent, EnterFullscreen, ExitFullscreen, diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index b817fc77d1d..8a6efa946ad 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -56,6 +56,8 @@ use dom::uievent::UIEvent; use dom::window::{ReflowReason, Window}; use dom::windowproxy::WindowProxy; use dom::worker::TrustedWorkerAddress; +use dom::worklet::WorkletThreadPool; +use dom::workletglobalscope::WorkletGlobalScopeInit; use euclid::Rect; use euclid::point::Point2D; use hyper::header::{ContentType, HttpDate, LastModified, Headers}; @@ -71,7 +73,7 @@ use js::jsval::UndefinedValue; use js::rust::Runtime; use mem::heap_size_of_self_and_children; use microtask::{MicrotaskQueue, Microtask}; -use msg::constellation_msg::{FrameId, FrameType, PipelineId, PipelineNamespace}; +use msg::constellation_msg::{BrowsingContextId, FrameType, PipelineId, PipelineNamespace}; use net_traits::{CoreResourceMsg, FetchMetadata, FetchResponseListener}; use net_traits::{IpcSend, Metadata, ReferrerPolicy, ResourceThreads}; use net_traits::image_cache::{ImageCache, PendingImageResponse}; @@ -142,7 +144,7 @@ struct InProgressLoad { /// The pipeline which requested this load. pipeline_id: PipelineId, /// The frame being loaded into. - frame_id: FrameId, + browsing_context_id: BrowsingContextId, /// The parent pipeline and frame type associated with this load, if any. parent_info: Option<(PipelineId, FrameType)>, /// The current window size associated with this pipeline. @@ -162,7 +164,7 @@ struct InProgressLoad { impl InProgressLoad { /// Create a new InProgressLoad object. fn new(id: PipelineId, - frame_id: FrameId, + browsing_context_id: BrowsingContextId, parent_info: Option<(PipelineId, FrameType)>, layout_chan: Sender<message::Msg>, window_size: Option<WindowSizeData>, @@ -170,7 +172,7 @@ impl InProgressLoad { origin: MutableOrigin) -> InProgressLoad { InProgressLoad { pipeline_id: id, - frame_id: frame_id, + browsing_context_id: browsing_context_id, parent_info: parent_info, layout_chan: layout_chan, window_size: window_size, @@ -368,8 +370,10 @@ impl Documents { self.find_window(pipeline_id).map(|window| Root::from_ref(window.upcast())) } - pub fn find_iframe(&self, pipeline_id: PipelineId, frame_id: FrameId) -> Option<Root<HTMLIFrameElement>> { - self.find_document(pipeline_id).and_then(|doc| doc.find_iframe(frame_id)) + pub fn find_iframe(&self, pipeline_id: PipelineId, browsing_context_id: BrowsingContextId) + -> Option<Root<HTMLIFrameElement>> + { + self.find_document(pipeline_id).and_then(|doc| doc.find_iframe(browsing_context_id)) } pub fn iter<'a>(&'a self) -> DocumentsIter<'a> { @@ -400,7 +404,7 @@ pub struct ScriptThread { documents: DOMRefCell<Documents>, /// The window proxies known by this thread /// TODO: this map grows, but never shrinks. Issue #15258. - window_proxies: DOMRefCell<HashMap<FrameId, JS<WindowProxy>>>, + window_proxies: DOMRefCell<HashMap<BrowsingContextId, JS<WindowProxy>>>, /// A list of data pertaining to loads that have not yet received a network response incomplete_loads: DOMRefCell<Vec<InProgressLoad>>, /// A map to store service worker registrations for a given origin @@ -479,12 +483,18 @@ pub struct ScriptThread { microtask_queue: MicrotaskQueue, + /// Microtask Queue for adding support for mutation observer microtasks + mutation_observer_compound_microtask_queued: Cell<bool>, + /// The unit of related similar-origin browsing contexts' list of MutationObserver objects mutation_observers: DOMRefCell<Vec<JS<MutationObserver>>>, /// A handle to the webvr thread, if available webvr_thread: Option<IpcSender<WebVRMsg>>, + /// The worklet thread pool + worklet_thread_pool: DOMRefCell<Option<Rc<WorkletThreadPool>>>, + /// A list of pipelines containing documents that finished loading all their blocking /// resources during a turn of the event loop. docs_with_no_blocking_loads: DOMRefCell<HashSet<JS<Document>>>, @@ -538,11 +548,11 @@ impl ScriptThreadFactory for ScriptThread { thread::Builder::new().name(format!("ScriptThread {:?}", state.id)).spawn(move || { thread_state::initialize(thread_state::SCRIPT); PipelineNamespace::install(state.pipeline_namespace_id); - FrameId::install(state.top_level_frame_id); + BrowsingContextId::install(state.top_level_browsing_context_id); let roots = RootCollection::new(); let _stack_roots_tls = StackRootTLS::new(&roots); let id = state.id; - let frame_id = state.frame_id; + let browsing_context_id = state.browsing_context_id; let parent_info = state.parent_info; let mem_profiler_chan = state.mem_profiler_chan.clone(); let window_size = state.window_size; @@ -557,7 +567,7 @@ impl ScriptThreadFactory for ScriptThread { let mut failsafe = ScriptMemoryFailsafe::new(&script_thread); let origin = MutableOrigin::new(load_data.url.origin()); - let new_load = InProgressLoad::new(id, frame_id, parent_info, + let new_load = InProgressLoad::new(id, browsing_context_id, parent_info, layout_chan, window_size, load_data.url.clone(), origin); script_thread.start_page_load(new_load, load_data); @@ -587,6 +597,20 @@ impl ScriptThread { }) } + pub fn set_mutation_observer_compound_microtask_queued(value: bool) { + SCRIPT_THREAD_ROOT.with(|root| { + let script_thread = unsafe { &*root.get().unwrap() }; + script_thread.mutation_observer_compound_microtask_queued.set(value); + }) + } + + pub fn is_mutation_observer_compound_microtask_queued() -> bool { + SCRIPT_THREAD_ROOT.with(|root| { + let script_thread = unsafe { &*root.get().unwrap() }; + return script_thread.mutation_observer_compound_microtask_queued.get(); + }) + } + pub fn add_mutation_observer(observer: &MutationObserver) { SCRIPT_THREAD_ROOT.with(|root| { let script_thread = unsafe { &*root.get().unwrap() }; @@ -596,6 +620,13 @@ impl ScriptThread { }) } + pub fn get_mutation_observers() -> Vec<Root<MutationObserver>> { + SCRIPT_THREAD_ROOT.with(|root| { + let script_thread = unsafe { &*root.get().unwrap() }; + script_thread.mutation_observers.borrow().iter().map(|o| Root::from_ref(&**o)).collect() + }) + } + pub fn mark_document_with_no_blocked_loads(doc: &Document) { SCRIPT_THREAD_ROOT.with(|root| { let script_thread = unsafe { &*root.get().unwrap() }; @@ -669,7 +700,7 @@ impl ScriptThread { })) } - pub fn find_window_proxy(id: FrameId) -> Option<Root<WindowProxy>> { + pub fn find_window_proxy(id: BrowsingContextId) -> Option<Root<WindowProxy>> { SCRIPT_THREAD_ROOT.with(|root| root.get().and_then(|script_thread| { let script_thread = unsafe { &*script_thread }; script_thread.window_proxies.borrow().get(&id) @@ -677,6 +708,24 @@ impl ScriptThread { })) } + pub fn worklet_thread_pool() -> Rc<WorkletThreadPool> { + SCRIPT_THREAD_ROOT.with(|root| { + let script_thread = unsafe { &*root.get().unwrap() }; + script_thread.worklet_thread_pool.borrow_mut().get_or_insert_with(|| { + let chan = script_thread.chan.0.clone(); + let init = WorkletGlobalScopeInit { + resource_threads: script_thread.resource_threads.clone(), + mem_profiler_chan: script_thread.mem_profiler_chan.clone(), + time_profiler_chan: script_thread.time_profiler_chan.clone(), + devtools_chan: script_thread.devtools_chan.clone(), + constellation_chan: script_thread.constellation_chan.clone(), + scheduler_chan: script_thread.scheduler_chan.clone(), + }; + Rc::new(WorkletThreadPool::spawn(chan, init)) + }).clone() + }) + } + /// Creates a new script thread. pub fn new(state: InitialScriptState, port: Receiver<MainThreadScriptMsg>, @@ -748,12 +797,16 @@ impl ScriptThread { microtask_queue: MicrotaskQueue::default(), + mutation_observer_compound_microtask_queued: Default::default(), + mutation_observers: Default::default(), layout_to_constellation_chan: state.layout_to_constellation_chan, webvr_thread: state.webvr_thread, + worklet_thread_pool: Default::default(), + docs_with_no_blocking_loads: Default::default(), transitioning_nodes: Default::default(), @@ -1037,6 +1090,7 @@ impl ScriptThread { ScriptThreadEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent, ScriptThreadEventCategory::WebVREvent => ProfilerCategory::ScriptWebVREvent, ScriptThreadEventCategory::WorkerEvent => ProfilerCategory::ScriptWorkerEvent, + ScriptThreadEventCategory::WorkletEvent => ProfilerCategory::ScriptWorkletEvent, ScriptThreadEventCategory::ServiceWorkerEvent => ProfilerCategory::ScriptServiceWorkerEvent, ScriptThreadEventCategory::EnterFullscreen => ProfilerCategory::ScriptEnterFullscreen, ScriptThreadEventCategory::ExitFullscreen => ProfilerCategory::ScriptExitFullscreen, @@ -1049,8 +1103,8 @@ impl ScriptThread { fn handle_msg_from_constellation(&self, msg: ConstellationControlMsg) { match msg { - ConstellationControlMsg::Navigate(parent_pipeline_id, frame_id, load_data, replace) => - self.handle_navigate(parent_pipeline_id, Some(frame_id), load_data, replace), + ConstellationControlMsg::Navigate(parent_pipeline_id, browsing_context_id, load_data, replace) => + self.handle_navigate(parent_pipeline_id, Some(browsing_context_id), load_data, replace), ConstellationControlMsg::SendEvent(id, event) => self.handle_event(id, event), ConstellationControlMsg::ResizeInactive(id, new_size) => @@ -1061,22 +1115,22 @@ impl ScriptThread { self.handle_set_document_activity_msg(pipeline_id, activity), ConstellationControlMsg::ChangeFrameVisibilityStatus(pipeline_id, visible) => self.handle_visibility_change_msg(pipeline_id, visible), - ConstellationControlMsg::NotifyVisibilityChange(parent_pipeline_id, frame_id, visible) => - self.handle_visibility_change_complete_msg(parent_pipeline_id, frame_id, visible), + ConstellationControlMsg::NotifyVisibilityChange(parent_pipeline_id, browsing_context_id, visible) => + self.handle_visibility_change_complete_msg(parent_pipeline_id, browsing_context_id, visible), ConstellationControlMsg::PostMessage(pipeline_id, origin, data) => self.handle_post_message_msg(pipeline_id, origin, data), ConstellationControlMsg::MozBrowserEvent(parent_pipeline_id, - frame_id, + browsing_context_id, event) => self.handle_mozbrowser_event_msg(parent_pipeline_id, - frame_id, + browsing_context_id, event), ConstellationControlMsg::UpdatePipelineId(parent_pipeline_id, - frame_id, + browsing_context_id, new_pipeline_id, reason) => self.handle_update_pipeline_id(parent_pipeline_id, - frame_id, + browsing_context_id, new_pipeline_id, reason), ConstellationControlMsg::FocusIFrame(parent_pipeline_id, frame_id) => @@ -1089,9 +1143,9 @@ impl ScriptThread { self.handle_transition_event(unsafe_node, name, duration), ConstellationControlMsg::WebFontLoaded(pipeline_id) => self.handle_web_font_loaded(pipeline_id), - ConstellationControlMsg::DispatchFrameLoadEvent { - target: frame_id, parent: parent_id, child: child_id } => - self.handle_frame_load_event(parent_id, frame_id, child_id), + ConstellationControlMsg::DispatchIFrameLoadEvent { + target: browsing_context_id, parent: parent_id, child: child_id } => + self.handle_iframe_load_event(parent_id, browsing_context_id, child_id), ConstellationControlMsg::DispatchStorageEvent(pipeline_id, storage, url, key, old_value, new_value) => self.handle_storage_event(pipeline_id, storage, url, key, old_value, new_value), ConstellationControlMsg::ReportCSSError(pipeline_id, filename, line, column, msg) => @@ -1121,7 +1175,7 @@ impl ScriptThread { // The category of the runnable is ignored by the pattern, however // it is still respected by profiling (see categorize_msg). if !runnable.is_cancelled() { - runnable.handler() + runnable.main_thread_handler(self) } } MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) => @@ -1224,8 +1278,8 @@ impl ScriptThread { webdriver_handlers::handle_get_rect(&*documents, pipeline_id, node_id, reply), WebDriverScriptCommand::GetElementText(node_id, reply) => webdriver_handlers::handle_get_text(&*documents, pipeline_id, node_id, reply), - WebDriverScriptCommand::GetFrameId(frame_id, reply) => - webdriver_handlers::handle_get_frame_id(&*documents, pipeline_id, frame_id, reply), + WebDriverScriptCommand::GetPipelineId(browsing_context_id, reply) => + webdriver_handlers::handle_get_pipeline_id(&*documents, pipeline_id, browsing_context_id, reply), WebDriverScriptCommand::GetUrl(reply) => webdriver_handlers::handle_get_url(&*documents, pipeline_id, reply), WebDriverScriptCommand::IsEnabled(element_id, reply) => @@ -1292,7 +1346,7 @@ impl ScriptThread { let NewLayoutInfo { parent_info, new_pipeline_id, - frame_id, + browsing_context_id, load_data, window_size, pipeline_port, @@ -1328,7 +1382,7 @@ impl ScriptThread { }; // Kick off the fetch for the new resource. - let new_load = InProgressLoad::new(new_pipeline_id, frame_id, parent_info, + let new_load = InProgressLoad::new(new_pipeline_id, browsing_context_id, parent_info, layout_chan, window_size, load_data.url.clone(), origin); if load_data.url.as_str() == "about:blank" { @@ -1369,8 +1423,12 @@ impl ScriptThread { } /// Updates iframe element after a change in visibility - fn handle_visibility_change_complete_msg(&self, parent_pipeline_id: PipelineId, id: FrameId, visible: bool) { - let iframe = self.documents.borrow().find_iframe(parent_pipeline_id, id); + fn handle_visibility_change_complete_msg(&self, + parent_pipeline_id: PipelineId, + browsing_context_id: BrowsingContextId, + visible: bool) + { + let iframe = self.documents.borrow().find_iframe(parent_pipeline_id, browsing_context_id); if let Some(iframe) = iframe { iframe.change_visibility_status(visible); } @@ -1418,9 +1476,9 @@ impl ScriptThread { fn handle_focus_iframe_msg(&self, parent_pipeline_id: PipelineId, - frame_id: FrameId) { + browsing_context_id: BrowsingContextId) { let doc = self.documents.borrow().find_document(parent_pipeline_id).unwrap(); - let frame_element = doc.find_iframe(frame_id); + let frame_element = doc.find_iframe(browsing_context_id); if let Some(ref frame_element) = frame_element { doc.begin_focus_transaction(); @@ -1440,17 +1498,17 @@ impl ScriptThread { /// https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowserloadstart fn handle_mozbrowser_event_msg(&self, parent_pipeline_id: PipelineId, - frame_id: Option<FrameId>, + browsing_context_id: Option<BrowsingContextId>, event: MozBrowserEvent) { let doc = match { self.documents.borrow().find_document(parent_pipeline_id) } { - None => return warn!("Mozbrowser event after pipeline {:?} closed.", parent_pipeline_id), + None => return warn!("Mozbrowser event after pipeline {} closed.", parent_pipeline_id), Some(doc) => doc, }; - match frame_id { + match browsing_context_id { None => doc.window().dispatch_mozbrowser_event(event), - Some(frame_id) => match doc.find_iframe(frame_id) { - None => warn!("Mozbrowser event after iframe {:?}/{:?} closed.", parent_pipeline_id, frame_id), + Some(browsing_context_id) => match doc.find_iframe(browsing_context_id) { + None => warn!("Mozbrowser event after iframe {}/{} closed.", parent_pipeline_id, browsing_context_id), Some(frame_element) => frame_element.dispatch_mozbrowser_event(event), }, } @@ -1458,10 +1516,10 @@ impl ScriptThread { fn handle_update_pipeline_id(&self, parent_pipeline_id: PipelineId, - frame_id: FrameId, + browsing_context_id: BrowsingContextId, new_pipeline_id: PipelineId, reason: UpdatePipelineIdReason) { - let frame_element = self.documents.borrow().find_iframe(parent_pipeline_id, frame_id); + let frame_element = self.documents.borrow().find_iframe(parent_pipeline_id, browsing_context_id); if let Some(frame_element) = frame_element { frame_element.update_pipeline_id(new_pipeline_id, reason); } @@ -1690,18 +1748,22 @@ impl ScriptThread { storage.queue_storage_event(url, key, old_value, new_value); } - /// Notify the containing document of a child frame that has completed loading. - fn handle_frame_load_event(&self, parent_id: PipelineId, frame_id: FrameId, child_id: PipelineId) { - let iframe = self.documents.borrow().find_iframe(parent_id, frame_id); + /// Notify the containing document of a child iframe that has completed loading. + fn handle_iframe_load_event(&self, + parent_id: PipelineId, + browsing_context_id: BrowsingContextId, + child_id: PipelineId) + { + let iframe = self.documents.borrow().find_iframe(parent_id, browsing_context_id); match iframe { Some(iframe) => iframe.iframe_load_event_steps(child_id), None => warn!("Message sent to closed pipeline {}.", parent_id), } } - fn ask_constellation_for_frame_id(&self, pipeline_id: PipelineId) -> Option<FrameId> { + fn ask_constellation_for_browsing_context_id(&self, pipeline_id: PipelineId) -> Option<BrowsingContextId> { let (result_sender, result_receiver) = ipc::channel().unwrap(); - let msg = ConstellationMsg::GetFrameId(pipeline_id, result_sender); + let msg = ConstellationMsg::GetBrowsingContextId(pipeline_id, result_sender); self.constellation_chan.send(msg).expect("Failed to send to constellation."); result_receiver.recv().expect("Failed to get frame id from constellation.") } @@ -1724,19 +1786,19 @@ impl ScriptThread { pipeline_id: PipelineId) -> Option<Root<WindowProxy>> { - let frame_id = match self.ask_constellation_for_frame_id(pipeline_id) { - Some(frame_id) => frame_id, + let browsing_context_id = match self.ask_constellation_for_browsing_context_id(pipeline_id) { + Some(browsing_context_id) => browsing_context_id, None => return None, }; - if let Some(window_proxy) = self.window_proxies.borrow().get(&frame_id) { + if let Some(window_proxy) = self.window_proxies.borrow().get(&browsing_context_id) { return Some(Root::from_ref(window_proxy)); } let parent = match self.ask_constellation_for_parent_info(pipeline_id) { Some((parent_id, FrameType::IFrame)) => self.remote_window_proxy(global_to_clone, parent_id), _ => None, }; - let window_proxy = WindowProxy::new_dissimilar_origin(global_to_clone, frame_id, parent.r()); - self.window_proxies.borrow_mut().insert(frame_id, JS::from_ref(&*window_proxy)); + let window_proxy = WindowProxy::new_dissimilar_origin(global_to_clone, browsing_context_id, parent.r()); + self.window_proxies.borrow_mut().insert(browsing_context_id, JS::from_ref(&*window_proxy)); Some(window_proxy) } @@ -1748,16 +1810,16 @@ impl ScriptThread { // to the `window_proxies` map, and return it. fn local_window_proxy(&self, window: &Window, - frame_id: FrameId, + browsing_context_id: BrowsingContextId, parent_info: Option<(PipelineId, FrameType)>) -> Root<WindowProxy> { - if let Some(window_proxy) = self.window_proxies.borrow().get(&frame_id) { + if let Some(window_proxy) = self.window_proxies.borrow().get(&browsing_context_id) { window_proxy.set_currently_active(&*window); return Root::from_ref(window_proxy); } let iframe = match parent_info { - Some((parent_id, FrameType::IFrame)) => self.documents.borrow().find_iframe(parent_id, frame_id), + Some((parent_id, FrameType::IFrame)) => self.documents.borrow().find_iframe(parent_id, browsing_context_id), _ => None, }; let parent = match (parent_info, iframe.as_ref()) { @@ -1765,8 +1827,8 @@ impl ScriptThread { (Some((parent_id, FrameType::IFrame)), _) => self.remote_window_proxy(window.upcast(), parent_id), _ => None, }; - let window_proxy = WindowProxy::new(&window, frame_id, iframe.r().map(Castable::upcast), parent.r()); - self.window_proxies.borrow_mut().insert(frame_id, JS::from_ref(&*window_proxy)); + let window_proxy = WindowProxy::new(&window, browsing_context_id, iframe.r().map(Castable::upcast), parent.r()); + self.window_proxies.borrow_mut().insert(browsing_context_id, JS::from_ref(&*window_proxy)); window_proxy } @@ -1823,7 +1885,7 @@ impl ScriptThread { self.webvr_thread.clone()); // Initialize the browsing context for the window. - let window_proxy = self.local_window_proxy(&window, incomplete.frame_id, incomplete.parent_info); + let window_proxy = self.local_window_proxy(&window, incomplete.browsing_context_id, incomplete.parent_info); window.init_window_proxy(&window_proxy); let last_modified = metadata.headers.as_ref().and_then(|headers| { @@ -2093,12 +2155,12 @@ impl ScriptThread { /// The entry point for content to notify that a new load has been requested /// for the given pipeline (specifically the "navigate" algorithm). fn handle_navigate(&self, parent_pipeline_id: PipelineId, - frame_id: Option<FrameId>, + browsing_context_id: Option<BrowsingContextId>, load_data: LoadData, replace: bool) { - match frame_id { - Some(frame_id) => { - let iframe = self.documents.borrow().find_iframe(parent_pipeline_id, frame_id); + match browsing_context_id { + Some(browsing_context_id) => { + let iframe = self.documents.borrow().find_iframe(parent_pipeline_id, browsing_context_id); if let Some(iframe) = iframe { iframe.navigate_or_reload_child_browsing_context(Some(load_data), NavigationType::Regular, replace); } diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index 7e7fe95122b..7bc31263c25 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -109,10 +109,10 @@ pub fn handle_execute_async_script(documents: &Documents, window.upcast::<GlobalScope>().evaluate_js_on_global_with_result(&eval, rval.handle_mut()); } -pub fn handle_get_frame_id(documents: &Documents, - pipeline: PipelineId, - webdriver_frame_id: WebDriverFrameId, - reply: IpcSender<Result<Option<PipelineId>, ()>>) { +pub fn handle_get_pipeline_id(documents: &Documents, + pipeline: PipelineId, + webdriver_frame_id: WebDriverFrameId, + reply: IpcSender<Result<Option<PipelineId>, ()>>) { let result = match webdriver_frame_id { WebDriverFrameId::Short(_) => { // This isn't supported yet diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs index 9219f1e4b09..40b719f6dbd 100644 --- a/components/script_layout_interface/wrapper_traits.rs +++ b/components/script_layout_interface/wrapper_traits.rs @@ -11,16 +11,16 @@ use SVGSVGData; use atomic_refcell::AtomicRefCell; use gfx_traits::{ByteIndex, FragmentType, combine_id_with_fragment_type}; use html5ever::{Namespace, LocalName}; -use msg::constellation_msg::{FrameId, PipelineId}; +use msg::constellation_msg::{BrowsingContextId, PipelineId}; use range::Range; use servo_url::ServoUrl; use std::fmt::Debug; +use style::attr::AttrValue; use style::computed_values::display; use style::context::SharedStyleContext; use style::data::ElementData; use style::dom::{LayoutIterator, NodeInfo, PresentationalHintsSynthesizer, TNode}; use style::dom::OpaqueNode; -use style::element_state::ElementState; use style::font_metrics::ServoMetricsProvider; use style::properties::{CascadeFlags, ServoComputedValues}; use style::selector_parser::{PseudoElement, PseudoElementCascadeType, SelectorImpl}; @@ -272,9 +272,9 @@ pub trait ThreadSafeLayoutNode: Clone + Copy + Debug + GetLayoutData + NodeInfo fn svg_data(&self) -> Option<SVGSVGData>; - /// If this node is an iframe element, returns its frame ID. If this node is + /// If this node is an iframe element, returns its browsing context ID. If this node is /// not an iframe element, fails. - fn iframe_frame_id(&self) -> FrameId; + fn iframe_browsing_context_id(&self) -> BrowsingContextId; /// If this node is an iframe element, returns its pipeline ID. If this node is /// not an iframe element, fails. @@ -336,6 +336,8 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug + #[inline] fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&str>; + fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue>; + fn get_style_data(&self) -> Option<&AtomicRefCell<ElementData>>; #[inline] @@ -435,7 +437,6 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug + &context.guards, unsafe { &self.unsafe_get() }, &style_pseudo, - ElementState::empty(), data.styles().primary.values(), &ServoMetricsProvider); data.styles_mut().cached_pseudos diff --git a/components/script_traits/Cargo.toml b/components/script_traits/Cargo.toml index da5fcc78704..993c14c43b0 100644 --- a/components/script_traits/Cargo.toml +++ b/components/script_traits/Cargo.toml @@ -10,7 +10,6 @@ name = "script_traits" path = "lib.rs" [dependencies] -app_units = "0.4" bluetooth_traits = {path = "../bluetooth_traits"} canvas_traits = {path = "../canvas_traits"} cookie = "0.6" diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index bfc1e63fcc8..889a06e5cb3 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -53,7 +53,7 @@ use hyper::header::Headers; use hyper::method::Method; use ipc_channel::ipc::{IpcReceiver, IpcSender}; use libc::c_void; -use msg::constellation_msg::{FrameId, FrameType, Key, KeyModifiers, KeyState}; +use msg::constellation_msg::{BrowsingContextId, FrameType, Key, KeyModifiers, KeyState}; use msg::constellation_msg::{PipelineId, PipelineNamespaceId, TraversalDirection}; use net_traits::{ReferrerPolicy, ResourceThreads}; use net_traits::image::base::Image; @@ -179,8 +179,8 @@ pub struct NewLayoutInfo { pub parent_info: Option<(PipelineId, FrameType)>, /// Id of the newly-created pipeline. pub new_pipeline_id: PipelineId, - /// Id of the frame associated with this pipeline. - pub frame_id: FrameId, + /// Id of the browsing context associated with this pipeline. + pub browsing_context_id: BrowsingContextId, /// Network request data which will be initiated by the script thread. pub load_data: LoadData, /// Information about the initial window size. @@ -253,22 +253,22 @@ pub enum ConstellationControlMsg { /// Notifies script thread whether frame is visible ChangeFrameVisibilityStatus(PipelineId, bool), /// Notifies script thread that frame visibility change is complete - /// PipelineId is for the parent, FrameId is for the actual frame. - NotifyVisibilityChange(PipelineId, FrameId, bool), + /// PipelineId is for the parent, BrowsingContextId is for the nested browsing context + NotifyVisibilityChange(PipelineId, BrowsingContextId, bool), /// Notifies script thread that a url should be loaded in this iframe. - /// PipelineId is for the parent, FrameId is for the actual frame. - Navigate(PipelineId, FrameId, LoadData, bool), + /// PipelineId is for the parent, BrowsingContextId is for the nested browsing context + Navigate(PipelineId, BrowsingContextId, LoadData, bool), /// Post a message to a given window. PostMessage(PipelineId, Option<ImmutableOrigin>, Vec<u8>), /// Requests the script thread forward a mozbrowser event to an iframe it owns, - /// or to the window if no child frame id is provided. - MozBrowserEvent(PipelineId, Option<FrameId>, MozBrowserEvent), + /// or to the window if no browsing context id is provided. + MozBrowserEvent(PipelineId, Option<BrowsingContextId>, MozBrowserEvent), /// Updates the current pipeline ID of a given iframe. /// First PipelineId is for the parent, second is the new PipelineId for the frame. - UpdatePipelineId(PipelineId, FrameId, PipelineId, UpdatePipelineIdReason), + UpdatePipelineId(PipelineId, BrowsingContextId, PipelineId, UpdatePipelineIdReason), /// Set an iframe to be focused. Used when an element in an iframe gains focus. - /// PipelineId is for the parent, FrameId is for the actual frame. - FocusIFrame(PipelineId, FrameId), + /// PipelineId is for the parent, BrowsingContextId is for the nested browsing context + FocusIFrame(PipelineId, BrowsingContextId), /// Passes a webdriver command to the script thread for execution WebDriverScriptCommand(PipelineId, WebDriverScriptCommand), /// Notifies script thread that all animations are done @@ -278,10 +278,10 @@ pub enum ConstellationControlMsg { /// Notifies the script thread that a new Web font has been loaded, and thus the page should be /// reflowed. WebFontLoaded(PipelineId), - /// Cause a `load` event to be dispatched at the appropriate frame element. - DispatchFrameLoadEvent { + /// Cause a `load` event to be dispatched at the appropriate iframe element. + DispatchIFrameLoadEvent { /// The frame that has been marked as loaded. - target: FrameId, + target: BrowsingContextId, /// The pipeline that contains a frame loading the target pipeline. parent: PipelineId, /// The pipeline that has completed loading. @@ -323,7 +323,7 @@ impl fmt::Debug for ConstellationControlMsg { TickAllAnimations(..) => "TickAllAnimations", TransitionEnd(..) => "TransitionEnd", WebFontLoaded(..) => "WebFontLoaded", - DispatchFrameLoadEvent { .. } => "DispatchFrameLoadEvent", + DispatchIFrameLoadEvent { .. } => "DispatchIFrameLoadEvent", DispatchStorageEvent(..) => "DispatchStorageEvent", ReportCSSError(..) => "ReportCSSError", Reload(..) => "Reload", @@ -489,9 +489,9 @@ pub struct InitialScriptState { /// If `None`, this is the root. pub parent_info: Option<(PipelineId, FrameType)>, /// The ID of the frame this script is part of. - pub frame_id: FrameId, + pub browsing_context_id: BrowsingContextId, /// The ID of the top-level frame this script is part of. - pub top_level_frame_id: FrameId, + pub top_level_browsing_context_id: BrowsingContextId, /// A channel with which messages can be sent to us (the script thread). pub control_chan: IpcSender<ConstellationControlMsg>, /// A port on which messages sent by the constellation to script can be received. @@ -549,7 +549,7 @@ pub struct IFrameLoadInfo { /// Pipeline ID of the parent of this iframe pub parent_pipeline_id: PipelineId, /// The ID for this iframe. - pub frame_id: FrameId, + pub browsing_context_id: BrowsingContextId, /// The new pipeline ID that the iframe has generated. pub new_pipeline_id: PipelineId, /// Whether this iframe should be considered private @@ -732,13 +732,13 @@ pub enum WebDriverCommandMsg { pub enum ConstellationMsg { /// Exit the constellation. Exit, - /// Request that the constellation send the FrameId corresponding to the document + /// Request that the constellation send the BrowsingContextId corresponding to the document /// with the provided pipeline id - GetFrame(PipelineId, IpcSender<Option<FrameId>>), + GetBrowsingContext(PipelineId, IpcSender<Option<BrowsingContextId>>), /// Request that the constellation send the current pipeline id for the provided frame /// id, or for the root frame if this is None, over a provided channel. /// Also returns a boolean saying whether the document has finished loading or not. - GetPipeline(Option<FrameId>, IpcSender<Option<PipelineId>>), + GetPipeline(Option<BrowsingContextId>, IpcSender<Option<PipelineId>>), /// Requests that the constellation inform the compositor of the title of the pipeline /// immediately. GetPipelineTitle(PipelineId), @@ -760,8 +760,8 @@ pub enum ConstellationMsg { WebDriverCommand(WebDriverCommandMsg), /// Reload the current page. Reload, - /// A log entry, with the top-level frame id and thread name - LogEntry(Option<FrameId>, Option<String>, LogEntry), + /// A log entry, with the top-level browsing context id and thread name + LogEntry(Option<BrowsingContextId>, Option<String>, LogEntry), /// Set the WebVR thread channel. SetWebVRThread(IpcSender<WebVRMsg>), /// Dispatch WebVR events to the subscribed script threads. diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index 0402e3ddad6..d34f2feeb5f 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -17,7 +17,7 @@ use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId}; use euclid::point::Point2D; use euclid::size::{Size2D, TypedSize2D}; use ipc_channel::ipc::IpcSender; -use msg::constellation_msg::{FrameId, FrameType, PipelineId, TraversalDirection}; +use msg::constellation_msg::{BrowsingContextId, FrameType, PipelineId, TraversalDirection}; use msg::constellation_msg::{Key, KeyModifiers, KeyState}; use net_traits::CoreResourceMsg; use net_traits::storage_thread::StorageType; @@ -34,8 +34,8 @@ use webrender_traits::ClipId; pub enum LayoutMsg { /// Indicates whether this pipeline is currently running animations. ChangeRunningAnimationsState(PipelineId, AnimationState), - /// Inform the constellation of the size of the frame's viewport. - FrameSizes(Vec<(FrameId, TypedSize2D<f32, CSSPixel>)>), + /// Inform the constellation of the size of the iframe's viewport. + IFrameSizes(Vec<(BrowsingContextId, TypedSize2D<f32, CSSPixel>)>), /// Requests that the constellation inform the compositor of the a cursor change. SetCursor(Cursor), /// Notifies the constellation that the viewport has been constrained in some manner @@ -87,7 +87,7 @@ pub enum ScriptMsg { /// Requests that the constellation retrieve the current contents of the clipboard GetClipboardContents(IpcSender<String>), /// Get the frame id for a given pipeline. - GetFrameId(PipelineId, IpcSender<Option<FrameId>>), + GetBrowsingContextId(PipelineId, IpcSender<Option<BrowsingContextId>>), /// Get the parent info for a given pipeline. GetParentInfo(PipelineId, IpcSender<Option<(PipelineId, FrameType)>>), /// <head> tag finished parsing @@ -99,7 +99,7 @@ pub enum ScriptMsg { /// instead of adding a new entry. LoadUrl(PipelineId, LoadData, bool), /// Post a message to the currently active window of a given browsing context. - PostMessage(FrameId, Option<ImmutableOrigin>, Vec<u8>), + PostMessage(BrowsingContextId, Option<ImmutableOrigin>, Vec<u8>), /// Dispatch a mozbrowser event to the parent of this pipeline. /// The first PipelineId is for the parent, the second is for the originating pipeline. MozBrowserEvent(PipelineId, PipelineId, MozBrowserEvent), @@ -113,7 +113,7 @@ pub enum ScriptMsg { NodeStatus(Option<String>), /// Notification that this iframe should be removed. /// Returns a list of pipelines which were closed. - RemoveIFrame(FrameId, IpcSender<Vec<PipelineId>>), + RemoveIFrame(BrowsingContextId, IpcSender<Vec<PipelineId>>), /// Change pipeline visibility SetVisible(PipelineId, bool), /// Notifies constellation that an iframe's visibility has been changed. @@ -147,8 +147,8 @@ pub enum ScriptMsg { ResizeTo(Size2D<u32>), /// Script has handled a touch event, and either prevented or allowed default actions. TouchEventProcessed(EventResult), - /// A log entry, with the top-level frame id and thread name - LogEntry(Option<FrameId>, Option<String>, LogEntry), + /// A log entry, with the top-level browsing context id and thread name + LogEntry(Option<BrowsingContextId>, Option<String>, LogEntry), /// Notifies the constellation that this pipeline has exited. PipelineExited(PipelineId), /// Send messages from postMessage calls from serviceworker diff --git a/components/script_traits/webdriver_msg.rs b/components/script_traits/webdriver_msg.rs index c09f1f19dae..b92e32ff65c 100644 --- a/components/script_traits/webdriver_msg.rs +++ b/components/script_traits/webdriver_msg.rs @@ -31,7 +31,7 @@ pub enum WebDriverScriptCommand { GetElementRect(String, IpcSender<Result<Rect<f64>, ()>>), GetElementTagName(String, IpcSender<Result<String, ()>>), GetElementText(String, IpcSender<Result<String, ()>>), - GetFrameId(WebDriverFrameId, IpcSender<Result<Option<PipelineId>, ()>>), + GetPipelineId(WebDriverFrameId, IpcSender<Result<Option<PipelineId>, ()>>), GetUrl(IpcSender<ServoUrl>), IsEnabled(String, IpcSender<Result<bool, ()>>), IsSelected(String, IpcSender<Result<bool, ()>>), diff --git a/components/selectors/Cargo.toml b/components/selectors/Cargo.toml index ceef176eae1..4ca9a240c67 100644 --- a/components/selectors/Cargo.toml +++ b/components/selectors/Cargo.toml @@ -10,6 +10,7 @@ repository = "https://github.com/servo/servo" readme = "README.md" keywords = ["css", "selectors"] license = "MPL-2.0" +build = "build.rs" [lib] name = "selectors" @@ -25,8 +26,12 @@ bitflags = "0.7" matches = "0.1" cssparser = "0.13.3" fnv = "1.0" +phf = "0.7.18" precomputed-hash = "0.1" smallvec = "0.3" [dev-dependencies] size_of_test = {path = "../size_of_test"} + +[build-dependencies] +phf_codegen = "0.7.18" diff --git a/components/selectors/attr.rs b/components/selectors/attr.rs new file mode 100644 index 00000000000..274081ae9f7 --- /dev/null +++ b/components/selectors/attr.rs @@ -0,0 +1,190 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use cssparser::ToCss; +use parser::SelectorImpl; +use std::ascii::AsciiExt; +use std::fmt; + +#[derive(Eq, PartialEq, Clone)] +pub struct AttrSelectorWithNamespace<Impl: SelectorImpl> { + pub namespace: NamespaceConstraint<(Impl::NamespacePrefix, Impl::NamespaceUrl)>, + pub local_name: Impl::LocalName, + pub local_name_lower: Impl::LocalName, + pub operation: ParsedAttrSelectorOperation<Impl::AttrValue>, + pub never_matches: bool, +} + +impl<Impl: SelectorImpl> AttrSelectorWithNamespace<Impl> { + pub fn namespace(&self) -> NamespaceConstraint<&Impl::NamespaceUrl> { + match self.namespace { + NamespaceConstraint::Any => NamespaceConstraint::Any, + NamespaceConstraint::Specific((_, ref url)) => { + NamespaceConstraint::Specific(url) + } + } + } +} + +#[derive(Eq, PartialEq, Clone)] +pub enum NamespaceConstraint<NamespaceUrl> { + Any, + + /// Empty string for no namespace + Specific(NamespaceUrl), +} + +#[derive(Eq, PartialEq, Clone)] +pub enum ParsedAttrSelectorOperation<AttrValue> { + Exists, + WithValue { + operator: AttrSelectorOperator, + case_sensitivity: ParsedCaseSensitivity, + expected_value: AttrValue, + } +} + +#[derive(Eq, PartialEq, Clone)] +pub enum AttrSelectorOperation<AttrValue> { + Exists, + WithValue { + operator: AttrSelectorOperator, + case_sensitivity: CaseSensitivity, + expected_value: AttrValue, + } +} + +impl<AttrValue> AttrSelectorOperation<AttrValue> { + pub fn eval_str(&self, element_attr_value: &str) -> bool where AttrValue: AsRef<str> { + match *self { + AttrSelectorOperation::Exists => true, + AttrSelectorOperation::WithValue { operator, case_sensitivity, ref expected_value } => { + operator.eval_str(element_attr_value, expected_value.as_ref(), case_sensitivity) + } + } + } +} + +#[derive(Eq, PartialEq, Clone, Copy)] +pub enum AttrSelectorOperator { + Equal, + Includes, + DashMatch, + Prefix, + Substring, + Suffix, +} + +impl ToCss for AttrSelectorOperator { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + dest.write_str(match *self { + AttrSelectorOperator::Equal => " = ", + AttrSelectorOperator::Includes => " ~= ", + AttrSelectorOperator::DashMatch => " |= ", + AttrSelectorOperator::Prefix => " ^= ", + AttrSelectorOperator::Substring => " *= ", + AttrSelectorOperator::Suffix => " $= ", + }) + } +} + +impl AttrSelectorOperator { + pub fn eval_str(self, element_attr_value: &str, attr_selector_value: &str, + case_sensitivity: CaseSensitivity) -> bool { + let e = element_attr_value.as_bytes(); + let s = attr_selector_value.as_bytes(); + let case = case_sensitivity; + match self { + AttrSelectorOperator::Equal => { + case.eq(e, s) + } + AttrSelectorOperator::Prefix => { + e.len() >= s.len() && case.eq(&e[..s.len()], s) + } + AttrSelectorOperator::Suffix => { + e.len() >= s.len() && case.eq(&e[(e.len() - s.len())..], s) + } + AttrSelectorOperator::Substring => { + case.contains(element_attr_value, attr_selector_value) + } + AttrSelectorOperator::Includes => { + element_attr_value.split(SELECTOR_WHITESPACE) + .any(|part| case.eq(part.as_bytes(), s)) + } + AttrSelectorOperator::DashMatch => { + case.eq(e, s) || ( + e.get(s.len()) == Some(&b'-') && + case.eq(&e[..s.len()], s) + ) + } + } + } +} + +/// The definition of whitespace per CSS Selectors Level 3 § 4. +pub static SELECTOR_WHITESPACE: &'static [char] = &[' ', '\t', '\n', '\r', '\x0C']; + +#[derive(Eq, PartialEq, Clone, Copy, Debug)] +pub enum ParsedCaseSensitivity { + CaseSensitive, // Selectors spec says language-defined, but HTML says sensitive. + AsciiCaseInsensitive, + AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument, +} + +impl ParsedCaseSensitivity { + pub fn to_unconditional(self, is_html_element_in_html_document: bool) -> CaseSensitivity { + match self { + ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument + if is_html_element_in_html_document => { + CaseSensitivity::AsciiCaseInsensitive + } + ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => { + CaseSensitivity::CaseSensitive + } + ParsedCaseSensitivity::CaseSensitive => CaseSensitivity::CaseSensitive, + ParsedCaseSensitivity::AsciiCaseInsensitive => CaseSensitivity::AsciiCaseInsensitive, + } + } +} + +#[derive(Eq, PartialEq, Clone, Copy, Debug)] +pub enum CaseSensitivity { + CaseSensitive, // Selectors spec says language-defined, but HTML says sensitive. + AsciiCaseInsensitive, +} + +impl CaseSensitivity { + pub fn eq(self, a: &[u8], b: &[u8]) -> bool { + match self { + CaseSensitivity::CaseSensitive => a == b, + CaseSensitivity::AsciiCaseInsensitive => a.eq_ignore_ascii_case(b), + } + } + + pub fn contains(self, haystack: &str, needle: &str) -> bool { + match self { + CaseSensitivity::CaseSensitive => haystack.contains(needle), + CaseSensitivity::AsciiCaseInsensitive => { + if let Some((&n_first_byte, n_rest)) = needle.as_bytes().split_first() { + haystack.bytes().enumerate().any(|(i, byte)| { + if !byte.eq_ignore_ascii_case(&n_first_byte) { + return false + } + let after_this_byte = &haystack.as_bytes()[i + 1..]; + match after_this_byte.get(..n_rest.len()) { + None => false, + Some(haystack_slice) => { + haystack_slice.eq_ignore_ascii_case(n_rest) + } + } + }) + } else { + // any_str.contains("") == true, + // though these cases should be handled with *NeverMatches and never go here. + true + } + } + } + } +} diff --git a/components/selectors/build.rs b/components/selectors/build.rs new file mode 100644 index 00000000000..0d0a40256f3 --- /dev/null +++ b/components/selectors/build.rs @@ -0,0 +1,75 @@ +/* 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/. */ + +extern crate phf_codegen; + +use std::env; +use std::fs::File; +use std::io::{BufWriter, Write}; +use std::path::Path; + +fn main() { + let path = Path::new(&env::var("OUT_DIR").unwrap()) + .join("ascii_case_insensitive_html_attributes.rs"); + let mut file = BufWriter::new(File::create(&path).unwrap()); + + write!(&mut file, "{{ static SET: ::phf::Set<&'static str> = ", + ).unwrap(); + let mut set = phf_codegen::Set::new(); + for name in ASCII_CASE_INSENSITIVE_HTML_ATTRIBUTES.split_whitespace() { + set.entry(name); + } + set.build(&mut file).unwrap(); + write!(&mut file, "; &SET }}").unwrap(); +} + +/// https://html.spec.whatwg.org/multipage/#selectors +static ASCII_CASE_INSENSITIVE_HTML_ATTRIBUTES: &'static str = r#" + accept + accept-charset + align + alink + axis + bgcolor + charset + checked + clear + codetype + color + compact + declare + defer + dir + direction + disabled + enctype + face + frame + hreflang + http-equiv + lang + language + link + media + method + multiple + nohref + noresize + noshade + nowrap + readonly + rel + rev + rules + scope + scrolling + selected + shape + target + text + type + valign + valuetype + vlink +"#; diff --git a/components/selectors/gecko_like_types.rs b/components/selectors/gecko_like_types.rs index bbb78823c50..9ffffaf2aed 100644 --- a/components/selectors/gecko_like_types.rs +++ b/components/selectors/gecko_like_types.rs @@ -19,9 +19,6 @@ pub enum PseudoElement { B, } -#[derive(Eq, PartialEq, Clone, Debug)] -pub struct PseudoElementSelector(PseudoElement, u64); - #[derive(Eq, PartialEq, Clone, Debug, Default)] pub struct Atom(usize); diff --git a/components/selectors/lib.rs b/components/selectors/lib.rs index d352d2f1d3c..3e413c0c08b 100644 --- a/components/selectors/lib.rs +++ b/components/selectors/lib.rs @@ -6,11 +6,13 @@ #[macro_use] extern crate cssparser; #[macro_use] extern crate matches; extern crate fnv; +extern crate phf; extern crate precomputed_hash; #[cfg(test)] #[macro_use] extern crate size_of_test; extern crate smallvec; pub mod arcslice; +pub mod attr; pub mod bloom; pub mod matching; pub mod parser; @@ -21,4 +23,3 @@ pub mod visitor; pub use parser::{SelectorImpl, Parser, SelectorList}; pub use tree::Element; -pub use tree::{MatchAttr, MatchAttrGeneric}; diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index ada15089061..50b686f2558 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -1,8 +1,10 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use attr::{ParsedAttrSelectorOperation, AttrSelectorOperation, NamespaceConstraint}; use bloom::BloomFilter; -use parser::{CaseSensitivity, Combinator, ComplexSelector, Component, LocalName}; +use parser::{Combinator, ComplexSelector, Component, LocalName}; use parser::{Selector, SelectorInner, SelectorIter}; use std::borrow::Borrow; use tree::Element; @@ -17,6 +19,7 @@ bitflags! { /// the selector matching process. /// /// This is used to implement efficient sharing. + #[derive(Default)] pub flags StyleRelations: usize { /// Whether this element is affected by an ID selector. const AFFECTED_BY_ID_SELECTOR = 1 << 0, @@ -68,18 +71,67 @@ impl ElementSelectorFlags { } } +/// What kind of selector matching mode we should use. +/// +/// There are two modes of selector matching. The difference is only noticeable +/// in presence of pseudo-elements. +#[derive(Debug, PartialEq)] +pub enum MatchingMode { + /// Don't ignore any pseudo-element selectors. + Normal, + + /// Ignores any stateless pseudo-element selectors in the rightmost sequence + /// of simple selectors. + /// + /// This is useful, for example, to match against ::before when you aren't a + /// pseudo-element yourself. + /// + /// For example, in presence of `::before:hover`, it would never match, but + /// `::before` would be ignored as in "matching". + /// + /// It's required for all the selectors you match using this mode to have a + /// pseudo-element. + ForStatelessPseudoElement, +} + + +/// Data associated with the matching process for a element. This context is +/// used across many selectors for an element, so it's not appropriate for +/// transient data that applies to only a single selector. +pub struct MatchingContext<'a> { + /// Output that records certains relations between elements noticed during + /// matching (and also extended after matching). + pub relations: StyleRelations, + /// The matching mode we should use when matching selectors. + pub matching_mode: MatchingMode, + /// The bloom filter used to fast-reject selectors. + pub bloom_filter: Option<&'a BloomFilter>, +} + +impl<'a> MatchingContext<'a> { + /// Constructs a new `MatchingContext`. + pub fn new(matching_mode: MatchingMode, + bloom_filter: Option<&'a BloomFilter>) + -> Self + { + Self { + relations: StyleRelations::empty(), + matching_mode: matching_mode, + bloom_filter: bloom_filter, + } + } +} + pub fn matches_selector_list<E>(selector_list: &[Selector<E::Impl>], element: &E, - parent_bf: Option<&BloomFilter>) + context: &mut MatchingContext) -> bool where E: Element { selector_list.iter().any(|selector| { - selector.pseudo_element.is_none() && matches_selector(&selector.inner, element, - parent_bf, - &mut StyleRelations::empty(), + context, &mut |_, _| {}) }) } @@ -104,27 +156,6 @@ fn may_match<E>(sel: &SelectorInner<E::Impl>, true } -/// Determines whether the given element matches the given complex selector. -pub fn matches_selector<E, F>(selector: &SelectorInner<E::Impl>, - element: &E, - parent_bf: Option<&BloomFilter>, - relations: &mut StyleRelations, - flags_setter: &mut F) - -> bool - where E: Element, - F: FnMut(&E, ElementSelectorFlags), -{ - // Use the bloom filter to fast-reject. - if let Some(filter) = parent_bf { - if !may_match::<E>(selector, filter) { - return false; - } - } - - // Match the selector. - matches_complex_selector(&selector.complex, element, relations, flags_setter) -} - /// A result of selector matching, includes 3 failure types, /// /// NotMatchedAndRestartFromClosestLaterSibling @@ -175,18 +206,67 @@ enum SelectorMatchingResult { NotMatchedGlobally, } +/// Matches an inner selector. +pub fn matches_selector<E, F>(selector: &SelectorInner<E::Impl>, + element: &E, + context: &mut MatchingContext, + flags_setter: &mut F) + -> bool + where E: Element, + F: FnMut(&E, ElementSelectorFlags), +{ + // Use the bloom filter to fast-reject. + if let Some(filter) = context.bloom_filter { + if !may_match::<E>(&selector, filter) { + return false; + } + } + + matches_complex_selector(&selector.complex, element, context, flags_setter) +} + /// Matches a complex selector. -pub fn matches_complex_selector<E, F>(selector: &ComplexSelector<E::Impl>, +/// +/// Use `matches_selector` if you need to skip pseudos. +pub fn matches_complex_selector<E, F>(complex_selector: &ComplexSelector<E::Impl>, element: &E, - relations: &mut StyleRelations, + context: &mut MatchingContext, flags_setter: &mut F) -> bool - where E: Element, - F: FnMut(&E, ElementSelectorFlags), + where E: Element, + F: FnMut(&E, ElementSelectorFlags), { - match matches_complex_selector_internal(selector.iter(), + let mut iter = complex_selector.iter(); + + if cfg!(debug_assertions) { + if context.matching_mode == MatchingMode::ForStatelessPseudoElement { + assert!(complex_selector.iter().any(|c| { + matches!(*c, Component::PseudoElement(..)) + })); + } + } + + if context.matching_mode == MatchingMode::ForStatelessPseudoElement { + match *iter.next().unwrap() { + // Stateful pseudo, just don't match. + Component::NonTSPseudoClass(..) => return false, + Component::PseudoElement(..) => { + // Pseudo, just eat the whole sequence. + let next = iter.next(); + debug_assert!(next.is_none(), + "Someone messed up pseudo-element parsing?"); + + if iter.next_sequence().is_none() { + return true; + } + } + _ => panic!("Used MatchingMode::ForStatelessPseudoElement in a non-pseudo selector"), + } + } + + match matches_complex_selector_internal(iter, element, - relations, + context, flags_setter) { SelectorMatchingResult::Matched => true, _ => false @@ -195,14 +275,14 @@ pub fn matches_complex_selector<E, F>(selector: &ComplexSelector<E::Impl>, fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Impl>, element: &E, - relations: &mut StyleRelations, + context: &mut MatchingContext, flags_setter: &mut F) -> SelectorMatchingResult where E: Element, F: FnMut(&E, ElementSelectorFlags), { let matches_all_simple_selectors = selector_iter.all(|simple| { - matches_simple_selector(simple, element, relations, flags_setter) + matches_simple_selector(simple, element, context, flags_setter) }); let combinator = selector_iter.next_sequence(); @@ -218,12 +298,19 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im match combinator { None => SelectorMatchingResult::Matched, Some(c) => { - let (mut next_element, candidate_not_found) = if siblings { - (element.prev_sibling_element(), - SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant) - } else { - (element.parent_element(), - SelectorMatchingResult::NotMatchedGlobally) + let (mut next_element, candidate_not_found) = match c { + Combinator::NextSibling | Combinator::LaterSibling => { + (element.prev_sibling_element(), + SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant) + } + Combinator::Child | Combinator::Descendant => { + (element.parent_element(), + SelectorMatchingResult::NotMatchedGlobally) + } + Combinator::PseudoElement => { + (element.pseudo_element_originating_element(), + SelectorMatchingResult::NotMatchedGlobally) + } }; loop { @@ -233,7 +320,7 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im }; let result = matches_complex_selector_internal(selector_iter.clone(), &element, - relations, + context, flags_setter); match (result, c) { // Return the status immediately. @@ -242,6 +329,7 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im // Upgrade the failure status to // NotMatchedAndRestartFromClosestDescendant. + (_, Combinator::PseudoElement) | (_, Combinator::Child) => return SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant, // Return the status directly. @@ -276,7 +364,7 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im fn matches_simple_selector<E, F>( selector: &Component<E::Impl>, element: &E, - relations: &mut StyleRelations, + context: &mut MatchingContext, flags_setter: &mut F) -> bool where E: Element, @@ -285,7 +373,7 @@ fn matches_simple_selector<E, F>( macro_rules! relation_if { ($ex:expr, $flag:ident) => { if $ex { - *relations |= $flag; + context.relations |= $flag; true } else { false @@ -295,12 +383,24 @@ fn matches_simple_selector<E, F>( match *selector { Component::Combinator(_) => unreachable!(), + Component::PseudoElement(ref pseudo) => { + element.match_pseudo_element(pseudo, context) + } Component::LocalName(LocalName { ref name, ref lower_name }) => { - let name = if element.is_html_element_in_html_document() { lower_name } else { name }; - element.get_local_name() == name.borrow() + let is_html = element.is_html_element_in_html_document(); + element.get_local_name() == select_name(is_html, name, lower_name).borrow() + } + Component::ExplicitUniversalType | + Component::ExplicitAnyNamespace => { + true } - Component::Namespace(ref namespace) => { - element.get_namespace() == namespace.url.borrow() + Component::Namespace(_, ref url) | + Component::DefaultNamespace(ref url) => { + element.get_namespace() == url.borrow() + } + Component::ExplicitNoNamespace => { + let ns = ::parser::namespace_empty_string::<E::Impl>(); + element.get_namespace() == ns.borrow() } // TODO: case-sensitivity depends on the document type and quirks mode Component::ID(ref id) => { @@ -310,38 +410,62 @@ fn matches_simple_selector<E, F>( Component::Class(ref class) => { element.has_class(class) } - Component::AttrExists(ref attr) => { - element.match_attr_has(attr) - } - Component::AttrEqual(ref attr, ref value, case_sensitivity) => { - match case_sensitivity { - CaseSensitivity::CaseSensitive => element.match_attr_equals(attr, value), - CaseSensitivity::CaseInsensitive => element.match_attr_equals_ignore_ascii_case(attr, value), + Component::AttributeInNoNamespaceExists { ref local_name, ref local_name_lower } => { + let is_html = element.is_html_element_in_html_document(); + element.attr_matches( + &NamespaceConstraint::Specific(&::parser::namespace_empty_string::<E::Impl>()), + select_name(is_html, local_name, local_name_lower), + &AttrSelectorOperation::Exists + ) + } + Component::AttributeInNoNamespace { + ref local_name, + ref local_name_lower, + ref value, + operator, + case_sensitivity, + never_matches, + } => { + if never_matches { + return false } + let is_html = element.is_html_element_in_html_document(); + element.attr_matches( + &NamespaceConstraint::Specific(&::parser::namespace_empty_string::<E::Impl>()), + select_name(is_html, local_name, local_name_lower), + &AttrSelectorOperation::WithValue { + operator: operator, + case_sensitivity: case_sensitivity.to_unconditional(is_html), + expected_value: value, + } + ) } - Component::AttrIncludes(ref attr, ref value) => { - element.match_attr_includes(attr, value) - } - Component::AttrDashMatch(ref attr, ref value) => { - element.match_attr_dash(attr, value) - } - Component::AttrPrefixMatch(ref attr, ref value) => { - element.match_attr_prefix(attr, value) - } - Component::AttrSubstringMatch(ref attr, ref value) => { - element.match_attr_substring(attr, value) - } - Component::AttrSuffixMatch(ref attr, ref value) => { - element.match_attr_suffix(attr, value) - } - Component::AttrIncludesNeverMatch(..) | - Component::AttrPrefixNeverMatch(..) | - Component::AttrSubstringNeverMatch(..) | - Component::AttrSuffixNeverMatch(..) => { - false + Component::AttributeOther(ref attr_sel) => { + if attr_sel.never_matches { + return false + } + let is_html = element.is_html_element_in_html_document(); + element.attr_matches( + &attr_sel.namespace(), + select_name(is_html, &attr_sel.local_name, &attr_sel.local_name_lower), + &match attr_sel.operation { + ParsedAttrSelectorOperation::Exists => AttrSelectorOperation::Exists, + ParsedAttrSelectorOperation::WithValue { + operator, + case_sensitivity, + ref expected_value, + } => { + AttrSelectorOperation::WithValue { + operator: operator, + case_sensitivity: case_sensitivity.to_unconditional(is_html), + expected_value: expected_value, + } + } + } + ) } Component::NonTSPseudoClass(ref pc) => { - element.match_non_ts_pseudo_class(pc, relations, flags_setter) + element.match_non_ts_pseudo_class(pc, context, flags_setter) } Component::FirstChild => { matches_first_child(element, flags_setter) @@ -385,11 +509,19 @@ fn matches_simple_selector<E, F>( matches_generic_nth_child(element, 0, 1, true, true, flags_setter) } Component::Negation(ref negated) => { - !negated.iter().all(|ss| matches_simple_selector(ss, element, relations, flags_setter)) + !negated.iter().all(|ss| matches_simple_selector(ss, element, context, flags_setter)) } } } +fn select_name<'a, T>(is_html: bool, local_name: &'a T, local_name_lower: &'a T) -> &'a T { + if is_html { + local_name_lower + } else { + local_name + } +} + #[inline] fn matches_generic_nth_child<E, F>(element: &E, a: i32, diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index 82571d9bd5e..197ce4f2629 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -3,6 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use arcslice::ArcSlice; +use attr::{AttrSelectorWithNamespace, ParsedAttrSelectorOperation, AttrSelectorOperator}; +use attr::{ParsedCaseSensitivity, SELECTOR_WHITESPACE, NamespaceConstraint}; use cssparser::{Token, Parser as CssParser, parse_nth, ToCss, serialize_identifier, CssStringWriter}; use precomputed_hash::PrecomputedHash; use smallvec::SmallVec; @@ -13,9 +15,34 @@ use std::fmt::{self, Display, Debug, Write}; use std::iter::Rev; use std::ops::Add; use std::slice; -use tree::SELECTOR_WHITESPACE; use visitor::SelectorVisitor; +/// A trait that represents a pseudo-element. +pub trait PseudoElement : Sized + ToCss { + /// The `SelectorImpl` this pseudo-element is used for. + type Impl: SelectorImpl; + + /// Whether the pseudo-element supports a given state selector to the right + /// of it. + fn supports_pseudo_class( + &self, + _pseudo_class: &<Self::Impl as SelectorImpl>::NonTSPseudoClass) + -> bool + { + false + } +} + +fn to_ascii_lowercase(s: &str) -> Cow<str> { + if let Some(first_uppercase) = s.bytes().position(|byte| byte >= b'A' && byte <= b'Z') { + let mut string = s.to_owned(); + string[first_uppercase..].make_ascii_lowercase(); + string.into() + } else { + s.into() + } +} + macro_rules! with_all_bounds { ( [ $( $InSelector: tt )* ] @@ -29,16 +56,6 @@ macro_rules! with_all_bounds { } } - fn from_ascii_lowercase<T>(s: &str) -> T where T: $($FromStr)* { - if let Some(first_uppercase) = s.bytes().position(|byte| byte >= b'A' && byte <= b'Z') { - let mut string = s.to_owned(); - string[first_uppercase..].make_ascii_lowercase(); - T::from(string) - } else { - T::from(s) - } - } - /// This trait allows to define the parser implementation in regards /// of pseudo-classes/elements /// @@ -60,7 +77,7 @@ macro_rules! with_all_bounds { type NonTSPseudoClass: $($CommonBounds)* + Sized + ToCss + SelectorMethods<Impl = Self>; /// pseudo-elements - type PseudoElementSelector: $($CommonBounds)* + Sized + ToCss; + type PseudoElement: $($CommonBounds)* + PseudoElement<Impl = Self>; } } } @@ -97,8 +114,8 @@ pub trait Parser { Err(()) } - fn parse_pseudo_element(&self, _name: Cow<str>, _input: &mut CssParser) - -> Result<<Self::Impl as SelectorImpl>::PseudoElementSelector, ()> { + fn parse_pseudo_element(&self, _name: Cow<str>) + -> Result<<Self::Impl as SelectorImpl>::PseudoElement, ()> { Err(()) } @@ -178,8 +195,59 @@ impl<Impl: SelectorImpl> SelectorInner<Impl> { #[derive(PartialEq, Eq, Clone)] pub struct Selector<Impl: SelectorImpl> { pub inner: SelectorInner<Impl>, - pub pseudo_element: Option<Impl::PseudoElementSelector>, - pub specificity: u32, + specificity_and_flags: u32, +} + +impl<Impl: SelectorImpl> ::std::borrow::Borrow<SelectorInner<Impl>> for Selector<Impl> { + fn borrow(&self) -> &SelectorInner<Impl> { + &self.inner + } +} + +const HAS_PSEUDO_BIT: u32 = 1 << 30; + +impl<Impl: SelectorImpl> Selector<Impl> { + pub fn specificity(&self) -> u32 { + self.specificity_and_flags & !HAS_PSEUDO_BIT + } + + pub fn new_for_unit_testing(inner: SelectorInner<Impl>, specificity: u32) -> Self { + Self { + inner: inner, + specificity_and_flags: specificity, + } + } + + pub fn pseudo_element(&self) -> Option<&Impl::PseudoElement> { + if !self.has_pseudo_element() { + return None + } + + for component in self.inner.complex.iter() { + if let Component::PseudoElement(ref pseudo) = *component { + return Some(pseudo) + } + } + + debug_assert!(false, "has_pseudo_element lied!"); + None + } + + pub fn has_pseudo_element(&self) -> bool { + (self.specificity_and_flags & HAS_PSEUDO_BIT) != 0 + } + + /// Whether this selector (pseudo-element part excluded) matches every element. + /// + /// Used for "pre-computed" pseudo-elements in components/style/stylist.rs + pub fn is_universal(&self) -> bool { + self.inner.complex.iter_raw().all(|c| matches!(*c, + Component::ExplicitUniversalType | + Component::ExplicitAnyNamespace | + Component::Combinator(Combinator::PseudoElement) | + Component::PseudoElement(..) + )) + } } pub trait SelectorMethods { @@ -246,18 +314,37 @@ impl<Impl: SelectorImpl> SelectorMethods for Component<Impl> { return false; } } - }, - AttrExists(ref selector) | - AttrEqual(ref selector, _, _) | - AttrIncludes(ref selector, _) | - AttrDashMatch(ref selector, _) | - AttrPrefixMatch(ref selector, _) | - AttrSubstringMatch(ref selector, _) | - AttrSuffixMatch(ref selector, _) => { - if !visitor.visit_attribute_selector(selector) { + } + + AttributeInNoNamespaceExists { ref local_name, ref local_name_lower } => { + if !visitor.visit_attribute_selector( + &NamespaceConstraint::Specific(&namespace_empty_string::<Impl>()), + local_name, + local_name_lower, + ) { + return false; + } + } + AttributeInNoNamespace { ref local_name, ref local_name_lower, never_matches, .. } + if !never_matches => { + if !visitor.visit_attribute_selector( + &NamespaceConstraint::Specific(&namespace_empty_string::<Impl>()), + local_name, + local_name_lower, + ) { return false; } } + AttributeOther(ref attr_selector) if !attr_selector.never_matches => { + if !visitor.visit_attribute_selector( + &attr_selector.namespace(), + &attr_selector.local_name, + &attr_selector.local_name_lower, + ) { + return false; + } + } + NonTSPseudoClass(ref pseudo_class) => { if !pseudo_class.visit(visitor) { return false; @@ -270,6 +357,11 @@ impl<Impl: SelectorImpl> SelectorMethods for Component<Impl> { } } +pub fn namespace_empty_string<Impl: SelectorImpl>() -> Impl::NamespaceUrl { + // Rust type’s default, not default namespace + Impl::NamespaceUrl::default() +} + /// A ComplexSelectors stores a sequence of simple selectors and combinators. The /// iterator classes allow callers to iterate at either the raw sequence level or /// at the level of sequences of simple selectors separated by combinators. Most @@ -349,7 +441,8 @@ impl<'a, Impl: 'a + SelectorImpl> SelectorIter<'a, Impl> { impl<'a, Impl: SelectorImpl> Iterator for SelectorIter<'a, Impl> { type Item = &'a Component<Impl>; fn next(&mut self) -> Option<Self::Item> { - debug_assert!(self.next_combinator.is_none(), "Should call take_combinator!"); + debug_assert!(self.next_combinator.is_none(), + "You should call next_sequence!"); match self.iter.next() { None => None, Some(&Component::Combinator(c)) => { @@ -372,12 +465,12 @@ impl<'a, Impl: 'a + SelectorImpl> AncestorIter<'a, Impl> { result } - /// Skips a sequence of simple selectors and all subsequent sequences until an - /// ancestor combinator is reached. + /// Skips a sequence of simple selectors and all subsequent sequences until + /// a non-pseudo-element ancestor combinator is reached. fn skip_until_ancestor(&mut self) { loop { - while let Some(_) = self.0.next() {} - if self.0.next_sequence().map_or(true, |x| x.is_ancestor()) { + while self.0.next().is_some() {} + if self.0.next_sequence().map_or(true, |x| matches!(x, Combinator::Child | Combinator::Descendant)) { break; } } @@ -395,7 +488,7 @@ impl<'a, Impl: SelectorImpl> Iterator for AncestorIter<'a, Impl> { // See if there are more sequences. If so, skip any non-ancestor sequences. if let Some(combinator) = self.0.next_sequence() { - if !combinator.is_ancestor() { + if !matches!(combinator, Combinator::Child | Combinator::Descendant) { self.skip_until_ancestor(); } } @@ -410,12 +503,24 @@ pub enum Combinator { Descendant, // space NextSibling, // + LaterSibling, // ~ + /// A dummy combinator we use to the left of pseudo-elements. + /// + /// It serializes as the empty string, and acts effectively as a child + /// combinator. + PseudoElement, } impl Combinator { /// Returns true if this combinator is a child or descendant combinator. pub fn is_ancestor(&self) -> bool { - matches!(*self, Combinator::Child | Combinator::Descendant) + matches!(*self, Combinator::Child | + Combinator::Descendant | + Combinator::PseudoElement) + } + + /// Returns true if this combinator is a pseudo-element combinator. + pub fn is_pseudo_element(&self) -> bool { + matches!(*self, Combinator::PseudoElement) } /// Returns true if this combinator is a next- or later-sibling combinator. @@ -431,24 +536,32 @@ impl Combinator { #[derive(Eq, PartialEq, Clone)] pub enum Component<Impl: SelectorImpl> { Combinator(Combinator), + + ExplicitAnyNamespace, + ExplicitNoNamespace, + DefaultNamespace(Impl::NamespaceUrl), + Namespace(Impl::NamespacePrefix, Impl::NamespaceUrl), + + ExplicitUniversalType, + LocalName(LocalName<Impl>), + ID(Impl::Identifier), Class(Impl::ClassName), - LocalName(LocalName<Impl>), - Namespace(Namespace<Impl>), - - // Attribute selectors - AttrExists(AttrSelector<Impl>), // [foo] - AttrEqual(AttrSelector<Impl>, Impl::AttrValue, CaseSensitivity), // [foo=bar] - AttrIncludes(AttrSelector<Impl>, Impl::AttrValue), // [foo~=bar] - AttrDashMatch(AttrSelector<Impl>, Impl::AttrValue), // [foo|=bar] - AttrPrefixMatch(AttrSelector<Impl>, Impl::AttrValue), // [foo^=bar] - AttrSubstringMatch(AttrSelector<Impl>, Impl::AttrValue), // [foo*=bar] - AttrSuffixMatch(AttrSelector<Impl>, Impl::AttrValue), // [foo$=bar] - - AttrIncludesNeverMatch(AttrSelector<Impl>, Impl::AttrValue), // empty value or with whitespace - AttrPrefixNeverMatch(AttrSelector<Impl>, Impl::AttrValue), // empty value - AttrSubstringNeverMatch(AttrSelector<Impl>, Impl::AttrValue), // empty value - AttrSuffixNeverMatch(AttrSelector<Impl>, Impl::AttrValue), // empty value + + AttributeInNoNamespaceExists { + local_name: Impl::LocalName, + local_name_lower: Impl::LocalName, + }, + AttributeInNoNamespace { + local_name: Impl::LocalName, + local_name_lower: Impl::LocalName, + operator: AttrSelectorOperator, + value: Impl::AttrValue, + case_sensitivity: ParsedCaseSensitivity, + never_matches: bool, + }, + // Use a Box in the less common cases with more data to keep size_of::<Component>() small. + AttributeOther(Box<AttrSelectorWithNamespace<Impl>>), // Pseudo-classes // @@ -472,7 +585,7 @@ pub enum Component<Impl: SelectorImpl> { LastOfType, OnlyOfType, NonTSPseudoClass(Impl::NonTSPseudoClass), - // ... + PseudoElement(Impl::PseudoElement), } impl<Impl: SelectorImpl> Component<Impl> { @@ -489,8 +602,9 @@ impl<Impl: SelectorImpl> Component<Impl> { None } }, - Component::Namespace(ref namespace) => { - Some(namespace.url.precomputed_hash()) + Component::DefaultNamespace(ref url) | + Component::Namespace(_, ref url) => { + Some(url.precomputed_hash()) }, Component::ID(ref id) => { Some(id.precomputed_hash()) @@ -516,53 +630,17 @@ impl<Impl: SelectorImpl> Component<Impl> { } } -#[derive(Eq, PartialEq, Clone, Copy, Debug)] -pub enum CaseSensitivity { - CaseSensitive, // Selectors spec says language-defined, but HTML says sensitive. - CaseInsensitive, -} - - #[derive(Eq, PartialEq, Clone)] pub struct LocalName<Impl: SelectorImpl> { pub name: Impl::LocalName, pub lower_name: Impl::LocalName, } -#[derive(Eq, PartialEq, Clone)] -pub struct AttrSelector<Impl: SelectorImpl> { - pub name: Impl::LocalName, - pub lower_name: Impl::LocalName, - pub namespace: NamespaceConstraint<Impl>, -} - -#[derive(Eq, PartialEq, Clone, Debug)] -pub enum NamespaceConstraint<Impl: SelectorImpl> { - Any, - Specific(Namespace<Impl>), -} - -#[derive(Eq, PartialEq, Clone)] -pub struct Namespace<Impl: SelectorImpl> { - pub prefix: Option<Impl::NamespacePrefix>, - pub url: Impl::NamespaceUrl, -} - -impl<Impl: SelectorImpl> Default for Namespace<Impl> { - fn default() -> Self { - Namespace { - prefix: None, - url: Impl::NamespaceUrl::default(), // empty string - } - } -} - - impl<Impl: SelectorImpl> Debug for Selector<Impl> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("Selector(")?; self.to_css(f)?; - write!(f, ", specificity = 0x{:x})", self.specificity) + write!(f, ", specificity = 0x{:x})", self.specificity()) } } @@ -575,10 +653,7 @@ impl<Impl: SelectorImpl> Debug for ComplexSelector<Impl> { impl<Impl: SelectorImpl> Debug for Component<Impl> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_css(f) } } -impl<Impl: SelectorImpl> Debug for AttrSelector<Impl> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_css(f) } -} -impl<Impl: SelectorImpl> Debug for Namespace<Impl> { +impl<Impl: SelectorImpl> Debug for AttrSelectorWithNamespace<Impl> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_css(f) } } impl<Impl: SelectorImpl> Debug for LocalName<Impl> { @@ -601,11 +676,7 @@ impl<Impl: SelectorImpl> ToCss for SelectorList<Impl> { impl<Impl: SelectorImpl> ToCss for Selector<Impl> { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - self.inner.complex.to_css(dest)?; - if let Some(ref pseudo) = self.pseudo_element { - pseudo.to_css(dest)?; - } - Ok(()) + self.inner.complex.to_css(dest) } } @@ -626,6 +697,7 @@ impl ToCss for Combinator { Combinator::Descendant => dest.write_str(" "), Combinator::NextSibling => dest.write_str(" + "), Combinator::LaterSibling => dest.write_str(" ~ "), + Combinator::PseudoElement => Ok(()), } } } @@ -637,6 +709,9 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> { Combinator(ref c) => { c.to_css(dest) } + PseudoElement(ref p) => { + p.to_css(dest) + } ID(ref s) => { dest.write_char('#')?; display_to_css_identifier(s, dest) @@ -646,34 +721,41 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> { display_to_css_identifier(s, dest) } LocalName(ref s) => s.to_css(dest), - Namespace(ref ns) => ns.to_css(dest), + ExplicitUniversalType => dest.write_char('*'), + + DefaultNamespace(_) => Ok(()), + ExplicitNoNamespace => dest.write_char('|'), + ExplicitAnyNamespace => dest.write_str("*|"), + Namespace(ref prefix, _) => { + display_to_css_identifier(prefix, dest)?; + dest.write_char('|') + } - // Attribute selectors - AttrExists(ref a) => { + AttributeInNoNamespaceExists { ref local_name, .. } => { dest.write_char('[')?; - a.to_css(dest)?; + display_to_css_identifier(local_name, dest)?; dest.write_char(']') } - AttrEqual(ref a, ref v, case) => { - attr_selector_to_css(a, " = ", v, match case { - CaseSensitivity::CaseSensitive => None, - CaseSensitivity::CaseInsensitive => Some(" i"), - }, dest) + AttributeInNoNamespace { ref local_name, operator, ref value, case_sensitivity, .. } => { + dest.write_char('[')?; + display_to_css_identifier(local_name, dest)?; + operator.to_css(dest)?; + dest.write_char('"')?; + write!(CssStringWriter::new(dest), "{}", value)?; + dest.write_char('"')?; + match case_sensitivity { + ParsedCaseSensitivity::CaseSensitive | + ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {}, + ParsedCaseSensitivity::AsciiCaseInsensitive => dest.write_str(" i")?, + } + dest.write_char(']') } - AttrDashMatch(ref a, ref v) => attr_selector_to_css(a, " |= ", v, None, dest), - AttrIncludesNeverMatch(ref a, ref v) | - AttrIncludes(ref a, ref v) => attr_selector_to_css(a, " ~= ", v, None, dest), - AttrPrefixNeverMatch(ref a, ref v) | - AttrPrefixMatch(ref a, ref v) => attr_selector_to_css(a, " ^= ", v, None, dest), - AttrSubstringNeverMatch(ref a, ref v) | - AttrSubstringMatch(ref a, ref v) => attr_selector_to_css(a, " *= ", v, None, dest), - AttrSuffixNeverMatch(ref a, ref v) | - AttrSuffixMatch(ref a, ref v) => attr_selector_to_css(a, " $= ", v, None, dest), + AttributeOther(ref attr_selector) => attr_selector.to_css(dest), // Pseudo-classes Negation(ref arg) => { dest.write_str(":not(")?; - debug_assert!(arg.len() <= 1 || (arg.len() == 2 && matches!(arg[0], Component::Namespace(_)))); + debug_assert!(single_simple_selector(arg)); for component in arg.iter() { component.to_css(dest)?; } @@ -697,22 +779,36 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> { } } -impl<Impl: SelectorImpl> ToCss for AttrSelector<Impl> { +impl<Impl: SelectorImpl> ToCss for AttrSelectorWithNamespace<Impl> { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - if let NamespaceConstraint::Specific(ref ns) = self.namespace { - ns.to_css(dest)?; + dest.write_char('[')?; + match self.namespace { + NamespaceConstraint::Specific((ref prefix, _)) => { + display_to_css_identifier(prefix, dest)?; + dest.write_char('|')? + } + NamespaceConstraint::Any => { + dest.write_str("*|")? + } } - display_to_css_identifier(&self.name, dest) - } -} - -impl<Impl: SelectorImpl> ToCss for Namespace<Impl> { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - if let Some(ref prefix) = self.prefix { - display_to_css_identifier(prefix, dest)?; - dest.write_char('|')?; + display_to_css_identifier(&self.local_name, dest)?; + match self.operation { + ParsedAttrSelectorOperation::Exists => {}, + ParsedAttrSelectorOperation::WithValue { + operator, case_sensitivity, ref expected_value + } => { + operator.to_css(dest)?; + dest.write_char('"')?; + write!(CssStringWriter::new(dest), "{}", expected_value)?; + dest.write_char('"')?; + match case_sensitivity { + ParsedCaseSensitivity::CaseSensitive | + ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {}, + ParsedCaseSensitivity::AsciiCaseInsensitive => dest.write_str(" i")?, + } + }, } - Ok(()) + dest.write_char(']') } } @@ -722,26 +818,6 @@ impl<Impl: SelectorImpl> ToCss for LocalName<Impl> { } } -fn attr_selector_to_css<Impl, W>(attr: &AttrSelector<Impl>, - operator: &str, - value: &Impl::AttrValue, - modifier: Option<&str>, - dest: &mut W) - -> fmt::Result -where Impl: SelectorImpl, W: fmt::Write -{ - dest.write_char('[')?; - attr.to_css(dest)?; - dest.write_str(operator)?; - dest.write_char('"')?; - write!(CssStringWriter::new(dest), "{}", value)?; - dest.write_char('"')?; - if let Some(m) = modifier { - dest.write_str(m)?; - } - dest.write_char(']') -} - /// Serialize the output of Display as a CSS identifier fn display_to_css_identifier<T: Display, W: fmt::Write>(x: &T, dest: &mut W) -> fmt::Result { // FIXME(SimonSapin): it is possible to avoid this heap allocation @@ -813,42 +889,33 @@ impl From<Specificity> for u32 { } } -fn specificity<Impl>(complex_selector: &ComplexSelector<Impl>, - pseudo_element: Option<&Impl::PseudoElementSelector>) - -> u32 - where Impl: SelectorImpl { - let mut specificity = complex_selector_specificity(complex_selector); - if pseudo_element.is_some() { - specificity.element_selectors += 1; - } - specificity.into() +fn specificity<Impl>(complex_selector: &ComplexSelector<Impl>) -> u32 + where Impl: SelectorImpl +{ + complex_selector_specificity(complex_selector).into() } fn complex_selector_specificity<Impl>(selector: &ComplexSelector<Impl>) -> Specificity - where Impl: SelectorImpl { + where Impl: SelectorImpl +{ fn simple_selector_specificity<Impl>(simple_selector: &Component<Impl>, specificity: &mut Specificity) - where Impl: SelectorImpl { + where Impl: SelectorImpl + { match *simple_selector { Component::Combinator(..) => unreachable!(), - Component::LocalName(..) => - specificity.element_selectors += 1, - Component::ID(..) => - specificity.id_selectors += 1, + Component::PseudoElement(..) | + Component::LocalName(..) => { + specificity.element_selectors += 1 + } + Component::ID(..) => { + specificity.id_selectors += 1 + } Component::Class(..) | - Component::AttrExists(..) | - Component::AttrEqual(..) | - Component::AttrIncludes(..) | - Component::AttrDashMatch(..) | - Component::AttrPrefixMatch(..) | - Component::AttrSubstringMatch(..) | - Component::AttrSuffixMatch(..) | - - Component::AttrIncludesNeverMatch(..) | - Component::AttrPrefixNeverMatch(..) | - Component::AttrSubstringNeverMatch(..) | - Component::AttrSuffixNeverMatch(..) | + Component::AttributeInNoNamespace { .. } | + Component::AttributeInNoNamespaceExists { .. } | + Component::AttributeOther(..) | Component::FirstChild | Component::LastChild | Component::OnlyChild | Component::Root | @@ -859,10 +926,16 @@ fn complex_selector_specificity<Impl>(selector: &ComplexSelector<Impl>) Component::NthLastOfType(..) | Component::FirstOfType | Component::LastOfType | Component::OnlyOfType | - Component::NonTSPseudoClass(..) => - specificity.class_like_selectors += 1, - - Component::Namespace(..) => (), + Component::NonTSPseudoClass(..) => { + specificity.class_like_selectors += 1 + } + Component::ExplicitUniversalType | + Component::ExplicitAnyNamespace | + Component::ExplicitNoNamespace | + Component::DefaultNamespace(..) | + Component::Namespace(..) => { + // Does not affect specificity + } Component::Negation(ref negated) => { for ss in negated.iter() { simple_selector_specificity(&ss, specificity); @@ -892,12 +965,14 @@ fn complex_selector_specificity<Impl>(selector: &ComplexSelector<Impl>) fn parse_selector<P, Impl>(parser: &P, input: &mut CssParser) -> Result<Selector<Impl>, ()> where P: Parser<Impl=Impl>, Impl: SelectorImpl { - let (complex, pseudo_element) = - parse_complex_selector_and_pseudo_element(parser, input)?; + let (complex, has_pseudo_element) = parse_complex_selector(parser, input)?; + let mut specificity = specificity(&complex); + if has_pseudo_element { + specificity |= HAS_PSEUDO_BIT; + } Ok(Selector { - specificity: specificity(&complex, pseudo_element.as_ref()), + specificity_and_flags: specificity, inner: SelectorInner::new(complex), - pseudo_element: pseudo_element, }) } @@ -911,19 +986,25 @@ fn parse_selector<P, Impl>(parser: &P, input: &mut CssParser) -> Result<Selector /// If we parse N > 8 entries, we save two reallocations. type ParseVec<Impl> = SmallVec<[Component<Impl>; 8]>; -fn parse_complex_selector_and_pseudo_element<P, Impl>( +/// Parses a complex selector, including any pseudo-element. +/// +/// For now, it always forces the pseudo-element to be at the end of the +/// selector, and the boolean represents whether the last thing parsed was a +/// pseudo-element. +fn parse_complex_selector<P, Impl>( parser: &P, input: &mut CssParser) - -> Result<(ComplexSelector<Impl>, Option<Impl::PseudoElementSelector>), ()> + -> Result<(ComplexSelector<Impl>, bool), ()> where P: Parser<Impl=Impl>, Impl: SelectorImpl { let mut sequence = ParseVec::new(); - let mut pseudo_element; + let mut parsed_pseudo_element; 'outer_loop: loop { // Parse a sequence of simple selectors. - pseudo_element = parse_compound_selector(parser, input, &mut sequence, - /* inside_negation = */ false)?; - if pseudo_element.is_some() { + parsed_pseudo_element = + parse_compound_selector(parser, input, &mut sequence, + /* inside_negation = */ false)?; + if parsed_pseudo_element { break; } @@ -962,17 +1043,17 @@ fn parse_complex_selector_and_pseudo_element<P, Impl>( } let complex = ComplexSelector(ArcSlice::new(sequence.into_vec().into_boxed_slice())); - Ok((complex, pseudo_element)) + Ok((complex, parsed_pseudo_element)) } impl<Impl: SelectorImpl> ComplexSelector<Impl> { - /// Parse a complex selector. + /// Parse a complex selector, without any pseudo-element. pub fn parse<P>(parser: &P, input: &mut CssParser) -> Result<Self, ()> where P: Parser<Impl=Impl> { - let (complex, pseudo_element) = - parse_complex_selector_and_pseudo_element(parser, input)?; - if pseudo_element.is_some() { + let (complex, has_pseudo_element) = + parse_complex_selector(parser, input)?; + if has_pseudo_element { return Err(()) } Ok(complex) @@ -990,19 +1071,33 @@ fn parse_type_selector<P, Impl>(parser: &P, input: &mut CssParser, sequence: &mu None => Ok(false), Some((namespace, local_name)) => { match namespace { - NamespaceConstraint::Specific(ns) => { - sequence.push(Component::Namespace(ns)) - }, - NamespaceConstraint::Any => (), + QNamePrefix::ImplicitAnyNamespace => {} + QNamePrefix::ImplicitDefaultNamespace(url) => { + sequence.push(Component::DefaultNamespace(url)) + } + QNamePrefix::ExplicitNamespace(prefix, url) => { + sequence.push(Component::Namespace(prefix, url)) + } + QNamePrefix::ExplicitNoNamespace => { + sequence.push(Component::ExplicitNoNamespace) + } + QNamePrefix::ExplicitAnyNamespace => { + sequence.push(Component::ExplicitAnyNamespace) + } + QNamePrefix::ImplicitNoNamespace => { + unreachable!() // Not returned with in_attr_selector = false + } } match local_name { Some(name) => { sequence.push(Component::LocalName(LocalName { - lower_name: from_ascii_lowercase(&name), + lower_name: from_cow_str(to_ascii_lowercase(&name)), name: from_cow_str(name), })) } - None => (), + None => { + sequence.push(Component::ExplicitUniversalType) + } } Ok(true) } @@ -1012,7 +1107,17 @@ fn parse_type_selector<P, Impl>(parser: &P, input: &mut CssParser, sequence: &mu #[derive(Debug)] enum SimpleSelectorParseResult<Impl: SelectorImpl> { SimpleSelector(Component<Impl>), - PseudoElement(Impl::PseudoElementSelector), + PseudoElement(Impl::PseudoElement), +} + +#[derive(Debug)] +enum QNamePrefix<Impl: SelectorImpl> { + ImplicitNoNamespace, // `foo` in attr selectors + ImplicitAnyNamespace, // `foo` in type selectors, without a default ns + ImplicitDefaultNamespace(Impl::NamespaceUrl), // `foo` in type selectors, with a default ns + ExplicitNoNamespace, // `|foo` + ExplicitAnyNamespace, // `*|foo` + ExplicitNamespace(Impl::NamespacePrefix, Impl::NamespaceUrl), // `prefix|foo` } /// * `Err(())`: Invalid selector, abort @@ -1021,16 +1126,13 @@ enum SimpleSelectorParseResult<Impl: SelectorImpl> { fn parse_qualified_name<'i, 't, P, Impl> (parser: &P, input: &mut CssParser<'i, 't>, in_attr_selector: bool) - -> Result<Option<(NamespaceConstraint<Impl>, Option<Cow<'i, str>>)>, ()> + -> Result<Option<(QNamePrefix<Impl>, Option<Cow<'i, str>>)>, ()> where P: Parser<Impl=Impl>, Impl: SelectorImpl { let default_namespace = |local_name| { let namespace = match parser.default_namespace() { - Some(url) => NamespaceConstraint::Specific(Namespace { - prefix: None, - url: url - }), - None => NamespaceConstraint::Any, + Some(url) => QNamePrefix::ImplicitDefaultNamespace(url), + None => QNamePrefix::ImplicitAnyNamespace, }; Ok(Some((namespace, local_name))) }; @@ -1056,15 +1158,12 @@ fn parse_qualified_name<'i, 't, P, Impl> let prefix = from_cow_str(value); let result = parser.namespace_for_prefix(&prefix); let url = result.ok_or(())?; - explicit_namespace(input, NamespaceConstraint::Specific(Namespace { - prefix: Some(prefix), - url: url - })) + explicit_namespace(input, QNamePrefix::ExplicitNamespace(prefix, url)) }, _ => { input.reset(position); if in_attr_selector { - Ok(Some((NamespaceConstraint::Specific(Default::default()), Some(value)))) + Ok(Some((QNamePrefix::ImplicitNoNamespace, Some(value)))) } else { default_namespace(Some(value)) } @@ -1074,7 +1173,9 @@ fn parse_qualified_name<'i, 't, P, Impl> Ok(Token::Delim('*')) => { let position = input.position(); match input.next_including_whitespace() { - Ok(Token::Delim('|')) => explicit_namespace(input, NamespaceConstraint::Any), + Ok(Token::Delim('|')) => { + explicit_namespace(input, QNamePrefix::ExplicitAnyNamespace) + } _ => { input.reset(position); if in_attr_selector { @@ -1086,7 +1187,7 @@ fn parse_qualified_name<'i, 't, P, Impl> } }, Ok(Token::Delim('|')) => { - explicit_namespace(input, NamespaceConstraint::Specific(Default::default())) + explicit_namespace(input, QNamePrefix::ExplicitNoNamespace) } _ => { input.reset(position); @@ -1100,77 +1201,143 @@ fn parse_attribute_selector<P, Impl>(parser: &P, input: &mut CssParser) -> Result<Component<Impl>, ()> where P: Parser<Impl=Impl>, Impl: SelectorImpl { - let attr = match parse_qualified_name(parser, input, /* in_attr_selector = */ true)? { + let namespace; + let local_name; + match parse_qualified_name(parser, input, /* in_attr_selector = */ true)? { None => return Err(()), Some((_, None)) => unreachable!(), - Some((namespace, Some(local_name))) => AttrSelector { - namespace: namespace, - lower_name: from_ascii_lowercase(&local_name), - name: from_cow_str(local_name), - }, - }; + Some((ns, Some(ln))) => { + local_name = ln; + namespace = match ns { + QNamePrefix::ImplicitNoNamespace | + QNamePrefix::ExplicitNoNamespace => { + None + } + QNamePrefix::ExplicitNamespace(prefix, url) => { + Some(NamespaceConstraint::Specific((prefix, url))) + } + QNamePrefix::ExplicitAnyNamespace => { + Some(NamespaceConstraint::Any) + } + QNamePrefix::ImplicitAnyNamespace | + QNamePrefix::ImplicitDefaultNamespace(_) => { + unreachable!() // Not returned with in_attr_selector = true + } + } + } + } + let operator; + let value; + let never_matches; match input.next() { // [foo] - Err(()) => Ok(Component::AttrExists(attr)), + Err(()) => { + let local_name_lower = from_cow_str(to_ascii_lowercase(&local_name)); + let local_name = from_cow_str(local_name); + if let Some(namespace) = namespace { + return Ok(Component::AttributeOther(Box::new(AttrSelectorWithNamespace { + namespace: namespace, + local_name: local_name, + local_name_lower: local_name_lower, + operation: ParsedAttrSelectorOperation::Exists, + never_matches: false, + }))) + } else { + return Ok(Component::AttributeInNoNamespaceExists { + local_name: local_name, + local_name_lower: local_name_lower, + }) + } + } // [foo=bar] Ok(Token::Delim('=')) => { - let value = input.expect_ident_or_string()?; - let flags = parse_attribute_flags(input)?; - Ok(Component::AttrEqual(attr, from_cow_str(value), flags)) + value = input.expect_ident_or_string()?; + never_matches = false; + operator = AttrSelectorOperator::Equal; } // [foo~=bar] Ok(Token::IncludeMatch) => { - let value = input.expect_ident_or_string()?; - if value.is_empty() || value.contains(SELECTOR_WHITESPACE) { - Ok(Component::AttrIncludesNeverMatch(attr, from_cow_str(value))) - } else { - Ok(Component::AttrIncludes(attr, from_cow_str(value))) - } + value = input.expect_ident_or_string()?; + never_matches = value.is_empty() || value.contains(SELECTOR_WHITESPACE); + operator = AttrSelectorOperator::Includes; } // [foo|=bar] Ok(Token::DashMatch) => { - let value = input.expect_ident_or_string()?; - Ok(Component::AttrDashMatch(attr, from_cow_str(value))) + value = input.expect_ident_or_string()?; + never_matches = false; + operator = AttrSelectorOperator::DashMatch; } // [foo^=bar] Ok(Token::PrefixMatch) => { - let value = input.expect_ident_or_string()?; - if value.is_empty() { - Ok(Component::AttrPrefixNeverMatch(attr, from_cow_str(value))) - } else { - Ok(Component::AttrPrefixMatch(attr, from_cow_str(value))) - } + value = input.expect_ident_or_string()?; + never_matches = value.is_empty(); + operator = AttrSelectorOperator::Prefix; } // [foo*=bar] Ok(Token::SubstringMatch) => { - let value = input.expect_ident_or_string()?; - if value.is_empty() { - Ok(Component::AttrSubstringNeverMatch(attr, from_cow_str(value))) - } else { - Ok(Component::AttrSubstringMatch(attr, from_cow_str(value))) - } + value = input.expect_ident_or_string()?; + never_matches = value.is_empty(); + operator = AttrSelectorOperator::Substring; } // [foo$=bar] Ok(Token::SuffixMatch) => { - let value = input.expect_ident_or_string()?; - if value.is_empty() { - Ok(Component::AttrSuffixNeverMatch(attr, from_cow_str(value))) - } else { - Ok(Component::AttrSuffixMatch(attr, from_cow_str(value))) + value = input.expect_ident_or_string()?; + never_matches = value.is_empty(); + operator = AttrSelectorOperator::Suffix; + } + _ => return Err(()) + } + + let mut case_sensitivity = parse_attribute_flags(input)?; + + let value = from_cow_str(value); + let local_name_lower; + { + let local_name_lower_cow = to_ascii_lowercase(&local_name); + if let ParsedCaseSensitivity::CaseSensitive = case_sensitivity { + if namespace.is_none() && + include!(concat!(env!("OUT_DIR"), "/ascii_case_insensitive_html_attributes.rs")) + .contains(&*local_name_lower_cow) + { + case_sensitivity = + ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument } } - _ => Err(()) + local_name_lower = from_cow_str(local_name_lower_cow); + } + let local_name = from_cow_str(local_name); + if let Some(namespace) = namespace { + Ok(Component::AttributeOther(Box::new(AttrSelectorWithNamespace { + namespace: namespace, + local_name: local_name, + local_name_lower: local_name_lower, + never_matches: never_matches, + operation: ParsedAttrSelectorOperation::WithValue { + operator: operator, + case_sensitivity: case_sensitivity, + expected_value: value, + } + }))) + } else { + Ok(Component::AttributeInNoNamespace { + local_name: local_name, + local_name_lower: local_name_lower, + operator: operator, + value: value, + case_sensitivity: case_sensitivity, + never_matches: never_matches, + }) } } -fn parse_attribute_flags(input: &mut CssParser) -> Result<CaseSensitivity, ()> { +fn parse_attribute_flags(input: &mut CssParser) -> Result<ParsedCaseSensitivity, ()> { match input.next() { - Err(()) => Ok(CaseSensitivity::CaseSensitive), + Err(()) => Ok(ParsedCaseSensitivity::CaseSensitive), Ok(Token::Ident(ref value)) if value.eq_ignore_ascii_case("i") => { - Ok(CaseSensitivity::CaseInsensitive) + Ok(ParsedCaseSensitivity::AsciiCaseInsensitive) } _ => Err(()) } @@ -1187,28 +1354,46 @@ fn parse_negation<P, Impl>(parser: &P, let mut v = ParseVec::new(); parse_compound_selector(parser, input, &mut v, /* inside_negation = */ true)?; - let allow = v.len() <= 1 || - (v.len() == 2 && matches!(v[0], Component::Namespace(_)) && - matches!(v[1], Component::LocalName(_))); - - if allow { + if single_simple_selector(&v) { Ok(Component::Negation(v.into_vec().into_boxed_slice())) } else { Err(()) } } +// A single type selector can be represented as two components +fn single_simple_selector<Impl: SelectorImpl>(v: &[Component<Impl>]) -> bool { + v.len() == 1 || ( + v.len() == 2 && + match v[1] { + Component::LocalName(_) | Component::ExplicitUniversalType => { + debug_assert!(matches!(v[0], + Component::ExplicitAnyNamespace | + Component::ExplicitNoNamespace | + Component::DefaultNamespace(_) | + Component::Namespace(..) + )); + true + } + _ => false, + } + ) + +} + /// simple_selector_sequence /// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]* /// | [ HASH | class | attrib | pseudo | negation ]+ /// -/// `Err(())` means invalid selector +/// `Err(())` means invalid selector. +/// +/// The boolean represent whether a pseudo-element has been parsed. fn parse_compound_selector<P, Impl>( parser: &P, input: &mut CssParser, mut sequence: &mut ParseVec<Impl>, inside_negation: bool) - -> Result<Option<Impl::PseudoElementSelector>, ()> + -> Result<bool, ()> where P: Parser<Impl=Impl>, Impl: SelectorImpl { // Consume any leading whitespace. @@ -1228,17 +1413,14 @@ fn parse_compound_selector<P, Impl>( // // Note that this doesn't apply to :not() and :matches() per spec. if !inside_negation { - sequence.push(Component::Namespace(Namespace { - prefix: None, - url: url - })); + sequence.push(Component::DefaultNamespace(url)) } } } else { empty = false; } - let mut pseudo_element = None; + let mut pseudo = false; loop { match parse_one_simple_selector(parser, input, inside_negation)? { None => break, @@ -1247,7 +1429,41 @@ fn parse_compound_selector<P, Impl>( empty = false } Some(SimpleSelectorParseResult::PseudoElement(p)) => { - pseudo_element = Some(p); + // Try to parse state to its right. + let mut state_selectors = ParseVec::new(); + + loop { + match input.next_including_whitespace() { + Ok(Token::Colon) => {}, + Ok(Token::WhiteSpace(_)) | Err(()) => break, + _ => return Err(()), + } + + // TODO(emilio): Functional pseudo-classes too? + // We don't need it for now. + let name = match input.next_including_whitespace() { + Ok(Token::Ident(name)) => name, + _ => return Err(()), + }; + + let pseudo_class = + P::parse_non_ts_pseudo_class(parser, name)?; + if !p.supports_pseudo_class(&pseudo_class) { + return Err(()); + } + state_selectors.push(Component::NonTSPseudoClass(pseudo_class)); + } + + if !sequence.is_empty() { + sequence.push(Component::Combinator(Combinator::PseudoElement)); + } + + sequence.push(Component::PseudoElement(p)); + for state_selector in state_selectors { + sequence.push(state_selector); + } + + pseudo = true; empty = false; break } @@ -1257,7 +1473,7 @@ fn parse_compound_selector<P, Impl>( // An empty selector is invalid. Err(()) } else { - Ok(pseudo_element) + Ok(pseudo) } } @@ -1333,7 +1549,7 @@ fn parse_one_simple_selector<P, Impl>(parser: &P, name.eq_ignore_ascii_case("after") || name.eq_ignore_ascii_case("first-line") || name.eq_ignore_ascii_case("first-letter") { - let pseudo_element = P::parse_pseudo_element(parser, name, input)?; + let pseudo_element = P::parse_pseudo_element(parser, name)?; Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo_element))) } else { let pseudo_class = parse_simple_pseudo_class(parser, name)?; @@ -1349,7 +1565,7 @@ fn parse_one_simple_selector<P, Impl>(parser: &P, Ok(Token::Colon) => { match input.next_including_whitespace() { Ok(Token::Ident(name)) => { - let pseudo = P::parse_pseudo_element(parser, name, input)?; + let pseudo = P::parse_pseudo_element(parser, name)?; Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo))) } _ => Err(()) @@ -1388,6 +1604,7 @@ fn parse_simple_pseudo_class<P, Impl>(parser: &P, name: Cow<str>) -> Result<Comp #[cfg(test)] pub mod tests { use cssparser::{Parser as CssParser, ToCss, serialize_identifier}; + use parser; use std::borrow::Cow; use std::collections::HashMap; use std::fmt; @@ -1396,6 +1613,7 @@ pub mod tests { #[derive(PartialEq, Clone, Debug, Eq)] pub enum PseudoClass { Hover, + Active, Lang(String), } @@ -1405,10 +1623,23 @@ pub mod tests { After, } + impl parser::PseudoElement for PseudoElement { + type Impl = DummySelectorImpl; + + fn supports_pseudo_class(&self, pc: &PseudoClass) -> bool { + match *pc { + PseudoClass::Hover => true, + PseudoClass::Active | + PseudoClass::Lang(..) => false, + } + } + } + impl ToCss for PseudoClass { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match *self { PseudoClass::Hover => dest.write_str(":hover"), + PseudoClass::Active => dest.write_str(":active"), PseudoClass::Lang(ref lang) => { dest.write_str(":lang(")?; serialize_identifier(lang, dest)?; @@ -1453,7 +1684,7 @@ pub mod tests { type BorrowedLocalName = DummyAtom; type BorrowedNamespaceUrl = DummyAtom; type NonTSPseudoClass = PseudoClass; - type PseudoElementSelector = PseudoElement; + type PseudoElement = PseudoElement; } #[derive(Default, Debug, Clone, PartialEq, Eq, Hash)] @@ -1490,6 +1721,7 @@ pub mod tests { -> Result<PseudoClass, ()> { match_ignore_ascii_case! { &name, "hover" => Ok(PseudoClass::Hover), + "active" => Ok(PseudoClass::Active), _ => Err(()) } } @@ -1503,8 +1735,7 @@ pub mod tests { } } - fn parse_pseudo_element(&self, name: Cow<str>, _input: &mut CssParser) - -> Result<PseudoElement, ()> { + fn parse_pseudo_element(&self, name: Cow<str>) -> Result<PseudoElement, ()> { match_ignore_ascii_case! { &name, "before" => Ok(PseudoElement::Before), "after" => Ok(PseudoElement::After), @@ -1554,25 +1785,64 @@ pub mod tests { assert_eq!(parse(":lang(4)"), Err(())) ; assert_eq!(parse(":lang(en US)"), Err(())) ; assert_eq!(parse("EeÉ"), Ok(SelectorList(vec!(Selector { - inner: SelectorInner::from_vec(vec!(Component::LocalName(LocalName { + inner: SelectorInner::from_vec(vec!( + Component::LocalName(LocalName { name: DummyAtom::from("EeÉ"), lower_name: DummyAtom::from("eeÉ") })), ), - pseudo_element: None, - specificity: specificity(0, 0, 1), + specificity_and_flags: specificity(0, 0, 1), + })))); + assert_eq!(parse("|e"), Ok(SelectorList(vec!(Selector { + inner: SelectorInner::from_vec(vec!( + Component::ExplicitNoNamespace, + Component::LocalName(LocalName { + name: DummyAtom::from("e"), + lower_name: DummyAtom::from("e") + }), + )), + specificity_and_flags: specificity(0, 0, 1), + })))); + // https://github.com/servo/servo/issues/16020 + assert_eq!(parse("*|e"), Ok(SelectorList(vec!(Selector { + inner: SelectorInner::from_vec(vec!( + Component::ExplicitAnyNamespace, + Component::LocalName(LocalName { + name: DummyAtom::from("e"), + lower_name: DummyAtom::from("e") + }), + )), + specificity_and_flags: specificity(0, 0, 1), + })))); + assert_eq!(parse("*"), Ok(SelectorList(vec!(Selector { + inner: SelectorInner::from_vec(vec!( + Component::ExplicitUniversalType, + )), + specificity_and_flags: specificity(0, 0, 0), + })))); + assert_eq!(parse("|*"), Ok(SelectorList(vec!(Selector { + inner: SelectorInner::from_vec(vec!( + Component::ExplicitNoNamespace, + Component::ExplicitUniversalType, + )), + specificity_and_flags: specificity(0, 0, 0), + })))); + assert_eq!(parse("*|*"), Ok(SelectorList(vec!(Selector { + inner: SelectorInner::from_vec(vec!( + Component::ExplicitAnyNamespace, + Component::ExplicitUniversalType, + )), + specificity_and_flags: specificity(0, 0, 0), })))); assert_eq!(parse(".foo:lang(en-US)"), Ok(SelectorList(vec!(Selector { inner: SelectorInner::from_vec(vec![ Component::Class(DummyAtom::from("foo")), Component::NonTSPseudoClass(PseudoClass::Lang("en-US".to_owned())) ]), - pseudo_element: None, - specificity: specificity(0, 2, 0), + specificity_and_flags: specificity(0, 2, 0), })))); assert_eq!(parse("#bar"), Ok(SelectorList(vec!(Selector { inner: SelectorInner::from_vec(vec!(Component::ID(DummyAtom::from("bar")))), - pseudo_element: None, - specificity: specificity(1, 0, 0), + specificity_and_flags: specificity(1, 0, 0), })))); assert_eq!(parse("e.foo#bar"), Ok(SelectorList(vec!(Selector { inner: SelectorInner::from_vec(vec!(Component::LocalName(LocalName { @@ -1580,8 +1850,7 @@ pub mod tests { lower_name: DummyAtom::from("e") }), Component::Class(DummyAtom::from("foo")), Component::ID(DummyAtom::from("bar")))), - pseudo_element: None, - specificity: specificity(1, 1, 1), + specificity_and_flags: specificity(1, 1, 1), })))); assert_eq!(parse("e.foo #bar"), Ok(SelectorList(vec!(Selector { inner: SelectorInner::from_vec(vec!( @@ -1593,40 +1862,40 @@ pub mod tests { Component::Combinator(Combinator::Descendant), Component::ID(DummyAtom::from("bar")), )), - pseudo_element: None, - specificity: specificity(1, 1, 1), + specificity_and_flags: specificity(1, 1, 1), })))); // Default namespace does not apply to attribute selectors // https://github.com/mozilla/servo/pull/1652 let mut parser = DummyParser::default(); assert_eq!(parse_ns("[Foo]", &parser), Ok(SelectorList(vec!(Selector { - inner: SelectorInner::from_vec(vec!( - Component::AttrExists(AttrSelector { - name: DummyAtom::from("Foo"), - lower_name: DummyAtom::from("foo"), - namespace: NamespaceConstraint::Specific(Namespace { - prefix: None, - url: "".into(), - }) }))), - pseudo_element: None, - specificity: specificity(0, 1, 0), + inner: SelectorInner::from_vec(vec![ + Component::AttributeInNoNamespaceExists { + local_name: DummyAtom::from("Foo"), + local_name_lower: DummyAtom::from("foo"), + } + ]), + specificity_and_flags: specificity(0, 1, 0), })))); assert_eq!(parse_ns("svg|circle", &parser), Err(())); parser.ns_prefixes.insert(DummyAtom("svg".into()), DummyAtom(SVG.into())); assert_eq!(parse_ns("svg|circle", &parser), Ok(SelectorList(vec![Selector { inner: SelectorInner::from_vec( vec![ - Component::Namespace(Namespace { - prefix: Some(DummyAtom("svg".into())), - url: SVG.into(), - }), + Component::Namespace(DummyAtom("svg".into()), SVG.into()), Component::LocalName(LocalName { name: DummyAtom::from("circle"), lower_name: DummyAtom::from("circle"), }) ]), - pseudo_element: None, - specificity: specificity(0, 0, 1), + specificity_and_flags: specificity(0, 0, 1), + }]))); + assert_eq!(parse_ns("svg|*", &parser), Ok(SelectorList(vec![Selector { + inner: SelectorInner::from_vec( + vec![ + Component::Namespace(DummyAtom("svg".into()), SVG.into()), + Component::ExplicitUniversalType, + ]), + specificity_and_flags: specificity(0, 0, 0), }]))); // Default namespace does not apply to attribute selectors // https://github.com/mozilla/servo/pull/1652 @@ -1636,58 +1905,123 @@ pub mod tests { assert_eq!(parse_ns("[Foo]", &parser), Ok(SelectorList(vec!(Selector { inner: SelectorInner::from_vec( vec![ - Component::Namespace(Namespace { - prefix: None, - url: MATHML.into(), - }), - Component::AttrExists(AttrSelector { - name: DummyAtom::from("Foo"), - lower_name: DummyAtom::from("foo"), - namespace: NamespaceConstraint::Specific(Namespace { - prefix: None, - url: "".into(), - }), - }), + Component::DefaultNamespace(MATHML.into()), + Component::AttributeInNoNamespaceExists { + local_name: DummyAtom::from("Foo"), + local_name_lower: DummyAtom::from("foo"), + }, ]), - pseudo_element: None, - specificity: specificity(0, 1, 0), + specificity_and_flags: specificity(0, 1, 0), })))); // Default namespace does apply to type selectors assert_eq!(parse_ns("e", &parser), Ok(SelectorList(vec!(Selector { inner: SelectorInner::from_vec( vec!( - Component::Namespace(Namespace { - prefix: None, - url: MATHML.into(), - }), + Component::DefaultNamespace(MATHML.into()), Component::LocalName(LocalName { name: DummyAtom::from("e"), lower_name: DummyAtom::from("e") }), )), - pseudo_element: None, - specificity: specificity(0, 0, 1), + specificity_and_flags: specificity(0, 0, 1), + })))); + assert_eq!(parse_ns("*", &parser), Ok(SelectorList(vec!(Selector { + inner: SelectorInner::from_vec( + vec!( + Component::DefaultNamespace(MATHML.into()), + Component::ExplicitUniversalType, + )), + specificity_and_flags: specificity(0, 0, 0), + })))); + assert_eq!(parse_ns("*|*", &parser), Ok(SelectorList(vec!(Selector { + inner: SelectorInner::from_vec( + vec!( + Component::ExplicitAnyNamespace, + Component::ExplicitUniversalType, + )), + specificity_and_flags: specificity(0, 0, 0), + })))); + // Default namespace applies to universal and type selectors inside :not and :matches, + // but not otherwise. + assert_eq!(parse_ns(":not(.cl)", &parser), Ok(SelectorList(vec!(Selector { + inner: SelectorInner::from_vec(vec!( + Component::DefaultNamespace(MATHML.into()), + Component::Negation(vec![ + Component::Class(DummyAtom::from("cl")) + ].into_boxed_slice()), + )), + specificity_and_flags: specificity(0, 1, 0), + })))); + assert_eq!(parse_ns(":not(*)", &parser), Ok(SelectorList(vec!(Selector { + inner: SelectorInner::from_vec(vec!( + Component::DefaultNamespace(MATHML.into()), + Component::Negation(vec![ + Component::DefaultNamespace(MATHML.into()), + Component::ExplicitUniversalType, + ].into_boxed_slice()), + )), + specificity_and_flags: specificity(0, 0, 0), + })))); + assert_eq!(parse_ns(":not(e)", &parser), Ok(SelectorList(vec!(Selector { + inner: SelectorInner::from_vec(vec!( + Component::DefaultNamespace(MATHML.into()), + Component::Negation(vec![ + Component::DefaultNamespace(MATHML.into()), + Component::LocalName(LocalName { + name: DummyAtom::from("e"), + lower_name: DummyAtom::from("e") + }), + ].into_boxed_slice()) + )), + specificity_and_flags: specificity(0, 0, 1), })))); assert_eq!(parse("[attr |= \"foo\"]"), Ok(SelectorList(vec![Selector { inner: SelectorInner::from_vec( vec![ - Component::AttrDashMatch(AttrSelector { - name: DummyAtom::from("attr"), - lower_name: DummyAtom::from("attr"), - namespace: NamespaceConstraint::Specific(Namespace { - prefix: None, - url: "".into(), - }), - }, DummyAtom::from("foo")) + Component::AttributeInNoNamespace { + local_name: DummyAtom::from("attr"), + local_name_lower: DummyAtom::from("attr"), + operator: AttrSelectorOperator::DashMatch, + value: DummyAtom::from("foo"), + never_matches: false, + case_sensitivity: ParsedCaseSensitivity::CaseSensitive, + } ]), - pseudo_element: None, - specificity: specificity(0, 1, 0), + specificity_and_flags: specificity(0, 1, 0), }]))); // https://github.com/mozilla/servo/issues/1723 assert_eq!(parse("::before"), Ok(SelectorList(vec!(Selector { - inner: SelectorInner::from_vec(vec![]), - pseudo_element: Some(PseudoElement::Before), - specificity: specificity(0, 0, 1), + inner: SelectorInner::from_vec( + vec![ + Component::PseudoElement(PseudoElement::Before), + ] + ), + specificity_and_flags: specificity(0, 0, 1) | HAS_PSEUDO_BIT, + })))); + assert_eq!(parse("::before:hover"), Ok(SelectorList(vec!(Selector { + inner: SelectorInner::from_vec( + vec![ + Component::PseudoElement(PseudoElement::Before), + Component::NonTSPseudoClass(PseudoClass::Hover), + ] + ), + specificity_and_flags: specificity(0, 1, 1) | HAS_PSEUDO_BIT, + })))); + assert_eq!(parse("::before:hover:hover"), Ok(SelectorList(vec!(Selector { + inner: SelectorInner::from_vec( + vec![ + Component::PseudoElement(PseudoElement::Before), + Component::NonTSPseudoClass(PseudoClass::Hover), + Component::NonTSPseudoClass(PseudoClass::Hover), + ] + ), + specificity_and_flags: specificity(0, 2, 1) | HAS_PSEUDO_BIT, })))); + assert_eq!(parse("::before:hover:active"), Err(())); + assert_eq!(parse("::before:hover .foo"), Err(())); + assert_eq!(parse("::before .foo"), Err(())); + assert_eq!(parse("::before ~ bar"), Err(())); + assert_eq!(parse("::before:active"), Err(())); + // https://github.com/servo/servo/issues/15335 assert_eq!(parse(":: before"), Err(())); assert_eq!(parse("div ::after"), Ok(SelectorList(vec!(Selector { @@ -1697,9 +2031,10 @@ pub mod tests { name: DummyAtom::from("div"), lower_name: DummyAtom::from("div") }), Component::Combinator(Combinator::Descendant), + Component::Combinator(Combinator::PseudoElement), + Component::PseudoElement(PseudoElement::After), ]), - pseudo_element: Some(PseudoElement::After), - specificity: specificity(0, 0, 2), + specificity_and_flags: specificity(0, 0, 2) | HAS_PSEUDO_BIT, })))); assert_eq!(parse("#d1 > .ok"), Ok(SelectorList(vec![Selector { inner: SelectorInner::from_vec( @@ -1708,8 +2043,7 @@ pub mod tests { Component::Combinator(Combinator::Child), Component::Class(DummyAtom::from("ok")), ]), - pseudo_element: None, - specificity: (1 << 20) + (1 << 10) + (0 << 0), + specificity_and_flags: (1 << 20) + (1 << 10) + (0 << 0), }]))); parser.default_ns = None; assert_eq!(parse(":not(#provel.old)"), Err(())); @@ -1721,25 +2055,86 @@ pub mod tests { Component::ID(DummyAtom::from("provel")), ].into_boxed_slice() ))), - pseudo_element: None, - specificity: specificity(1, 0, 0), + specificity_and_flags: specificity(1, 0, 0), })))); assert_eq!(parse_ns(":not(svg|circle)", &parser), Ok(SelectorList(vec!(Selector { inner: SelectorInner::from_vec(vec!(Component::Negation( vec![ - Component::Namespace(Namespace { - prefix: Some(DummyAtom("svg".into())), - url: SVG.into(), - }), + Component::Namespace(DummyAtom("svg".into()), SVG.into()), Component::LocalName(LocalName { name: DummyAtom::from("circle"), lower_name: DummyAtom::from("circle") }), ].into_boxed_slice() ))), - pseudo_element: None, - specificity: specificity(0, 0, 1), + specificity_and_flags: specificity(0, 0, 1), })))); + // https://github.com/servo/servo/issues/16017 + assert_eq!(parse_ns(":not(*)", &parser), Ok(SelectorList(vec!(Selector { + inner: SelectorInner::from_vec(vec!(Component::Negation( + vec![ + Component::ExplicitUniversalType, + ].into_boxed_slice() + ))), + specificity_and_flags: specificity(0, 0, 0), + })))); + assert_eq!(parse_ns(":not(|*)", &parser), Ok(SelectorList(vec!(Selector { + inner: SelectorInner::from_vec(vec!(Component::Negation( + vec![ + Component::ExplicitNoNamespace, + Component::ExplicitUniversalType, + ].into_boxed_slice() + ))), + specificity_and_flags: specificity(0, 0, 0), + })))); + assert_eq!(parse_ns(":not(*|*)", &parser), Ok(SelectorList(vec!(Selector { + inner: SelectorInner::from_vec(vec!(Component::Negation( + vec![ + Component::ExplicitAnyNamespace, + Component::ExplicitUniversalType, + ].into_boxed_slice() + ))), + specificity_and_flags: specificity(0, 0, 0), + })))); + assert_eq!(parse_ns(":not(svg|*)", &parser), Ok(SelectorList(vec!(Selector { + inner: SelectorInner::from_vec(vec!(Component::Negation( + vec![ + Component::Namespace(DummyAtom("svg".into()), SVG.into()), + Component::ExplicitUniversalType, + ].into_boxed_slice() + ))), + specificity_and_flags: specificity(0, 0, 0), + })))); + } + + #[test] + fn test_pseudo_iter() { + let selector = &parse("q::before").unwrap().0[0]; + assert!(!selector.is_universal()); + let mut iter = selector.inner.complex.iter(); + assert_eq!(iter.next(), Some(&Component::PseudoElement(PseudoElement::Before))); + assert_eq!(iter.next(), None); + let combinator = iter.next_sequence(); + assert_eq!(combinator, Some(Combinator::PseudoElement)); + assert!(matches!(iter.next(), Some(&Component::LocalName(..)))); + assert_eq!(iter.next(), None); + assert_eq!(iter.next_sequence(), None); + } + + #[test] + fn test_universal() { + let selector = &parse("*|*::before").unwrap().0[0]; + assert!(selector.is_universal()); + } + + #[test] + fn test_empty_pseudo_iter() { + let selector = &parse("::before").unwrap().0[0]; + assert!(selector.is_universal()); + let mut iter = selector.inner.complex.iter(); + assert_eq!(iter.next(), Some(&Component::PseudoElement(PseudoElement::Before))); + assert_eq!(iter.next(), None); + assert_eq!(iter.next_sequence(), None); } struct TestVisitor { @@ -1762,5 +2157,9 @@ pub mod tests { let mut test_visitor = TestVisitor { seen: vec![], }; parse(":not(:hover) ~ label").unwrap().0[0].visit(&mut test_visitor); assert!(test_visitor.seen.contains(&":hover".into())); + + let mut test_visitor = TestVisitor { seen: vec![], }; + parse("::before:hover").unwrap().0[0].visit(&mut test_visitor); + assert!(test_visitor.seen.contains(&":hover".into())); } } diff --git a/components/selectors/size_of_tests.rs b/components/selectors/size_of_tests.rs index 1d06278abd5..5fec3085fcb 100644 --- a/components/selectors/size_of_tests.rs +++ b/components/selectors/size_of_tests.rs @@ -3,21 +3,25 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use cssparser::ToCss; +use gecko_like_types; use gecko_like_types::*; +use parser; use parser::*; use precomputed_hash::PrecomputedHash; use std::fmt; use visitor::SelectorVisitor; -size_of_test!(size_of_selector, Selector<Impl>, 72); -size_of_test!(size_of_pseudo_element, PseudoElementSelector, 16); +size_of_test!(size_of_selector, Selector<Impl>, 48); +size_of_test!(size_of_pseudo_element, gecko_like_types::PseudoElement, 1); size_of_test!(size_of_selector_inner, SelectorInner<Impl>, 40); size_of_test!(size_of_complex_selector, ComplexSelector<Impl>, 24); -size_of_test!(size_of_component, Component<Impl>, 64); -size_of_test!(size_of_attr_selector, AttrSelector<Impl>, 48); +size_of_test!(size_of_component, Component<Impl>, 32); size_of_test!(size_of_pseudo_class, PseudoClass, 24); +impl parser::PseudoElement for gecko_like_types::PseudoElement { + type Impl = Impl; +} // Boilerplate @@ -31,7 +35,7 @@ impl SelectorImpl for Impl { type BorrowedLocalName = Atom; type BorrowedNamespaceUrl = Atom; type NonTSPseudoClass = PseudoClass; - type PseudoElementSelector = PseudoElementSelector; + type PseudoElement = gecko_like_types::PseudoElement; } impl SelectorMethods for PseudoClass { @@ -45,7 +49,7 @@ impl ToCss for PseudoClass { fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write { unimplemented!() } } -impl ToCss for PseudoElementSelector { +impl ToCss for gecko_like_types::PseudoElement { fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write { unimplemented!() } } diff --git a/components/selectors/tree.rs b/components/selectors/tree.rs index abd56e85cd4..276c788d05b 100644 --- a/components/selectors/tree.rs +++ b/components/selectors/tree.rs @@ -5,147 +5,61 @@ //! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between layout and //! style. -use matching::{ElementSelectorFlags, StyleRelations}; -use parser::{AttrSelector, SelectorImpl}; -use std::ascii::AsciiExt; +use attr::{AttrSelectorOperation, NamespaceConstraint}; +use matching::{ElementSelectorFlags, MatchingContext}; +use parser::SelectorImpl; -/// The definition of whitespace per CSS Selectors Level 3 § 4. -pub static SELECTOR_WHITESPACE: &'static [char] = &[' ', '\t', '\n', '\r', '\x0C']; - -// Attribute matching routines. Consumers with simple implementations can implement -// MatchAttrGeneric instead. -pub trait MatchAttr { - type Impl: SelectorImpl; - - fn match_attr_has( - &self, - attr: &AttrSelector<Self::Impl>) -> bool; - - fn match_attr_equals( - &self, - attr: &AttrSelector<Self::Impl>, - value: &<Self::Impl as SelectorImpl>::AttrValue) -> bool; - - fn match_attr_equals_ignore_ascii_case( - &self, - attr: &AttrSelector<Self::Impl>, - value: &<Self::Impl as SelectorImpl>::AttrValue) -> bool; - - fn match_attr_includes( - &self, - attr: &AttrSelector<Self::Impl>, - value: &<Self::Impl as SelectorImpl>::AttrValue) -> bool; - - fn match_attr_dash( - &self, - attr: &AttrSelector<Self::Impl>, - value: &<Self::Impl as SelectorImpl>::AttrValue) -> bool; - - fn match_attr_prefix( - &self, - attr: &AttrSelector<Self::Impl>, - value: &<Self::Impl as SelectorImpl>::AttrValue) -> bool; - - fn match_attr_substring( - &self, - attr: &AttrSelector<Self::Impl>, - value: &<Self::Impl as SelectorImpl>::AttrValue) -> bool; - - fn match_attr_suffix( - &self, - attr: &AttrSelector<Self::Impl>, - value: &<Self::Impl as SelectorImpl>::AttrValue) -> bool; -} - -pub trait MatchAttrGeneric { +pub trait Element: Sized { type Impl: SelectorImpl; - fn match_attr<F>(&self, attr: &AttrSelector<Self::Impl>, test: F) -> bool where F: Fn(&str) -> bool; -} - -impl<T> MatchAttr for T where T: MatchAttrGeneric, T::Impl: SelectorImpl<AttrValue = String> { - type Impl = T::Impl; - - fn match_attr_has(&self, attr: &AttrSelector<Self::Impl>) -> bool { - self.match_attr(attr, |_| true) - } - - fn match_attr_equals(&self, attr: &AttrSelector<Self::Impl>, value: &String) -> bool { - self.match_attr(attr, |v| v == value) - } - - fn match_attr_equals_ignore_ascii_case(&self, attr: &AttrSelector<Self::Impl>, - value: &String) -> bool { - self.match_attr(attr, |v| v.eq_ignore_ascii_case(value)) - } - - fn match_attr_includes(&self, attr: &AttrSelector<Self::Impl>, value: &String) -> bool { - self.match_attr(attr, |attr_value| { - attr_value.split(SELECTOR_WHITESPACE).any(|v| v == value) - }) - } - fn match_attr_dash(&self, attr: &AttrSelector<Self::Impl>, value: &String) -> bool { - self.match_attr(attr, |attr_value| { - // The attribute must start with the pattern. - if !attr_value.starts_with(value) { - return false - } - - // If the strings are the same, we're done. - if attr_value.len() == value.len() { - return true - } - - // The attribute is long than the pattern, so the next character must be '-'. - attr_value.as_bytes()[value.len()] == '-' as u8 - }) - } - - fn match_attr_prefix(&self, attr: &AttrSelector<Self::Impl>, value: &String) -> bool { - self.match_attr(attr, |attr_value| { - attr_value.starts_with(value) - }) - } - - fn match_attr_substring(&self, attr: &AttrSelector<Self::Impl>, value: &String) -> bool { - self.match_attr(attr, |attr_value| { - attr_value.contains(value) - }) - } + fn parent_element(&self) -> Option<Self>; - fn match_attr_suffix(&self, attr: &AttrSelector<Self::Impl>, value: &String) -> bool { - self.match_attr(attr, |attr_value| { - attr_value.ends_with(value) - }) + /// The parent of a given pseudo-element, after matching a pseudo-element + /// selector. + /// + /// This is guaranteed to be called in a pseudo-element. + fn pseudo_element_originating_element(&self) -> Option<Self> { + self.parent_element() } -} -pub trait Element: MatchAttr + Sized { - fn parent_element(&self) -> Option<Self>; - - // Skips non-element nodes + /// Skips non-element nodes fn first_child_element(&self) -> Option<Self>; - // Skips non-element nodes + /// Skips non-element nodes fn last_child_element(&self) -> Option<Self>; - // Skips non-element nodes + /// Skips non-element nodes fn prev_sibling_element(&self) -> Option<Self>; - // Skips non-element nodes + /// Skips non-element nodes fn next_sibling_element(&self) -> Option<Self>; fn is_html_element_in_html_document(&self) -> bool; + fn get_local_name(&self) -> &<Self::Impl as SelectorImpl>::BorrowedLocalName; + + /// Empty string for no namespace fn get_namespace(&self) -> &<Self::Impl as SelectorImpl>::BorrowedNamespaceUrl; + fn attr_matches(&self, + ns: &NamespaceConstraint<&<Self::Impl as SelectorImpl>::NamespaceUrl>, + local_name: &<Self::Impl as SelectorImpl>::LocalName, + operation: &AttrSelectorOperation<&<Self::Impl as SelectorImpl>::AttrValue>) + -> bool; + fn match_non_ts_pseudo_class<F>(&self, pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass, - relations: &mut StyleRelations, + context: &mut MatchingContext, flags_setter: &mut F) -> bool where F: FnMut(&Self, ElementSelectorFlags); + fn match_pseudo_element(&self, + pe: &<Self::Impl as SelectorImpl>::PseudoElement, + context: &mut MatchingContext) + -> bool; + fn get_id(&self) -> Option<<Self::Impl as SelectorImpl>::Identifier>; + fn has_class(&self, name: &<Self::Impl as SelectorImpl>::ClassName) -> bool; /// Returns whether this element matches `:empty`. @@ -160,10 +74,4 @@ pub trait Element: MatchAttr + Sized { /// Note: this can be false even if `.parent_element()` is `None` /// if the parent node is a `DocumentFragment`. fn is_root(&self) -> bool; - - // Ordinarily I wouldn't use callbacks like this, but the alternative is - // really messy, since there is a `JSRef` and a `RefCell` involved. Maybe - // in the future when we have associated types and/or a more convenient - // JS GC story... --pcwalton - fn each_class<F>(&self, callback: F) where F: FnMut(&<Self::Impl as SelectorImpl>::ClassName); } diff --git a/components/selectors/visitor.rs b/components/selectors/visitor.rs index be335aed87b..07f121fe82f 100644 --- a/components/selectors/visitor.rs +++ b/components/selectors/visitor.rs @@ -6,8 +6,8 @@ #![deny(missing_docs)] -use parser::{AttrSelector, Combinator, Component}; -use parser::{SelectorImpl, SelectorIter}; +use attr::NamespaceConstraint; +use parser::{Combinator, Component, SelectorImpl, SelectorIter}; /// A trait to visit selector properties. /// @@ -20,7 +20,12 @@ pub trait SelectorVisitor { /// Visit an attribute selector that may match (there are other selectors /// that may never match, like those containing whitespace or the empty /// string). - fn visit_attribute_selector(&mut self, _: &AttrSelector<Self::Impl>) -> bool { + fn visit_attribute_selector( + &mut self, + _namespace: &NamespaceConstraint<&<Self::Impl as SelectorImpl>::NamespaceUrl>, + _local_name: &<Self::Impl as SelectorImpl>::LocalName, + _local_name_lower: &<Self::Impl as SelectorImpl>::LocalName, + ) -> bool { true } diff --git a/components/servo/lib.rs b/components/servo/lib.rs index e3afaada420..83362789b05 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -126,6 +126,8 @@ impl<Window> Browser<Window> where Window: WindowMethods + 'static { // Global configuration options, parsed from the command line. let opts = opts::get(); + // Make sure the gl context is made current. + window.prepare_for_composite(0, 0); // Get both endpoints of a special channel for communication between // the client window and the compositor. This channel is unique because diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index 57bc8e31a97..f9b93b7500d 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -14,7 +14,7 @@ doctest = false [features] gecko = ["nsstring_vendor", "rayon/unstable", "num_cpus", "style_traits/gecko"] -use_bindgen = ["bindgen", "regex"] +use_bindgen = ["bindgen", "regex", "toml"] servo = ["serde", "serde_derive", "heapsize", "heapsize_derive", "style_traits/servo", "servo_atoms", "servo_config", "html5ever", "cssparser/heapsize", "cssparser/serde", "encoding", "smallvec/heapsizeof", @@ -29,12 +29,13 @@ bitflags = "0.7" bit-vec = "0.4.3" byteorder = "1.0" cfg-if = "0.1.0" -cssparser = "0.13.3" +cssparser = "0.13.5" encoding = {version = "0.2", optional = true} euclid = "0.11" fnv = "1.0" heapsize = {version = "0.3.0", optional = true} heapsize_derive = {version = "0.1", optional = true} +itoa = "0.3" html5ever = {version = "0.16", optional = true} lazy_static = "0.2" log = "0.3" @@ -68,3 +69,4 @@ log = "0.3" bindgen = { version = "0.25", optional = true } regex = {version = "0.2", optional = true} walkdir = "1.0" +toml = {version = "0.2.1", optional = true, default-features = false} diff --git a/components/style/attr.rs b/components/style/attr.rs index bbf8d53a430..535d9109151 100644 --- a/components/style/attr.rs +++ b/components/style/attr.rs @@ -12,6 +12,7 @@ use cssparser::{self, Color, RGBA}; use euclid::num::Zero; use num_traits::ToPrimitive; use properties::PropertyDeclarationBlock; +use selectors::attr::AttrSelectorOperation; use servo_url::ServoUrl; use shared_lock::Locked; use std::ascii::AsciiExt; @@ -349,6 +350,13 @@ impl AttrValue { panic!("Uint not found"); } } + + pub fn eval_selector(&self, selector: &AttrSelectorOperation<&String>) -> bool { + // FIXME(SimonSapin) this can be more efficient by matching on `(self, selector)` variants + // and doing Atom comparisons instead of string comparisons where possible, + // with SelectorImpl::AttrValue changed to Atom. + selector.eval_str(self) + } } impl ::std::ops::Deref for AttrValue { @@ -371,6 +379,15 @@ impl ::std::ops::Deref for AttrValue { } } +impl PartialEq<Atom> for AttrValue { + fn eq(&self, other: &Atom) -> bool { + match *self { + AttrValue::Atom(ref value) => value == other, + _ => other == &**self, + } + } +} + /// https://html.spec.whatwg.org/multipage/#rules-for-parsing-non-zero-dimension-values pub fn parse_nonzero_length(value: &str) -> LengthOrPercentageOrAuto { match parse_length(value) { diff --git a/components/style/build.rs b/components/style/build.rs index 8d40ccb9969..e5d3b40c204 100644 --- a/components/style/build.rs +++ b/components/style/build.rs @@ -10,6 +10,8 @@ extern crate bindgen; extern crate log; #[cfg(feature = "bindgen")] extern crate regex; +#[cfg(feature = "bindgen")] +extern crate toml; extern crate walkdir; use std::env; diff --git a/components/style/build_gecko.rs b/components/style/build_gecko.rs index 6da76e25f42..a6ecb8ac732 100644 --- a/components/style/build_gecko.rs +++ b/components/style/build_gecko.rs @@ -29,18 +29,20 @@ mod common { mod bindings { use bindgen::{Builder, CodegenConfig}; use bindgen::callbacks::{EnumVariantCustomBehavior, EnumVariantValue, ParseCallbacks}; - use regex::Regex; + use regex::{Regex, RegexSet}; use std::cmp; - use std::collections::HashSet; + use std::collections::{HashSet, HashMap}; use std::env; use std::fs::{self, File}; use std::io::{Read, Write}; use std::path::{Path, PathBuf}; use std::process::{Command, exit}; + use std::slice; use std::sync::Mutex; use std::time::SystemTime; use super::common::*; use super::super::PYTHON; + use toml; const STRUCTS_DEBUG_FILE: &'static str = "structs_debug.rs"; const STRUCTS_RELEASE_FILE: &'static str = "structs_release.rs"; @@ -60,6 +62,41 @@ mod bindings { } lazy_static! { + static ref CONFIG: toml::Table = { + let path = PathBuf::from(env::var("MOZ_SRC").unwrap()) + .join("layout/style/ServoBindings.toml"); + println!("cargo:rerun-if-changed={}", path.to_str().unwrap()); + update_last_modified(&path); + + let mut contents = String::new(); + File::open(path).expect("Failed to open config file") + .read_to_string(&mut contents).expect("Failed to read config file"); + let mut parser = toml::Parser::new(&contents); + if let Some(result) = parser.parse() { + result + } else { + use std::fmt::Write; + let mut reason = String::from("Failed to parse config file:"); + for err in parser.errors.iter() { + let parsed = &contents[..err.lo]; + write!(&mut reason, "\n* line {} column {}: {}", + parsed.lines().count(), + parsed.lines().last().map_or(0, |l| l.len()), + err).unwrap(); + } + panic!(reason) + } + }; + static ref TARGET_INFO: HashMap<String, String> = { + const TARGET_PREFIX: &'static str = "CARGO_CFG_TARGET_"; + let mut result = HashMap::new(); + for (k, v) in env::vars() { + if k.starts_with(TARGET_PREFIX) { + result.insert(k[TARGET_PREFIX.len()..].to_lowercase(), v); + } + } + result + }; static ref INCLUDE_RE: Regex = Regex::new(r#"#include\s*"(.+?)""#).unwrap(); static ref DISTDIR_PATH: PathBuf = { let path = PathBuf::from(env::var("MOZ_DIST").unwrap()); @@ -73,7 +110,7 @@ mod bindings { DISTDIR_PATH.join("include/nspr"), ]; static ref ADDED_PATHS: Mutex<HashSet<PathBuf>> = Mutex::new(HashSet::new()); - pub static ref LAST_MODIFIED: Mutex<SystemTime> = + static ref LAST_MODIFIED: Mutex<SystemTime> = Mutex::new(get_modified_time(&env::current_exe().unwrap()) .expect("Failed to get modified time of executable")); } @@ -82,16 +119,19 @@ mod bindings { file.metadata().and_then(|m| m.modified()).ok() } + fn update_last_modified(file: &Path) { + let modified = get_modified_time(file).unwrap(); + let mut last_modified = LAST_MODIFIED.lock().unwrap(); + *last_modified = cmp::max(modified, *last_modified); + } + fn search_include(name: &str) -> Option<PathBuf> { for path in SEARCH_PATHS.iter() { let file = path.join(name); - if !file.is_file() { - continue; + if file.is_file() { + update_last_modified(&file); + return Some(file); } - let modified = get_modified_time(&file).unwrap(); - let mut last_modified = LAST_MODIFIED.lock().unwrap(); - *last_modified = cmp::max(modified, *last_modified); - return Some(file); } None } @@ -124,22 +164,43 @@ mod bindings { trait BuilderExt { fn get_initial_builder(build_type: BuildType) -> Builder; fn include<T: Into<String>>(self, file: T) -> Builder; - fn zero_size_type(self, ty: &str, structs_list: &[&str]) -> Builder; + fn zero_size_type(self, ty: &str, structs_list: &HashSet<&str>) -> Builder; fn borrowed_type(self, ty: &str) -> Builder; fn mutable_borrowed_type(self, ty: &str) -> Builder; } + fn add_clang_args(mut builder: Builder, config: &toml::Table, matched_os: &mut bool) -> Builder { + fn add_args(mut builder: Builder, values: &[toml::Value]) -> Builder { + for item in values.iter() { + builder = builder.clang_arg(item.as_str().expect("Expect string in list")); + } + builder + } + for (k, v) in config.iter() { + if k == "args" { + builder = add_args(builder, v.as_slice().unwrap()); + continue; + } + let equal_idx = k.find('=').expect(&format!("Invalid key: {}", k)); + let (target_type, target_value) = k.split_at(equal_idx); + if TARGET_INFO[target_type] != target_value[1..] { + continue; + } + if target_type == "os" { + *matched_os = true; + } + builder = match *v { + toml::Value::Table(ref table) => add_clang_args(builder, table, matched_os), + toml::Value::Array(ref array) => add_args(builder, array), + _ => panic!("Unknown type"), + }; + } + builder + } + impl BuilderExt for Builder { fn get_initial_builder(build_type: BuildType) -> Builder { let mut builder = Builder::default().no_unstable_rust(); - let args = [ - "-x", "c++", "-std=c++14", - "-DTRACING=1", "-DIMPL_LIBXUL", "-DMOZ_STYLO_BINDINGS=1", - "-DMOZILLA_INTERNAL_API", "-DRUST_BINDGEN", "-DMOZ_STYLO" - ]; - for &arg in args.iter() { - builder = builder.clang_arg(arg); - } for dir in SEARCH_PATHS.iter() { builder = builder.clang_arg("-I").clang_arg(dir.to_str().unwrap()); } @@ -148,55 +209,11 @@ mod bindings { if build_type == BuildType::Debug { builder = builder.clang_arg("-DDEBUG=1").clang_arg("-DJS_DEBUG=1"); } - if cfg!(target_family = "unix") { - builder = builder.clang_arg("-DOS_POSIX=1"); - } - if cfg!(target_os = "linux") { - builder = builder.clang_arg("-DOS_LINUX=1"); - // We may be cross-compiling with a clang that defaults to - // a different architecture, so we should explicitly specify - // the bitness being used here. Specifying --target instead - // leads to difficulties with LLVM search paths. - if cfg!(target_arch = "x86") { - builder = builder.clang_arg("-m32") - } else if cfg!(target_arch = "x86_64") { - builder = builder.clang_arg("-m64") - } - } else if cfg!(target_os = "solaris") { - builder = builder.clang_arg("-DOS_SOLARIS=1"); - } else if cfg!(target_os = "dragonfly") { - builder = builder.clang_arg("-DOS_BSD=1").clang_arg("-DOS_DRAGONFLY=1"); - } else if cfg!(target_os = "freebsd") { - builder = builder.clang_arg("-DOS_BSD=1").clang_arg("-DOS_FREEBSD=1"); - } else if cfg!(target_os = "netbsd") { - builder = builder.clang_arg("-DOS_BSD=1").clang_arg("-DOS_NETBSD=1"); - } else if cfg!(target_os = "openbsd") { - builder = builder.clang_arg("-DOS_BSD=1").clang_arg("-DOS_OPENBSD=1"); - } else if cfg!(target_os = "macos") { - builder = builder.clang_arg("-DOS_MACOSX=1") - .clang_arg("-stdlib=libc++") - // To disable the fixup bindgen applies which adds search - // paths from clang command line in order to avoid potential - // conflict with -stdlib=libc++. - .clang_arg("--target=x86_64-apple-darwin"); - } else if cfg!(target_env = "msvc") { - builder = builder.clang_arg("-DOS_WIN=1").clang_arg("-DWIN32=1") - // For compatibility with MSVC 2015 - .clang_arg("-fms-compatibility-version=19") - // To enable the builtin __builtin_offsetof so that CRT wouldn't - // use reinterpret_cast in offsetof() which is not allowed inside - // static_assert(). - .clang_arg("-D_CRT_USE_BUILTIN_OFFSETOF") - // Enable hidden attribute (which is not supported by MSVC and - // thus not enabled by default with a MSVC-compatibile build) - // to exclude hidden symbols from the generated file. - .clang_arg("-DHAVE_VISIBILITY_HIDDEN_ATTRIBUTE=1"); - if cfg!(target_pointer_width = "32") { - builder = builder.clang_arg("--target=i686-pc-win32"); - } else { - builder = builder.clang_arg("--target=x86_64-pc-win32"); - } - } else { + + let mut matched_os = false; + let build_config = CONFIG["build"].as_table().expect("Malformed config file"); + builder = add_clang_args(builder, build_config, &mut matched_os); + if !matched_os { panic!("Unknown platform"); } builder @@ -212,8 +229,8 @@ mod bindings { // Not 100% sure of how safe this is, but it's what we're using // in the XPCOM ffi too // https://github.com/nikomatsakis/rust-memory-model/issues/2 - fn zero_size_type(self, ty: &str, structs_list: &[&str]) -> Builder { - if !structs_list.contains(&ty) { + fn zero_size_type(self, ty: &str, structs_list: &HashSet<&str>) -> Builder { + if !structs_list.contains(ty) { self.hide_type(ty) .raw_line(format!("enum {}Void {{ }}", ty)) .raw_line(format!("pub struct {0}({0}Void);", ty)) @@ -284,318 +301,123 @@ mod bindings { .collect() } - #[derive(Debug)] - struct Callbacks; - impl ParseCallbacks for Callbacks { - fn enum_variant_behavior(&self, - enum_name: Option<&str>, - variant_name: &str, - _variant_value: EnumVariantValue) - -> Option<EnumVariantCustomBehavior> { - if enum_name.map_or(false, |n| n == "nsCSSPropertyID") && - variant_name.starts_with("eCSSProperty_COUNT") { - Some(EnumVariantCustomBehavior::Constify) - } else { - None + struct BuilderWithConfig<'a> { + builder: Builder, + config: &'a toml::Table, + used_keys: HashSet<&'static str>, + } + impl<'a> BuilderWithConfig<'a> { + fn new(builder: Builder, config: &'a toml::Table) -> Self { + BuilderWithConfig { + builder, config, + used_keys: HashSet::new(), + } + } + + fn handle_list<F>(self, key: &'static str, func: F) -> BuilderWithConfig<'a> + where F: FnOnce(Builder, slice::Iter<'a, toml::Value>) -> Builder { + let mut builder = self.builder; + let config = self.config; + let mut used_keys = self.used_keys; + if let Some(list) = config.get(key) { + used_keys.insert(key); + builder = func(builder, list.as_slice().unwrap().iter()); + } + BuilderWithConfig { builder, config, used_keys } + } + fn handle_items<F>(self, key: &'static str, mut func: F) -> BuilderWithConfig<'a> + where F: FnMut(Builder, &'a toml::Value) -> Builder { + self.handle_list(key, |b, iter| iter.fold(b, |b, item| func(b, item))) + } + fn handle_str_items<F>(self, key: &'static str, mut func: F) -> BuilderWithConfig<'a> + where F: FnMut(Builder, &'a str) -> Builder { + self.handle_items(key, |b, item| func(b, item.as_str().unwrap())) + } + fn handle_table_items<F>(self, key: &'static str, mut func: F) -> BuilderWithConfig<'a> + where F: FnMut(Builder, &'a toml::Table) -> Builder { + self.handle_items(key, |b, item| func(b, item.as_table().unwrap())) + } + fn handle_common(self, fixups: &mut Vec<Fixup>) -> BuilderWithConfig<'a> { + self.handle_str_items("headers", |b, item| b.header(add_include(item))) + .handle_str_items("raw-lines", |b, item| b.raw_line(item)) + .handle_str_items("hide-types", |b, item| b.hide_type(item)) + .handle_table_items("fixups", |builder, item| { + fixups.push(Fixup { + pat: item["pat"].as_str().unwrap().into(), + rep: item["rep"].as_str().unwrap().into(), + }); + builder + }) + } + + fn get_builder(self) -> Builder { + for key in self.config.keys() { + if !self.used_keys.contains(key.as_str()) { + panic!(format!("Unknown key: {}", key)); + } } + self.builder } } fn generate_structs(build_type: BuildType) { - let mut builder = Builder::get_initial_builder(build_type) + #[derive(Debug)] + struct Callbacks(HashMap<String, RegexSet>); + impl ParseCallbacks for Callbacks { + fn enum_variant_behavior(&self, + enum_name: Option<&str>, + variant_name: &str, + _variant_value: EnumVariantValue) + -> Option<EnumVariantCustomBehavior> { + enum_name.and_then(|enum_name| self.0.get(enum_name)) + .and_then(|regex| if regex.is_match(variant_name) { + Some(EnumVariantCustomBehavior::Constify) + } else { + None + }) + } + } + + let builder = Builder::get_initial_builder(build_type) .enable_cxx_namespaces() .with_codegen_config(CodegenConfig { types: true, vars: true, ..CodegenConfig::nothing() - }) - .header(add_include("nsCSSPseudoClasses.h")) // servo/rust-bindgen#599 - .include(add_include("nsStyleStruct.h")) - .include(add_include("mozilla/ServoPropPrefList.h")) - .include(add_include("mozilla/StyleAnimationValue.h")) - .include(add_include("gfxFontConstants.h")) - .include(add_include("nsThemeConstants.h")) - .include(add_include("mozilla/dom/AnimationEffectReadOnlyBinding.h")) - .include(add_include("mozilla/AnimationPropertySegment.h")) - .include(add_include("mozilla/ComputedTiming.h")) - .include(add_include("mozilla/ComputedTimingFunction.h")) - .include(add_include("mozilla/Keyframe.h")) - .include(add_include("mozilla/ServoElementSnapshot.h")) - .include(add_include("mozilla/ServoElementSnapshotTable.h")) - .include(add_include("mozilla/dom/Element.h")) - .include(add_include("mozilla/dom/NameSpaceConstants.h")) - .include(add_include("mozilla/LookAndFeel.h")) - .include(add_include("mozilla/ServoBindings.h")) - .include(add_include("nsCSSCounterStyleRule.h")) - .include(add_include("nsCSSFontFaceRule.h")) - .include(add_include("nsMediaFeatures.h")) - .include(add_include("nsMediaList.h")) - // FIXME(emilio): Incrementally remove these "pub use"s. Probably - // mozilla::css and mozilla::dom are easier. - .raw_line("pub use self::root::*;") - .raw_line("pub use self::root::mozilla::*;") - .raw_line("pub use self::root::mozilla::css::*;") - .raw_line("pub use self::root::mozilla::dom::*;") - .raw_line("use atomic_refcell::AtomicRefCell;") - .raw_line("use data::ElementData;") - .hide_type("nsString") - .bitfield_enum("nsChangeHint") - .bitfield_enum("nsRestyleHint") - .constified_enum("UpdateAnimationsTasks") - .constified_enum("ParsingMode") - .parse_callbacks(Box::new(Callbacks)); - let whitelist_vars = [ - "NS_AUTHOR_SPECIFIED_.*", - "NS_THEME_.*", - "NODE_.*", - "ELEMENT_.*", - "NS_FONT_.*", - "NS_STYLE_.*", - "NS_MATHML_.*", - "NS_RADIUS_.*", - "BORDER_COLOR_.*", - "BORDER_STYLE_.*", - "mozilla::SERVO_PREF_.*", - "CSS_PSEUDO_ELEMENT_.*", - "SERVO_CSS_PSEUDO_ELEMENT_FLAGS_.*", - "kNameSpaceID_.*", - "kGenericFont_.*", - "kPresContext_.*", - ]; - let whitelist = [ - "RawGecko.*", - "mozilla::AnimationPropertySegment", - "mozilla::ComputedTiming", - "mozilla::ComputedTimingFunction", - "mozilla::ComputedTimingFunction::BeforeFlag", - "mozilla::ServoStyleSheet", - "mozilla::ServoElementSnapshot.*", - "mozilla::CSSPseudoClassType", - "mozilla::css::SheetParsingMode", - "mozilla::css::URLMatchingFunction", - "mozilla::HalfCorner", - "mozilla::PropertyStyleAnimationValuePair", - "mozilla::TraversalRestyleBehavior", - "mozilla::TraversalRootBehavior", - "mozilla::StyleShapeRadius", - "mozilla::StyleGrid.*", - "mozilla::UpdateAnimationsTasks", - "mozilla::LookAndFeel", - ".*ThreadSafe.*Holder", - "AnonymousContent", - "AudioContext", - "CapturingContentInfo", - "DefaultDelete", - "DOMIntersectionObserverEntry", - "Element", - "FontFamilyList", - "FontFamilyListRefCnt", - "FontFamilyName", - "FontFamilyType", - "FontSizePrefs", - "FragmentOrURL", - "FrameRequestCallback", - "GeckoParserExtraData", - "GeckoFontMetrics", - "gfxAlternateValue", - "gfxFontFeature", - "gfxFontVariation", - "GridNamedArea", - "HalfCorner", - "Image", - "ImageURL", - "Keyframe", - "nsAttrName", - "nsAttrValue", - "nsBorderColors", - "nscolor", - "nsChangeHint", - "nsCSSCounterStyleRule", - "nsCSSFontFaceRule", - "nsCSSKeyword", - "nsCSSPropertyID", - "nsCSSProps", - "nsCSSRect", - "nsCSSRect_heap", - "nsCSSShadowArray", - "nsCSSValue", - "nsCSSValueFloatColor", - "nsCSSValueGradient", - "nsCSSValueGradientStop", - "nsCSSValueList", - "nsCSSValueList_heap", - "nsCSSValuePair_heap", - "nsCSSValuePairList", - "nsCSSValuePairList_heap", - "nsCSSValueTokenStream", - "nsCSSValueTriplet_heap", - "nsCursorImage", - "nsFont", - "nsIAtom", - "nsMainThreadPtrHandle", - "nsMainThreadPtrHolder", - "nsMargin", - "nsMediaExpression", - "nsMediaFeature", - "nsMediaFeatures", - "nsMediaList", - "nsRect", - "nsRestyleHint", - "nsresult", - "nsSize", - "nsStyleBackground", - "nsStyleBorder", - "nsStyleColor", - "nsStyleColumn", - "nsStyleContent", - "nsStyleContentData", - "nsStyleContext", - "nsStyleCoord", - "nsStyleCounterData", - "nsStyleDisplay", - "nsStyleEffects", - "nsStyleFilter", - "nsStyleFont", - "nsStyleGradient", - "nsStyleGradientStop", - "nsStyleGridTemplate", - "nsStyleImage", - "nsStyleImageLayers", - "nsStyleList", - "nsStyleMargin", - "nsStyleOutline", - "nsStylePadding", - "nsStylePosition", - "nsStyleSVG", - "nsStyleSVGPaint", - "nsStyleSVGReset", - "nsStyleTable", - "nsStyleTableBorder", - "nsStyleText", - "nsStyleTextReset", - "nsStyleUIReset", - "nsStyleUnion", - "nsStyleUnit", - "nsStyleUserInterface", - "nsStyleVariables", - "nsStyleVisibility", - "nsStyleXUL", - "nsTArray", - "nsTArrayHeader", - "Position", - "PropertyValuePair", - "Runnable", - "ServoAttrSnapshot", - "ServoBundledURI", - "ServoElementSnapshot", - "SheetParsingMode", - "StaticRefPtr", - "StyleAnimation", - "StyleBasicShape", - "StyleBasicShapeType", - "StyleGeometryBox", - "StyleShapeSource", - "StyleTransition", - "mozilla::UniquePtr", - "mozilla::DefaultDelete", - "mozilla::Side", - "mozilla::binding_danger::AssertAndSuppressCleanupPolicy", - "mozilla::ParsingMode", - "mozilla::InheritTarget", - ]; - let opaque_types = [ - "std::pair__PCCP", - "std::namespace::atomic___base", "std::atomic__My_base", - "std::atomic", - "std::atomic___base", - "mozilla::gfx::.*", - "FallibleTArray", - "mozilla::dom::Sequence", - "mozilla::dom::Optional", - "mozilla::dom::Nullable", - "RefPtr_Proxy", - "RefPtr_Proxy_member_function", - "nsAutoPtr_Proxy", - "nsAutoPtr_Proxy_member_function", - "mozilla::detail::PointerType", - "mozilla::Pair_Base", - "mozilla::SupportsWeakPtr", - "SupportsWeakPtr", - "mozilla::detail::WeakReference", - "mozilla::WeakPtr", - "nsWritingIterator_reference", "nsReadingIterator_reference", - "nsTObserverArray", // <- Inherits from nsAutoTObserverArray<T, 0> - "nsTHashtable", // <- Inheriting from inner typedefs that clang - // doesn't expose properly. - "nsRefPtrHashtable", "nsDataHashtable", "nsClassHashtable", // <- Ditto - "nsIDocument_SelectorCache", // <- Inherits from nsExpirationTracker<.., 4> - "nsIPresShell_ScrollAxis", // <- For some reason the alignment of this is 4 - // for clang. - "nsPIDOMWindow", // <- Takes the vtable from a template parameter, and we can't - // generate it conditionally. - "JS::Rooted", - "mozilla::Maybe", - "gfxSize", // <- union { struct { T width; T height; }; T components[2] }; - "gfxSize_Super", // Ditto. - "mozilla::ErrorResult", // Causes JSWhyMagic to be included & handled incorrectly. - "mozilla::StyleAnimationValue", - "StyleAnimationValue", // pulls in a whole bunch of stuff we don't need in the bindings - ]; - let blacklist = [ - ".*char_traits", - ".*incompatible_char_type", - ]; - - struct MappedGenericType { - generic: bool, - gecko: &'static str, - servo: &'static str, - } - let servo_mapped_generic_types = [ - MappedGenericType { - generic: true, - gecko: "mozilla::ServoUnsafeCell", - servo: "::std::cell::UnsafeCell" - }, - MappedGenericType { - generic: true, - gecko: "mozilla::ServoCell", - servo: "::std::cell::Cell" - }, - MappedGenericType { - generic: false, - gecko: "ServoNodeData", - servo: "AtomicRefCell<ElementData>", - } - ]; - let mut fixups = vec![ - Fixup { - pat: "root::nsString".into(), - rep: "::nsstring::nsStringRepr".into() - }, - ]; - for &var in whitelist_vars.iter() { - builder = builder.whitelisted_var(var); - } - for &ty in whitelist.iter() { - builder = builder.whitelisted_type(ty); - } - for &ty in opaque_types.iter() { - builder = builder.opaque_type(ty); - } - for &ty in blacklist.iter() { - builder = builder.hide_type(ty); - } - for ty in servo_mapped_generic_types.iter() { - let gecko_name = ty.gecko.rsplit("::").next().unwrap(); - builder = builder.hide_type(ty.gecko) - .raw_line(format!("pub type {0}{2} = {1}{2};", gecko_name, ty.servo, - if ty.generic { "<T>" } else { "" })); - fixups.push(Fixup { - pat: format!("root::{}", ty.gecko), - rep: format!("::gecko_bindings::structs::{}", gecko_name) }); - } + let mut fixups = vec![]; + let builder = BuilderWithConfig::new(builder, CONFIG["structs"].as_table().unwrap()) + .handle_common(&mut fixups) + .handle_str_items("bitfield-enums", |b, item| b.bitfield_enum(item)) + .handle_str_items("constified-enums", |b, item| b.constified_enum(item)) + .handle_str_items("whitelist-vars", |b, item| b.whitelisted_var(item)) + .handle_str_items("whitelist-types", |b, item| b.whitelisted_type(item)) + .handle_str_items("opaque-types", |b, item| b.opaque_type(item)) + .handle_list("constified-enum-variants", |builder, iter| { + let mut map = HashMap::new(); + for item in iter { + let item = item.as_table().unwrap(); + let name = item["enum"].as_str().unwrap(); + let variants = item["variants"].as_slice().unwrap().iter() + .map(|item| item.as_str().unwrap()); + map.insert(name.into(), RegexSet::new(variants).unwrap()); + } + builder.parse_callbacks(Box::new(Callbacks(map))) + }) + .handle_table_items("mapped-generic-types", |builder, item| { + let generic = item["generic"].as_bool().unwrap(); + let gecko = item["gecko"].as_str().unwrap(); + let servo = item["servo"].as_str().unwrap(); + let gecko_name = gecko.rsplit("::").next().unwrap(); + fixups.push(Fixup { + pat: format!("root::{}", gecko), + rep: format!("::gecko_bindings::structs::{}", gecko_name) + }); + builder.hide_type(gecko) + .raw_line(format!("pub type {0}{2} = {1}{2};", gecko_name, servo, + if generic { "<T>" } else { "" })) + }) + .get_builder(); write_binding_file(builder, structs_file(build_type), &fixups); } @@ -645,183 +467,62 @@ mod bindings { } fn generate_bindings() { - let mut builder = Builder::get_initial_builder(BuildType::Release) + let builder = Builder::get_initial_builder(BuildType::Release) .disable_name_namespacing() .with_codegen_config(CodegenConfig { functions: true, ..CodegenConfig::nothing() + }); + let config = CONFIG["bindings"].as_table().unwrap(); + let mut structs_types = HashSet::new(); + let mut fixups = vec![]; + let mut builder = BuilderWithConfig::new(builder, config) + .handle_common(&mut fixups) + .handle_str_items("whitelist-functions", |b, item| b.whitelisted_function(item)) + .handle_str_items("structs-types", |mut builder, ty| { + builder = builder.hide_type(ty) + .raw_line(format!("use gecko_bindings::structs::{};", ty)); + structs_types.insert(ty); + // TODO this is hacky, figure out a better way to do it without + // hardcoding everything... + if ty.starts_with("nsStyle") { + builder = builder + .raw_line(format!("unsafe impl Send for {} {{}}", ty)) + .raw_line(format!("unsafe impl Sync for {} {{}}", ty)); + } + builder }) - .header(add_include("mozilla/ServoBindings.h")) - .hide_type("nsACString_internal") - .hide_type("nsAString_internal") - .raw_line("pub use nsstring::{nsACString, nsAString, nsString};") - .raw_line("type nsACString_internal = nsACString;") - .raw_line("type nsAString_internal = nsAString;") - .whitelisted_function("Servo_.*") - .whitelisted_function("Gecko_.*"); - let structs_types = [ - "mozilla::css::GridTemplateAreasValue", - "mozilla::css::ImageValue", - "mozilla::css::URLValue", - "mozilla::Side", - "RawGeckoAnimationPropertySegment", - "RawGeckoComputedTiming", - "RawGeckoDocument", - "RawGeckoElement", - "RawGeckoKeyframeList", - "RawGeckoComputedKeyframeValuesList", - "RawGeckoFontFaceRuleList", - "RawGeckoNode", - "RawGeckoAnimationValueList", - "RawServoAnimationValue", - "RawServoDeclarationBlock", - "RawServoStyleRule", - "RawGeckoPresContext", - "RawGeckoPresContextOwned", - "RawGeckoStyleAnimationList", - "RawGeckoServoStyleRuleList", - "RawGeckoURLExtraData", - "RefPtr", - "CSSPseudoClassType", - "CSSPseudoElementType", - "TraversalRestyleBehavior", - "TraversalRootBehavior", - "ComputedTimingFunction_BeforeFlag", - "FontFamilyList", - "FontFamilyType", - "FontSizePrefs", - "GeckoFontMetrics", - "Keyframe", - "ServoBundledURI", - "ServoElementSnapshot", - "ServoElementSnapshotTable", - "SheetParsingMode", - "StyleBasicShape", - "StyleBasicShapeType", - "StyleShapeSource", - "StyleTransition", - "nsCSSCounterStyleRule", - "nsCSSFontFaceRule", - "nsCSSKeyword", - "nsCSSPropertyID", - "nsCSSShadowArray", - "nsCSSUnit", - "nsCSSValue", - "nsCSSValueSharedList", - "nsChangeHint", - "nsCursorImage", - "nsFont", - "nsIAtom", - "nsCompatibility", - "nsMediaFeature", - "nsRestyleHint", - "nsStyleBackground", - "nsStyleBorder", - "nsStyleColor", - "nsStyleColumn", - "nsStyleContent", - "nsStyleContentData", - "nsStyleContentType", - "nsStyleContext", - "nsStyleCoord", - "nsStyleCoord_Calc", - "nsStyleCoord_CalcValue", - "nsStyleDisplay", - "nsStyleEffects", - "nsStyleFilter", - "nsStyleFont", - "nsStyleGradient", - "nsStyleGradientStop", - "nsStyleGridTemplate", - "nsStyleImage", - "nsStyleImageLayers", - "nsStyleImageLayers_Layer", - "nsStyleImageLayers_LayerType", - "nsStyleImageRequest", - "nsStyleList", - "nsStyleMargin", - "nsStyleOutline", - "nsStylePadding", - "nsStylePosition", - "nsStyleQuoteValues", - "nsStyleSVG", - "nsStyleSVGPaint", - "nsStyleSVGReset", - "nsStyleTable", - "nsStyleTableBorder", - "nsStyleText", - "nsStyleTextReset", - "nsStyleUIReset", - "nsStyleUnion", - "nsStyleUnit", - "nsStyleUserInterface", - "nsStyleVariables", - "nsStyleVisibility", - "nsStyleXUL", - "nsTimingFunction", - "nscolor", - "nscoord", - "nsresult", - "Loader", - "ServoStyleSheet", - "EffectCompositor_CascadeLevel", - "UpdateAnimationsTasks", - "ParsingMode", - "InheritTarget", - "URLMatchingFunction", - ]; - struct ArrayType { - cpp_type: &'static str, - rust_type: &'static str - } - let array_types = [ - ArrayType { cpp_type: "uintptr_t", rust_type: "usize" }, - ]; - struct ServoOwnedType { - name: &'static str, - opaque: bool, - } - let servo_owned_types = [ - ServoOwnedType { name: "RawServoStyleSet", opaque: true }, - ServoOwnedType { name: "StyleChildrenIterator", opaque: true }, - ServoOwnedType { name: "ServoElementSnapshot", opaque: false }, - ServoOwnedType { name: "RawServoAnimationValueMap", opaque: true }, - ]; - let servo_immutable_borrow_types = [ - "RawGeckoNode", - "RawGeckoElement", - "RawGeckoDocument", - "RawServoDeclarationBlockStrong", - "RawGeckoPresContext", - "RawGeckoStyleAnimationList", - ]; - let servo_borrow_types = [ - "nsCSSValue", - "nsTimingFunction", - "RawGeckoAnimationPropertySegment", - "RawGeckoAnimationValueList", - "RawGeckoComputedTiming", - "RawGeckoKeyframeList", - "RawGeckoComputedKeyframeValuesList", - "RawGeckoFontFaceRuleList", - "RawGeckoServoStyleRuleList", - ]; - for &ty in structs_types.iter() { - builder = builder.hide_type(ty) - .raw_line(format!("use gecko_bindings::structs::{};", ty)); - // TODO this is hacky, figure out a better way to do it without - // hardcoding everything... - if ty.starts_with("nsStyle") { - builder = builder - .raw_line(format!("unsafe impl Send for {} {{}}", ty)) - .raw_line(format!("unsafe impl Sync for {} {{}}", ty)); - } - } - for &ArrayType { cpp_type, rust_type } in array_types.iter() { - builder = builder.hide_type(format!("nsTArrayBorrowed_{}", cpp_type)) - .raw_line(format!("pub type nsTArrayBorrowed_{}<'a> = &'a mut ::gecko_bindings::structs::nsTArray<{}>;", - cpp_type, rust_type)) - } + // TODO This was added due to servo/rust-bindgen#75, but + // that has been fixed in clang 4.0+. When we switch people + // to libclang 4.0, we can remove this. + .handle_table_items("array-types", |builder, item| { + let cpp_type = item["cpp-type"].as_str().unwrap(); + let rust_type = item["rust-type"].as_str().unwrap(); + builder.hide_type(format!("nsTArrayBorrowed_{}", cpp_type)) + .raw_line(format!(concat!("pub type nsTArrayBorrowed_{}<'a> = ", + "&'a mut ::gecko_bindings::structs::nsTArray<{}>;"), + cpp_type, rust_type)) + }) + .handle_table_items("servo-owned-types", |mut builder, item| { + let name = item["name"].as_str().unwrap(); + builder = builder.hide_type(format!("{}Owned", name)) + .raw_line(format!("pub type {0}Owned = ::gecko_bindings::sugar::ownership::Owned<{0}>;", name)) + .hide_type(format!("{}OwnedOrNull", name)) + .raw_line(format!(concat!("pub type {0}OwnedOrNull = ", + "::gecko_bindings::sugar::ownership::OwnedOrNull<{0}>;"), name)) + .mutable_borrowed_type(name); + if item["opaque"].as_bool().unwrap() { + builder = builder.zero_size_type(name, &structs_types); + } + builder + }) + .handle_str_items("servo-immutable-borrow-types", |b, ty| b.borrowed_type(ty)) + // Right now the only immutable borrow types are ones which we import + // from the |structs| module. As such, we don't need to create an opaque + // type with zero_size_type. If we ever introduce immutable borrow types + // which _do_ need to be opaque, we'll need a separate mode. + .handle_str_items("servo-borrow-types", |b, ty| b.mutable_borrowed_type(ty)) + .get_builder(); for ty in get_arc_types().iter() { builder = builder .hide_type(format!("{}Strong", ty)) @@ -829,29 +530,7 @@ mod bindings { .borrowed_type(ty) .zero_size_type(ty, &structs_types); } - for &ServoOwnedType { name, opaque } in servo_owned_types.iter() { - builder = builder - .hide_type(format!("{}Owned", name)) - .raw_line(format!("pub type {0}Owned = ::gecko_bindings::sugar::ownership::Owned<{0}>;", name)) - .hide_type(format!("{}OwnedOrNull", name)) - .raw_line(format!("pub type {0}OwnedOrNull = ::gecko_bindings::sugar::ownership::OwnedOrNull<{0}>;", - name)) - .mutable_borrowed_type(name); - if opaque { - builder = builder.zero_size_type(name, &structs_types); - } - } - for &ty in servo_immutable_borrow_types.iter() { - builder = builder.borrowed_type(ty); - } - for &ty in servo_borrow_types.iter() { - builder = builder.mutable_borrowed_type(ty); - // Right now the only immutable borrow types are ones which we import - // from the |structs| module. As such, we don't need to create an opaque - // type with zero_size_type. If we ever introduce immutable borrow types - // which _do_ need to be opaque, we'll need a separate mode. - } - write_binding_file(builder, BINDINGS_FILE, &Vec::new()); + write_binding_file(builder, BINDINGS_FILE, &fixups); } fn generate_atoms() { diff --git a/components/style/dom.rs b/components/style/dom.rs index 4dab32eba22..36997de5c32 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -381,6 +381,9 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone + /// Whether an attribute value equals `value`. fn attr_equals(&self, namespace: &Namespace, attr: &LocalName, value: &Atom) -> bool; + /// Internal iterator for the classes of this element. + fn each_class<F>(&self, callback: F) where F: FnMut(&Atom); + /// Get the pre-existing style to calculate restyle damage (change hints). /// /// This needs to be generic since it varies between Servo and Gecko. @@ -389,7 +392,7 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone + /// values as an argument here, but otherwise Servo would crash due to /// double borrows to return it. fn existing_style_for_restyle_damage<'a>(&'a self, - current_computed_values: &'a Arc<ComputedValues>, + current_computed_values: &'a ComputedValues, pseudo: Option<&PseudoElement>) -> Option<&'a PreExistingComputedValues>; diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 980ef36b280..1899a9799e1 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -24,7 +24,7 @@ impl From<CalcLengthOrPercentage> for nsStyleCoord_CalcValue { fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue { let has_percentage = other.percentage.is_some(); nsStyleCoord_CalcValue { - mLength: other.length.0, + mLength: other.unclamped_length().0, mPercent: other.percentage.unwrap_or(0.0), mHasPercent: has_percentage, } @@ -38,10 +38,7 @@ impl From<nsStyleCoord_CalcValue> for CalcLengthOrPercentage { } else { None }; - CalcLengthOrPercentage { - length: Au(other.mLength), - percentage: percentage, - } + Self::new(Au(other.mLength), percentage) } } diff --git a/components/style/gecko/generated/bindings.rs b/components/style/gecko/generated/bindings.rs index 439a3cd9ed0..ee99ffb4fe0 100644 --- a/components/style/gecko/generated/bindings.rs +++ b/components/style/gecko/generated/bindings.rs @@ -1,6 +1,7 @@ /* automatically generated by rust-bindgen */ -pub use nsstring::{nsACString, nsAString, nsString}; +pub use nsstring::{nsACString, nsAString, nsString, nsStringRepr}; +use gecko_bindings::structs::nsTArray; type nsACString_internal = nsACString; type nsAString_internal = nsAString; use gecko_bindings::structs::mozilla::css::GridTemplateAreasValue; @@ -944,6 +945,15 @@ extern "C" { pub fn Gecko_DropElementSnapshot(snapshot: ServoElementSnapshotOwned); } extern "C" { + pub fn Gecko_ResizeTArrayForStrings(array: *mut nsTArray<nsStringRepr>, + length: u32); +} +extern "C" { + pub fn Gecko_SetStyleGridTemplateArrayLengths(grid_template: + *mut nsStyleGridTemplate, + track_sizes: u32); +} +extern "C" { pub fn Gecko_CopyStyleGridTemplateValues(grid_template: *mut nsStyleGridTemplate, other: @@ -1670,7 +1680,8 @@ extern "C" { *const RawServoMediaList, extra_data: *mut RawGeckoURLExtraData, - line_number_offset: u32) + line_number_offset: u32, + quirks_mode: nsCompatibility) -> RawServoStyleSheetStrong; } extern "C" { @@ -1933,7 +1944,8 @@ extern "C" { pub fn Servo_ParseProperty(property: nsCSSPropertyID, value: *const nsACString, data: *mut RawGeckoURLExtraData, - parsing_mode: ParsingMode) + parsing_mode: ParsingMode, + quirks_mode: nsCompatibility) -> RawServoDeclarationBlockStrong; } extern "C" { @@ -2103,7 +2115,8 @@ extern "C" { value: *const nsACString, is_important: bool, data: *mut RawGeckoURLExtraData, - parsing_mode: ParsingMode) + parsing_mode: ParsingMode, + quirks_mode: nsCompatibility) -> bool; } extern "C" { @@ -2114,7 +2127,9 @@ extern "C" { is_important: bool, data: *mut RawGeckoURLExtraData, - parsing_mode: ParsingMode) + parsing_mode: ParsingMode, + quirks_mode: + nsCompatibility) -> bool; } extern "C" { diff --git a/components/style/gecko/generated/structs_debug.rs b/components/style/gecko/generated/structs_debug.rs index a693eaa3b7c..2f8b574b209 100644 --- a/components/style/gecko/generated/structs_debug.rs +++ b/components/style/gecko/generated/structs_debug.rs @@ -1196,65 +1196,101 @@ pub mod root { fn clone(&self) -> Self { *self } } #[repr(C)] + #[derive(Debug, Copy)] + pub struct forward_iterator_tag { + pub _address: u8, + } + #[test] + fn bindgen_test_layout_forward_iterator_tag() { + assert_eq!(::std::mem::size_of::<forward_iterator_tag>() , 1usize + , concat ! ( + "Size of: " , stringify ! ( forward_iterator_tag ) )); + assert_eq! (::std::mem::align_of::<forward_iterator_tag>() , + 1usize , concat ! ( + "Alignment of " , stringify ! ( forward_iterator_tag ) + )); + } + impl Clone for forward_iterator_tag { + fn clone(&self) -> Self { *self } + } + #[repr(C)] + #[derive(Debug, Copy)] + pub struct bidirectional_iterator_tag { + pub _address: u8, + } + #[test] + fn bindgen_test_layout_bidirectional_iterator_tag() { + assert_eq!(::std::mem::size_of::<bidirectional_iterator_tag>() , + 1usize , concat ! ( + "Size of: " , stringify ! ( bidirectional_iterator_tag + ) )); + assert_eq! (::std::mem::align_of::<bidirectional_iterator_tag>() , + 1usize , concat ! ( + "Alignment of " , stringify ! ( + bidirectional_iterator_tag ) )); + } + impl Clone for bidirectional_iterator_tag { + fn clone(&self) -> Self { *self } + } + #[repr(C)] + #[derive(Debug, Copy)] + pub struct random_access_iterator_tag { + pub _address: u8, + } + #[test] + fn bindgen_test_layout_random_access_iterator_tag() { + assert_eq!(::std::mem::size_of::<random_access_iterator_tag>() , + 1usize , concat ! ( + "Size of: " , stringify ! ( random_access_iterator_tag + ) )); + assert_eq! (::std::mem::align_of::<random_access_iterator_tag>() , + 1usize , concat ! ( + "Alignment of " , stringify ! ( + random_access_iterator_tag ) )); + } + impl Clone for random_access_iterator_tag { + fn clone(&self) -> Self { *self } + } + #[repr(C)] + pub struct iterator_traits { + pub _address: u8, + } + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct iterator { pub _address: u8, } - pub type iterator_iterator_category<_Category> = _Category; pub type iterator_value_type<_Tp> = _Tp; pub type iterator_difference_type<_Distance> = _Distance; pub type iterator_pointer<_Pointer> = _Pointer; pub type iterator_reference<_Reference> = _Reference; + pub type iterator_iterator_category<_Category> = _Category; #[repr(C)] - pub struct iterator_traits { - pub _address: u8, - } - #[repr(C)] - pub struct reverse_iterator<_Iterator> { - pub current: _Iterator, - pub _phantom_0: ::std::marker::PhantomData<::std::cell::UnsafeCell<_Iterator>>, + pub struct reverse_iterator<_Iter> { + pub __t: _Iter, + pub current: _Iter, + pub _phantom_0: ::std::marker::PhantomData<::std::cell::UnsafeCell<_Iter>>, } - pub type reverse_iterator___traits_type = root::std::iterator_traits; - pub type reverse_iterator_iterator_type<_Iterator> = _Iterator; + pub type reverse_iterator_iterator_type<_Iter> = _Iter; pub type reverse_iterator_difference_type = - root::std::reverse_iterator___traits_type; - pub type reverse_iterator_pointer = - root::std::reverse_iterator___traits_type; - pub type reverse_iterator_reference = - root::std::reverse_iterator___traits_type; + root::std::iterator_traits; + pub type reverse_iterator_reference = root::std::iterator_traits; + pub type reverse_iterator_pointer = root::std::iterator_traits; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct atomic { } - #[test] - fn __bindgen_test_layout_atomic_instantiation_89754() { - assert_eq!(::std::mem::size_of::<u32>() , 4usize , concat ! ( - "Size of template specialization: " , stringify ! ( u32 - ) )); - assert_eq!(::std::mem::align_of::<u32>() , 4usize , concat ! ( - "Alignment of template specialization: " , stringify ! - ( u32 ) )); - } - #[test] - fn __bindgen_test_layout_atomic_instantiation_89762() { - assert_eq!(::std::mem::size_of::<u64>() , 8usize , concat ! ( - "Size of template specialization: " , stringify ! ( u64 - ) )); - assert_eq!(::std::mem::align_of::<u64>() , 8usize , concat ! ( - "Alignment of template specialization: " , stringify ! - ( u64 ) )); - } - pub mod chrono { - #[allow(unused_imports)] - use self::super::super::super::root; + pub type atomic___base = u8; + #[repr(C)] + pub struct __bit_const_reference { + pub __seg_: root::std::__bit_const_reference___storage_pointer, + pub __mask_: root::std::__bit_const_reference___storage_type, } + pub type __bit_const_reference___storage_type = [u8; 0usize]; + pub type __bit_const_reference___storage_pointer = [u8; 0usize]; } - pub mod __gnu_cxx { - #[allow(unused_imports)] - use self::super::super::root; - } - pub type __off_t = ::std::os::raw::c_long; - pub type __off64_t = ::std::os::raw::c_long; + pub type __int64_t = ::std::os::raw::c_longlong; + pub type __darwin_off_t = root::__int64_t; pub mod mozilla { #[allow(unused_imports)] use self::super::super::root; @@ -1544,7 +1580,7 @@ pub mod root { #[repr(C)] #[derive(Debug)] pub struct MutexImpl { - pub platformData_: [*mut ::std::os::raw::c_void; 5usize], + pub platformData_: [*mut ::std::os::raw::c_void; 8usize], } #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -1553,7 +1589,7 @@ pub mod root { } #[test] fn bindgen_test_layout_MutexImpl() { - assert_eq!(::std::mem::size_of::<MutexImpl>() , 40usize , + assert_eq!(::std::mem::size_of::<MutexImpl>() , 64usize , concat ! ( "Size of: " , stringify ! ( MutexImpl ) )); assert_eq! (::std::mem::align_of::<MutexImpl>() , 8usize , @@ -2184,7 +2220,7 @@ pub mod root { } } #[repr(C)] - #[derive(Debug)] + #[derive(Debug, Copy)] pub struct ThreadSafeAutoRefCnt { pub mValue: u64, } @@ -2205,6 +2241,9 @@ pub mod root { ThreadSafeAutoRefCnt ) , "::" , stringify ! ( mValue ) )); } + impl Clone for ThreadSafeAutoRefCnt { + fn clone(&self) -> Self { *self } + } #[repr(C)] #[derive(Debug)] pub struct OwningNonNull<T> { @@ -3758,20 +3797,9 @@ pub mod root { _unused: [u8; 0], } #[repr(C)] - #[derive(Debug)] + #[derive(Debug, Copy, Clone)] pub struct EventHandlerNonNull { - pub _base: root::mozilla::dom::CallbackFunction, - } - #[test] - fn bindgen_test_layout_EventHandlerNonNull() { - assert_eq!(::std::mem::size_of::<EventHandlerNonNull>() , - 56usize , concat ! ( - "Size of: " , stringify ! ( EventHandlerNonNull ) - )); - assert_eq! (::std::mem::align_of::<EventHandlerNonNull>() , - 8usize , concat ! ( - "Alignment of " , stringify ! ( - EventHandlerNonNull ) )); + _unused: [u8; 0], } #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -6900,7 +6928,7 @@ pub mod root { _unused: [u8; 0], } #[test] - fn __bindgen_test_layout_StaticRefPtr_instantiation_141650() { + fn __bindgen_test_layout_StaticRefPtr_instantiation_118261() { assert_eq!(::std::mem::size_of::<root::mozilla::StaticRefPtr<root::mozilla::URLExtraData>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -7996,7 +8024,7 @@ pub mod root { } #[test] fn bindgen_test_layout_OffTheBooksMutex() { - assert_eq!(::std::mem::size_of::<OffTheBooksMutex>() , 72usize , + assert_eq!(::std::mem::size_of::<OffTheBooksMutex>() , 96usize , concat ! ( "Size of: " , stringify ! ( OffTheBooksMutex ) )); assert_eq! (::std::mem::align_of::<OffTheBooksMutex>() , 8usize , @@ -8004,7 +8032,7 @@ pub mod root { "Alignment of " , stringify ! ( OffTheBooksMutex ) )); assert_eq! (unsafe { & ( * ( 0 as * const OffTheBooksMutex ) ) . - mOwningThread as * const _ as usize } , 64usize , + mOwningThread as * const _ as usize } , 88usize , concat ! ( "Alignment of field: " , stringify ! ( OffTheBooksMutex ) , "::" , stringify ! ( @@ -8022,7 +8050,7 @@ pub mod root { } #[test] fn bindgen_test_layout_Mutex() { - assert_eq!(::std::mem::size_of::<Mutex>() , 72usize , concat ! ( + assert_eq!(::std::mem::size_of::<Mutex>() , 96usize , concat ! ( "Size of: " , stringify ! ( Mutex ) )); assert_eq! (::std::mem::align_of::<Mutex>() , 8usize , concat ! ( "Alignment of " , stringify ! ( Mutex ) )); @@ -8832,9 +8860,9 @@ pub mod root { #[repr(C)] #[derive(Debug)] pub struct ServoElementSnapshot { - pub mContains: root::mozilla::ServoElementSnapshot_Flags, pub mAttrs: root::nsTArray<root::mozilla::ServoAttrSnapshot>, pub mState: root::mozilla::ServoElementSnapshot_ServoStateType, + pub mContains: root::mozilla::ServoElementSnapshot_Flags, pub mIsHTMLElementInHTMLDocument: bool, pub mIsInChromeDocument: bool, } @@ -8847,7 +8875,7 @@ pub mod root { as ServoElementSnapshot_Flags; #[test] fn bindgen_test_layout_ServoElementSnapshot() { - assert_eq!(::std::mem::size_of::<ServoElementSnapshot>() , 32usize + assert_eq!(::std::mem::size_of::<ServoElementSnapshot>() , 24usize , concat ! ( "Size of: " , stringify ! ( ServoElementSnapshot ) )); assert_eq! (::std::mem::align_of::<ServoElementSnapshot>() , @@ -8855,34 +8883,34 @@ pub mod root { "Alignment of " , stringify ! ( ServoElementSnapshot ) )); assert_eq! (unsafe { - & ( * ( 0 as * const ServoElementSnapshot ) ) . - mContains as * const _ as usize } , 0usize , concat ! - ( - "Alignment of field: " , stringify ! ( - ServoElementSnapshot ) , "::" , stringify ! ( - mContains ) )); - assert_eq! (unsafe { & ( * ( 0 as * const ServoElementSnapshot ) ) . mAttrs - as * const _ as usize } , 8usize , concat ! ( + as * const _ as usize } , 0usize , concat ! ( "Alignment of field: " , stringify ! ( ServoElementSnapshot ) , "::" , stringify ! ( mAttrs ) )); assert_eq! (unsafe { & ( * ( 0 as * const ServoElementSnapshot ) ) . mState - as * const _ as usize } , 16usize , concat ! ( + as * const _ as usize } , 8usize , concat ! ( "Alignment of field: " , stringify ! ( ServoElementSnapshot ) , "::" , stringify ! ( mState ) )); assert_eq! (unsafe { & ( * ( 0 as * const ServoElementSnapshot ) ) . + mContains as * const _ as usize } , 16usize , concat ! + ( + "Alignment of field: " , stringify ! ( + ServoElementSnapshot ) , "::" , stringify ! ( + mContains ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const ServoElementSnapshot ) ) . mIsHTMLElementInHTMLDocument as * const _ as usize } , - 24usize , concat ! ( + 17usize , concat ! ( "Alignment of field: " , stringify ! ( ServoElementSnapshot ) , "::" , stringify ! ( mIsHTMLElementInHTMLDocument ) )); assert_eq! (unsafe { & ( * ( 0 as * const ServoElementSnapshot ) ) . - mIsInChromeDocument as * const _ as usize } , 25usize + mIsInChromeDocument as * const _ as usize } , 18usize , concat ! ( "Alignment of field: " , stringify ! ( ServoElementSnapshot ) , "::" , stringify ! ( @@ -9141,7 +9169,7 @@ pub mod root { ( mValue ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_178959() { + fn __bindgen_test_layout_DefaultDelete_instantiation_155741() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -9597,7 +9625,8 @@ pub mod root { pub enum ServoElementSnapshotFlags { State = 1, Attributes = 2, - All = 3, + Id = 4, + MaybeClass = 8, } #[repr(C)] #[derive(Debug, Copy)] @@ -10258,196 +10287,194 @@ pub mod root { pub _phantom_0: ::std::marker::PhantomData<::std::cell::UnsafeCell<T>>, } } + pub type va_list = root::__builtin_va_list; + pub type fpos_t = root::__darwin_off_t; #[repr(C)] #[derive(Debug, Copy)] - pub struct _IO_FILE { - pub _flags: ::std::os::raw::c_int, - pub _IO_read_ptr: *mut ::std::os::raw::c_char, - pub _IO_read_end: *mut ::std::os::raw::c_char, - pub _IO_read_base: *mut ::std::os::raw::c_char, - pub _IO_write_base: *mut ::std::os::raw::c_char, - pub _IO_write_ptr: *mut ::std::os::raw::c_char, - pub _IO_write_end: *mut ::std::os::raw::c_char, - pub _IO_buf_base: *mut ::std::os::raw::c_char, - pub _IO_buf_end: *mut ::std::os::raw::c_char, - pub _IO_save_base: *mut ::std::os::raw::c_char, - pub _IO_backup_base: *mut ::std::os::raw::c_char, - pub _IO_save_end: *mut ::std::os::raw::c_char, - pub _markers: *mut root::_IO_marker, - pub _chain: *mut root::_IO_FILE, - pub _fileno: ::std::os::raw::c_int, - pub _flags2: ::std::os::raw::c_int, - pub _old_offset: root::__off_t, - pub _cur_column: ::std::os::raw::c_ushort, - pub _vtable_offset: ::std::os::raw::c_schar, - pub _shortbuf: [::std::os::raw::c_char; 1usize], - pub _lock: *mut root::_IO_lock_t, - pub _offset: root::__off64_t, - pub __pad1: *mut ::std::os::raw::c_void, - pub __pad2: *mut ::std::os::raw::c_void, - pub __pad3: *mut ::std::os::raw::c_void, - pub __pad4: *mut ::std::os::raw::c_void, - pub __pad5: usize, - pub _mode: ::std::os::raw::c_int, - pub _unused2: [::std::os::raw::c_char; 20usize], - } - #[test] - fn bindgen_test_layout__IO_FILE() { - assert_eq!(::std::mem::size_of::<_IO_FILE>() , 216usize , concat ! ( - "Size of: " , stringify ! ( _IO_FILE ) )); - assert_eq! (::std::mem::align_of::<_IO_FILE>() , 8usize , concat ! ( - "Alignment of " , stringify ! ( _IO_FILE ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _flags as * const _ as + pub struct __sbuf { + pub _base: *mut ::std::os::raw::c_uchar, + pub _size: ::std::os::raw::c_int, + } + #[test] + fn bindgen_test_layout___sbuf() { + assert_eq!(::std::mem::size_of::<__sbuf>() , 16usize , concat ! ( + "Size of: " , stringify ! ( __sbuf ) )); + assert_eq! (::std::mem::align_of::<__sbuf>() , 8usize , concat ! ( + "Alignment of " , stringify ! ( __sbuf ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const __sbuf ) ) . _base as * const _ as usize } , 0usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _flags ) )); + "Alignment of field: " , stringify ! ( __sbuf ) , "::" , + stringify ! ( _base ) )); assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _IO_read_ptr as * - const _ as usize } , 8usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _IO_read_ptr ) )); + & ( * ( 0 as * const __sbuf ) ) . _size as * const _ as + usize } , 8usize , concat ! ( + "Alignment of field: " , stringify ! ( __sbuf ) , "::" , + stringify ! ( _size ) )); + } + impl Clone for __sbuf { + fn clone(&self) -> Self { *self } + } + #[repr(C)] + #[derive(Debug, Copy, Clone)] + pub struct __sFILEX { + _unused: [u8; 0], + } + #[repr(C)] + #[derive(Debug, Copy)] + pub struct __sFILE { + pub _p: *mut ::std::os::raw::c_uchar, + pub _r: ::std::os::raw::c_int, + pub _w: ::std::os::raw::c_int, + pub _flags: ::std::os::raw::c_short, + pub _file: ::std::os::raw::c_short, + pub _bf: root::__sbuf, + pub _lbfsize: ::std::os::raw::c_int, + pub _cookie: *mut ::std::os::raw::c_void, + pub _close: ::std::option::Option<unsafe extern "C" fn(arg1: + *mut ::std::os::raw::c_void) + -> ::std::os::raw::c_int>, + pub _read: ::std::option::Option<unsafe extern "C" fn(arg1: + *mut ::std::os::raw::c_void, + arg2: + *mut ::std::os::raw::c_char, + arg3: + ::std::os::raw::c_int) + -> ::std::os::raw::c_int>, + pub _seek: ::std::option::Option<unsafe extern "C" fn(arg1: + *mut ::std::os::raw::c_void, + arg2: + root::fpos_t, + arg3: + ::std::os::raw::c_int) + -> root::fpos_t>, + pub _write: ::std::option::Option<unsafe extern "C" fn(arg1: + *mut ::std::os::raw::c_void, + arg2: + *const ::std::os::raw::c_char, + arg3: + ::std::os::raw::c_int) + -> ::std::os::raw::c_int>, + pub _ub: root::__sbuf, + pub _extra: *mut root::__sFILEX, + pub _ur: ::std::os::raw::c_int, + pub _ubuf: [::std::os::raw::c_uchar; 3usize], + pub _nbuf: [::std::os::raw::c_uchar; 1usize], + pub _lb: root::__sbuf, + pub _blksize: ::std::os::raw::c_int, + pub _offset: root::fpos_t, + } + #[test] + fn bindgen_test_layout___sFILE() { + assert_eq!(::std::mem::size_of::<__sFILE>() , 152usize , concat ! ( + "Size of: " , stringify ! ( __sFILE ) )); + assert_eq! (::std::mem::align_of::<__sFILE>() , 8usize , concat ! ( + "Alignment of " , stringify ! ( __sFILE ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const __sFILE ) ) . _p as * const _ as + usize } , 0usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _p ) )); assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _IO_read_end as * - const _ as usize } , 16usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _IO_read_end ) )); + & ( * ( 0 as * const __sFILE ) ) . _r as * const _ as + usize } , 8usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _r ) )); assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _IO_read_base as * - const _ as usize } , 24usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _IO_read_base ) )); + & ( * ( 0 as * const __sFILE ) ) . _w as * const _ as + usize } , 12usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _w ) )); assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _IO_write_base as * - const _ as usize } , 32usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _IO_write_base ) )); + & ( * ( 0 as * const __sFILE ) ) . _flags as * const _ as + usize } , 16usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _flags ) )); assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _IO_write_ptr as * - const _ as usize } , 40usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _IO_write_ptr ) )); + & ( * ( 0 as * const __sFILE ) ) . _file as * const _ as + usize } , 18usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _file ) )); assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _IO_write_end as * - const _ as usize } , 48usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _IO_write_end ) )); + & ( * ( 0 as * const __sFILE ) ) . _bf as * const _ as + usize } , 24usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _bf ) )); assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _IO_buf_base as * - const _ as usize } , 56usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _IO_buf_base ) )); + & ( * ( 0 as * const __sFILE ) ) . _lbfsize as * const _ + as usize } , 40usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _lbfsize ) )); assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _IO_buf_end as * const - _ as usize } , 64usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _IO_buf_end ) )); + & ( * ( 0 as * const __sFILE ) ) . _cookie as * const _ as + usize } , 48usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _cookie ) )); assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _IO_save_base as * - const _ as usize } , 72usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _IO_save_base ) )); + & ( * ( 0 as * const __sFILE ) ) . _close as * const _ as + usize } , 56usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _close ) )); assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _IO_backup_base as * - const _ as usize } , 80usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _IO_backup_base ) )); + & ( * ( 0 as * const __sFILE ) ) . _read as * const _ as + usize } , 64usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _read ) )); assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _IO_save_end as * - const _ as usize } , 88usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _IO_save_end ) )); + & ( * ( 0 as * const __sFILE ) ) . _seek as * const _ as + usize } , 72usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _seek ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const __sFILE ) ) . _write as * const _ as + usize } , 80usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _write ) )); assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _markers as * const _ - as usize } , 96usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _markers ) )); + & ( * ( 0 as * const __sFILE ) ) . _ub as * const _ as + usize } , 88usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _ub ) )); assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _chain as * const _ as + & ( * ( 0 as * const __sFILE ) ) . _extra as * const _ as usize } , 104usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _chain ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _fileno as * const _ - as usize } , 112usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _fileno ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _flags2 as * const _ - as usize } , 116usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _flags2 ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _old_offset as * const - _ as usize } , 120usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _old_offset ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _cur_column as * const - _ as usize } , 128usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _cur_column ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _vtable_offset as * - const _ as usize } , 130usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _vtable_offset ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _shortbuf as * const _ - as usize } , 131usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _shortbuf ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _lock as * const _ as - usize } , 136usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _lock ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _offset as * const _ - as usize } , 144usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _extra ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const __sFILE ) ) . _ur as * const _ as + usize } , 112usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _ur ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const __sFILE ) ) . _ubuf as * const _ as + usize } , 116usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _ubuf ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const __sFILE ) ) . _nbuf as * const _ as + usize } , 119usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _nbuf ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const __sFILE ) ) . _lb as * const _ as + usize } , 120usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _lb ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const __sFILE ) ) . _blksize as * const _ + as usize } , 136usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , + stringify ! ( _blksize ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const __sFILE ) ) . _offset as * const _ as + usize } , 144usize , concat ! ( + "Alignment of field: " , stringify ! ( __sFILE ) , "::" , stringify ! ( _offset ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . __pad1 as * const _ as - usize } , 152usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( __pad1 ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . __pad2 as * const _ as - usize } , 160usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( __pad2 ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . __pad3 as * const _ as - usize } , 168usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( __pad3 ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . __pad4 as * const _ as - usize } , 176usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( __pad4 ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . __pad5 as * const _ as - usize } , 184usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( __pad5 ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _mode as * const _ as - usize } , 192usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _mode ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_FILE ) ) . _unused2 as * const _ - as usize } , 196usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_FILE ) , "::" , - stringify ! ( _unused2 ) )); - } - impl Clone for _IO_FILE { + } + impl Clone for __sFILE { fn clone(&self) -> Self { *self } } - pub type FILE = root::_IO_FILE; - pub type va_list = root::__builtin_va_list; + pub type FILE = root::__sFILE; #[repr(C)] #[derive(Debug, Copy)] pub struct InfallibleAllocPolicy { @@ -11004,39 +11031,6 @@ pub mod root { NS_OK_NO_NAME_CLAUSE_HANDLED = 7864354, } pub type nsrefcnt = root::MozRefCountType; - pub type _IO_lock_t = ::std::os::raw::c_void; - #[repr(C)] - #[derive(Debug, Copy)] - pub struct _IO_marker { - pub _next: *mut root::_IO_marker, - pub _sbuf: *mut root::_IO_FILE, - pub _pos: ::std::os::raw::c_int, - } - #[test] - fn bindgen_test_layout__IO_marker() { - assert_eq!(::std::mem::size_of::<_IO_marker>() , 24usize , concat ! ( - "Size of: " , stringify ! ( _IO_marker ) )); - assert_eq! (::std::mem::align_of::<_IO_marker>() , 8usize , concat ! ( - "Alignment of " , stringify ! ( _IO_marker ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_marker ) ) . _next as * const _ - as usize } , 0usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_marker ) , "::" - , stringify ! ( _next ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_marker ) ) . _sbuf as * const _ - as usize } , 8usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_marker ) , "::" - , stringify ! ( _sbuf ) )); - assert_eq! (unsafe { - & ( * ( 0 as * const _IO_marker ) ) . _pos as * const _ as - usize } , 16usize , concat ! ( - "Alignment of field: " , stringify ! ( _IO_marker ) , "::" - , stringify ! ( _pos ) )); - } - impl Clone for _IO_marker { - fn clone(&self) -> Self { *self } - } #[repr(C)] pub struct nsQueryFrame__bindgen_vtable(::std::os::raw::c_void); #[repr(C)] @@ -11596,7 +11590,7 @@ pub mod root { pub _address: u8, } #[test] - fn __bindgen_test_layout_nsCharTraits_instantiation_55138() { + fn __bindgen_test_layout_nsCharTraits_instantiation_52974() { assert_eq!(::std::mem::size_of::<root::nsCharTraits>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -11607,7 +11601,7 @@ pub mod root { root::nsCharTraits ) )); } #[test] - fn __bindgen_test_layout_nsCharTraits_instantiation_55142() { + fn __bindgen_test_layout_nsCharTraits_instantiation_52978() { assert_eq!(::std::mem::size_of::<root::nsCharTraits>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -13080,7 +13074,7 @@ pub mod root { } pub type nsCOMPtr_element_type<T> = T; #[test] - fn __bindgen_test_layout_nsCOMPtr_instantiation_92969() { + fn __bindgen_test_layout_nsCOMPtr_instantiation_62108() { assert_eq!(::std::mem::size_of::<root::nsCOMPtr<root::nsISupports>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -14679,7 +14673,7 @@ pub mod root { * count is 1. */ #[repr(C)] - #[derive(Debug)] + #[derive(Debug, Copy)] pub struct nsStringBuffer { pub mRefCount: u32, pub mStorageSize: u32, @@ -14701,6 +14695,9 @@ pub mod root { "Alignment of field: " , stringify ! ( nsStringBuffer ) , "::" , stringify ! ( mStorageSize ) )); } + impl Clone for nsStringBuffer { + fn clone(&self) -> Self { *self } + } #[repr(C)] #[derive(Debug, Copy)] pub struct nsIAtom { @@ -20789,57 +20786,57 @@ pub mod root { pub struct nsDOMMutationObserver { _unused: [u8; 0], } - pub const NODE_HAS_LISTENERMANAGER: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_HAS_LISTENERMANAGER; - pub const NODE_HAS_PROPERTIES: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_HAS_PROPERTIES; - pub const NODE_IS_ANONYMOUS_ROOT: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_IS_ANONYMOUS_ROOT; - pub const NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE; - pub const NODE_IS_NATIVE_ANONYMOUS_ROOT: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_IS_NATIVE_ANONYMOUS_ROOT; - pub const NODE_FORCE_XBL_BINDINGS: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_FORCE_XBL_BINDINGS; - pub const NODE_MAY_BE_IN_BINDING_MNGR: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_MAY_BE_IN_BINDING_MNGR; - pub const NODE_IS_EDITABLE: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_IS_EDITABLE; - pub const NODE_IS_NATIVE_ANONYMOUS: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_IS_NATIVE_ANONYMOUS; - pub const NODE_IS_IN_SHADOW_TREE: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_IS_IN_SHADOW_TREE; - pub const NODE_HAS_EMPTY_SELECTOR: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_HAS_EMPTY_SELECTOR; - pub const NODE_HAS_SLOW_SELECTOR: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_HAS_SLOW_SELECTOR; - pub const NODE_HAS_EDGE_CHILD_SELECTOR: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_HAS_EDGE_CHILD_SELECTOR; - pub const NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS; - pub const NODE_ALL_SELECTOR_FLAGS: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_ALL_SELECTOR_FLAGS; - pub const NODE_NEEDS_FRAME: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_NEEDS_FRAME; - pub const NODE_DESCENDANTS_NEED_FRAMES: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_DESCENDANTS_NEED_FRAMES; - pub const NODE_HAS_ACCESSKEY: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_HAS_ACCESSKEY; - pub const NODE_HAS_DIRECTION_RTL: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_HAS_DIRECTION_RTL; - pub const NODE_HAS_DIRECTION_LTR: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_HAS_DIRECTION_LTR; - pub const NODE_ALL_DIRECTION_FLAGS: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_ALL_DIRECTION_FLAGS; - pub const NODE_CHROME_ONLY_ACCESS: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_CHROME_ONLY_ACCESS; - pub const NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS; - pub const NODE_TYPE_SPECIFIC_BITS_OFFSET: root::_bindgen_ty_84 = - _bindgen_ty_84::NODE_TYPE_SPECIFIC_BITS_OFFSET; + pub const NODE_HAS_LISTENERMANAGER: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_LISTENERMANAGER; + pub const NODE_HAS_PROPERTIES: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_PROPERTIES; + pub const NODE_IS_ANONYMOUS_ROOT: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_IS_ANONYMOUS_ROOT; + pub const NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE; + pub const NODE_IS_NATIVE_ANONYMOUS_ROOT: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_IS_NATIVE_ANONYMOUS_ROOT; + pub const NODE_FORCE_XBL_BINDINGS: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_FORCE_XBL_BINDINGS; + pub const NODE_MAY_BE_IN_BINDING_MNGR: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_MAY_BE_IN_BINDING_MNGR; + pub const NODE_IS_EDITABLE: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_IS_EDITABLE; + pub const NODE_IS_NATIVE_ANONYMOUS: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_IS_NATIVE_ANONYMOUS; + pub const NODE_IS_IN_SHADOW_TREE: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_IS_IN_SHADOW_TREE; + pub const NODE_HAS_EMPTY_SELECTOR: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_EMPTY_SELECTOR; + pub const NODE_HAS_SLOW_SELECTOR: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_SLOW_SELECTOR; + pub const NODE_HAS_EDGE_CHILD_SELECTOR: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_EDGE_CHILD_SELECTOR; + pub const NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS; + pub const NODE_ALL_SELECTOR_FLAGS: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_ALL_SELECTOR_FLAGS; + pub const NODE_NEEDS_FRAME: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_NEEDS_FRAME; + pub const NODE_DESCENDANTS_NEED_FRAMES: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_DESCENDANTS_NEED_FRAMES; + pub const NODE_HAS_ACCESSKEY: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_ACCESSKEY; + pub const NODE_HAS_DIRECTION_RTL: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_DIRECTION_RTL; + pub const NODE_HAS_DIRECTION_LTR: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_DIRECTION_LTR; + pub const NODE_ALL_DIRECTION_FLAGS: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_ALL_DIRECTION_FLAGS; + pub const NODE_CHROME_ONLY_ACCESS: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_CHROME_ONLY_ACCESS; + pub const NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS; + pub const NODE_TYPE_SPECIFIC_BITS_OFFSET: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_TYPE_SPECIFIC_BITS_OFFSET; #[repr(u32)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] - pub enum _bindgen_ty_84 { + pub enum _bindgen_ty_17 { NODE_HAS_LISTENERMANAGER = 4, NODE_HAS_PROPERTIES = 8, NODE_IS_ANONYMOUS_ROOT = 16, @@ -27266,7 +27263,7 @@ pub mod root { pub type imgRequest_HasThreadSafeRefCnt = root::mozilla::TrueType; #[test] fn bindgen_test_layout_imgRequest() { - assert_eq!(::std::mem::size_of::<imgRequest>() , 416usize , concat ! ( + assert_eq!(::std::mem::size_of::<imgRequest>() , 440usize , concat ! ( "Size of: " , stringify ! ( imgRequest ) )); assert_eq! (::std::mem::align_of::<imgRequest>() , 8usize , concat ! ( "Alignment of " , stringify ! ( imgRequest ) )); @@ -28682,7 +28679,7 @@ pub mod root { ) , "::" , stringify ! ( mQuotePairs ) )); } #[test] - fn __bindgen_test_layout_StaticRefPtr_instantiation_174951() { + fn __bindgen_test_layout_StaticRefPtr_instantiation_151733() { assert_eq!(::std::mem::size_of::<root::mozilla::StaticRefPtr<root::nsStyleQuoteValues>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31150,48 +31147,48 @@ pub mod root { pub struct nsAttrValueOrString { _unused: [u8; 0], } - pub const ELEMENT_SHARED_RESTYLE_BIT_1: root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_SHARED_RESTYLE_BIT_1; - pub const ELEMENT_SHARED_RESTYLE_BIT_2: root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_SHARED_RESTYLE_BIT_2; - pub const ELEMENT_SHARED_RESTYLE_BIT_3: root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_SHARED_RESTYLE_BIT_3; - pub const ELEMENT_SHARED_RESTYLE_BIT_4: root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_SHARED_RESTYLE_BIT_4; - pub const ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO: root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_SHARED_RESTYLE_BIT_1; + pub const ELEMENT_SHARED_RESTYLE_BIT_1: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_1; + pub const ELEMENT_SHARED_RESTYLE_BIT_2: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_2; + pub const ELEMENT_SHARED_RESTYLE_BIT_3: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_3; + pub const ELEMENT_SHARED_RESTYLE_BIT_4: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_4; + pub const ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_1; pub const ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO: - root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_SHARED_RESTYLE_BIT_2; - pub const ELEMENT_HAS_SNAPSHOT: root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_SHARED_RESTYLE_BIT_3; - pub const ELEMENT_HANDLED_SNAPSHOT: root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_SHARED_RESTYLE_BIT_4; - pub const ELEMENT_HAS_PENDING_RESTYLE: root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_SHARED_RESTYLE_BIT_1; - pub const ELEMENT_IS_POTENTIAL_RESTYLE_ROOT: root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_SHARED_RESTYLE_BIT_2; - pub const ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE: root::_bindgen_ty_86 + root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_2; + pub const ELEMENT_HAS_SNAPSHOT: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_3; + pub const ELEMENT_HANDLED_SNAPSHOT: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_4; + pub const ELEMENT_HAS_PENDING_RESTYLE: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_1; + pub const ELEMENT_IS_POTENTIAL_RESTYLE_ROOT: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_2; + pub const ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE: root::_bindgen_ty_19 = - _bindgen_ty_86::ELEMENT_SHARED_RESTYLE_BIT_3; + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_3; pub const ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT: - root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_SHARED_RESTYLE_BIT_4; - pub const ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR: root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR; - pub const ELEMENT_PENDING_RESTYLE_FLAGS: root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_PENDING_RESTYLE_FLAGS; - pub const ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS: root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS; - pub const ELEMENT_ALL_RESTYLE_FLAGS: root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_ALL_RESTYLE_FLAGS; - pub const ELEMENT_HAS_SCROLLGRAB: root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_HAS_SCROLLGRAB; - pub const ELEMENT_TYPE_SPECIFIC_BITS_OFFSET: root::_bindgen_ty_86 = - _bindgen_ty_86::ELEMENT_TYPE_SPECIFIC_BITS_OFFSET; + root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_4; + pub const ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR; + pub const ELEMENT_PENDING_RESTYLE_FLAGS: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_PENDING_RESTYLE_FLAGS; + pub const ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS; + pub const ELEMENT_ALL_RESTYLE_FLAGS: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_ALL_RESTYLE_FLAGS; + pub const ELEMENT_HAS_SCROLLGRAB: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_HAS_SCROLLGRAB; + pub const ELEMENT_TYPE_SPECIFIC_BITS_OFFSET: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_TYPE_SPECIFIC_BITS_OFFSET; #[repr(u32)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] - pub enum _bindgen_ty_86 { + pub enum _bindgen_ty_19 { ELEMENT_SHARED_RESTYLE_BIT_1 = 8388608, ELEMENT_SHARED_RESTYLE_BIT_2 = 16777216, ELEMENT_SHARED_RESTYLE_BIT_3 = 33554432, @@ -31960,7 +31957,7 @@ pub mod root { } pub type __builtin_va_list = [root::__va_list_tag; 1usize]; #[test] - fn __bindgen_test_layout_IntegralConstant_instantiation_202803() { + fn __bindgen_test_layout_IntegralConstant_instantiation_173405() { assert_eq!(::std::mem::size_of::<u8>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( u8 ) )); @@ -31969,7 +31966,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_IntegralConstant_instantiation_202807() { + fn __bindgen_test_layout_IntegralConstant_instantiation_173409() { assert_eq!(::std::mem::size_of::<u8>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( u8 ) )); @@ -31978,7 +31975,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_nsReadingIterator_instantiation_203634() { + fn __bindgen_test_layout_nsReadingIterator_instantiation_173626() { assert_eq!(::std::mem::size_of::<root::nsReadingIterator<u16>>() , 24usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31989,7 +31986,7 @@ pub mod root { root::nsReadingIterator<u16> ) )); } #[test] - fn __bindgen_test_layout_nsWritingIterator_instantiation_203638() { + fn __bindgen_test_layout_nsWritingIterator_instantiation_173630() { assert_eq!(::std::mem::size_of::<root::nsWritingIterator<u16>>() , 24usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32000,7 +31997,7 @@ pub mod root { root::nsWritingIterator<u16> ) )); } #[test] - fn __bindgen_test_layout_nsReadingIterator_instantiation_203711() { + fn __bindgen_test_layout_nsReadingIterator_instantiation_173703() { assert_eq!(::std::mem::size_of::<root::nsReadingIterator<::std::os::raw::c_char>>() , 24usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32011,7 +32008,7 @@ pub mod root { root::nsReadingIterator<::std::os::raw::c_char> ) )); } #[test] - fn __bindgen_test_layout_nsWritingIterator_instantiation_203715() { + fn __bindgen_test_layout_nsWritingIterator_instantiation_173707() { assert_eq!(::std::mem::size_of::<root::nsWritingIterator<::std::os::raw::c_char>>() , 24usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32022,7 +32019,25 @@ pub mod root { root::nsWritingIterator<::std::os::raw::c_char> ) )); } #[test] - fn __bindgen_test_layout__bindgen_ty_id_209541_instantiation_209538() { + fn __bindgen_test_layout_atomic_instantiation_175747() { + assert_eq!(::std::mem::size_of::<u32>() , 4usize , concat ! ( + "Size of template specialization: " , stringify ! ( u32 ) + )); + assert_eq!(::std::mem::align_of::<u32>() , 4usize , concat ! ( + "Alignment of template specialization: " , stringify ! ( + u32 ) )); + } + #[test] + fn __bindgen_test_layout_atomic_instantiation_175755() { + assert_eq!(::std::mem::size_of::<u64>() , 8usize , concat ! ( + "Size of template specialization: " , stringify ! ( u64 ) + )); + assert_eq!(::std::mem::align_of::<u64>() , 8usize , concat ! ( + "Alignment of template specialization: " , stringify ! ( + u64 ) )); + } + #[test] + fn __bindgen_test_layout__bindgen_ty_id_176012_instantiation_176009() { assert_eq!(::std::mem::size_of::<u8>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( u8 ) )); @@ -32031,7 +32046,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout__bindgen_ty_id_209574_instantiation_209571() { + fn __bindgen_test_layout__bindgen_ty_id_176045_instantiation_176042() { assert_eq!(::std::mem::size_of::<u8>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( u8 ) )); @@ -32040,7 +32055,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_209842() { + fn __bindgen_test_layout_nsTArray_instantiation_176313() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::nsCString>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32051,7 +32066,7 @@ pub mod root { root::nsTArray<root::nsCString> ) )); } #[test] - fn __bindgen_test_layout_Handle_instantiation_210801() { + fn __bindgen_test_layout_Handle_instantiation_177145() { assert_eq!(::std::mem::size_of::<root::JS::Handle<*mut root::JSObject>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32062,7 +32077,7 @@ pub mod root { root::JS::Handle<*mut root::JSObject> ) )); } #[test] - fn __bindgen_test_layout_Handle_instantiation_210817() { + fn __bindgen_test_layout_Handle_instantiation_177161() { assert_eq!(::std::mem::size_of::<root::JS::Handle<root::JS::Value>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32073,7 +32088,7 @@ pub mod root { root::JS::Handle<root::JS::Value> ) )); } #[test] - fn __bindgen_test_layout_MutableHandle_instantiation_210827() { + fn __bindgen_test_layout_MutableHandle_instantiation_177171() { assert_eq!(::std::mem::size_of::<root::JS::MutableHandle<*mut root::JSObject>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32084,7 +32099,7 @@ pub mod root { root::JS::MutableHandle<*mut root::JSObject> ) )); } #[test] - fn __bindgen_test_layout_MutableHandle_instantiation_210843() { + fn __bindgen_test_layout_MutableHandle_instantiation_177187() { assert_eq!(::std::mem::size_of::<root::JS::MutableHandle<root::JS::Value>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32095,7 +32110,7 @@ pub mod root { root::JS::MutableHandle<root::JS::Value> ) )); } #[test] - fn __bindgen_test_layout_Rooted_instantiation_210846() { + fn __bindgen_test_layout_Rooted_instantiation_177190() { assert_eq!(::std::mem::size_of::<[u64; 3usize]>() , 24usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32106,7 +32121,7 @@ pub mod root { [u64; 3usize] ) )); } #[test] - fn __bindgen_test_layout_DeletePolicy_instantiation_211183() { + fn __bindgen_test_layout_DeletePolicy_instantiation_177527() { assert_eq!(::std::mem::size_of::<root::JS::DeletePolicy>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32117,7 +32132,7 @@ pub mod root { root::JS::DeletePolicy ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_213233() { + fn __bindgen_test_layout_nsTArray_instantiation_182503() { assert_eq!(::std::mem::size_of::<root::nsTArray<::nsstring::nsStringRepr>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32128,7 +32143,7 @@ pub mod root { root::nsTArray<::nsstring::nsStringRepr> ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_213237() { + fn __bindgen_test_layout_nsTArray_instantiation_182507() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::mozilla::FontFamilyName>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32139,7 +32154,7 @@ pub mod root { root::nsTArray<root::mozilla::FontFamilyName> ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_213250() { + fn __bindgen_test_layout_nsTArray_instantiation_182520() { assert_eq!(::std::mem::size_of::<root::nsTArray<::std::os::raw::c_uint>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32150,7 +32165,7 @@ pub mod root { root::nsTArray<::std::os::raw::c_uint> ) )); } #[test] - fn __bindgen_test_layout_TenuredHeap_instantiation_214375() { + fn __bindgen_test_layout_TenuredHeap_instantiation_183432() { assert_eq!(::std::mem::size_of::<root::JS::TenuredHeap>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32161,7 +32176,7 @@ pub mod root { root::JS::TenuredHeap ) )); } #[test] - fn __bindgen_test_layout_Heap_instantiation_214465() { + fn __bindgen_test_layout_Heap_instantiation_183522() { assert_eq!(::std::mem::size_of::<root::JS::Heap<*mut root::JSObject>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32172,7 +32187,7 @@ pub mod root { root::JS::Heap<*mut root::JSObject> ) )); } #[test] - fn __bindgen_test_layout_Heap_instantiation_214580() { + fn __bindgen_test_layout_Heap_instantiation_183637() { assert_eq!(::std::mem::size_of::<root::JS::Heap<root::JS::Value>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32183,7 +32198,7 @@ pub mod root { root::JS::Heap<root::JS::Value> ) )); } #[test] - fn __bindgen_test_layout_TErrorResult_instantiation_214587() { + fn __bindgen_test_layout_TErrorResult_instantiation_183644() { assert_eq!(::std::mem::size_of::<root::mozilla::binding_danger::TErrorResult>() , 32usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32194,7 +32209,7 @@ pub mod root { root::mozilla::binding_danger::TErrorResult ) )); } #[test] - fn __bindgen_test_layout_TErrorResult_instantiation_214603() { + fn __bindgen_test_layout_TErrorResult_instantiation_183660() { assert_eq!(::std::mem::size_of::<root::mozilla::binding_danger::TErrorResult>() , 32usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32205,7 +32220,7 @@ pub mod root { root::mozilla::binding_danger::TErrorResult ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_214608() { + fn __bindgen_test_layout_already_AddRefed_instantiation_183666() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsStringBuffer>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32216,7 +32231,7 @@ pub mod root { root::already_AddRefed<root::nsStringBuffer> ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_214660() { + fn __bindgen_test_layout_already_AddRefed_instantiation_183718() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsIAtom>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32227,7 +32242,7 @@ pub mod root { root::already_AddRefed<root::nsIAtom> ) )); } #[test] - fn __bindgen_test_layout_RefPtr_instantiation_215143() { + fn __bindgen_test_layout_RefPtr_instantiation_184201() { assert_eq!(::std::mem::size_of::<root::RefPtr<root::mozilla::StyleSheet>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32238,7 +32253,7 @@ pub mod root { root::RefPtr<root::mozilla::StyleSheet> ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_215489() { + fn __bindgen_test_layout_already_AddRefed_instantiation_184547() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::mozilla::dom::NodeInfo>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32249,7 +32264,7 @@ pub mod root { root::already_AddRefed<root::mozilla::dom::NodeInfo> ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_215734() { + fn __bindgen_test_layout_already_AddRefed_instantiation_184792() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsIURI>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32260,7 +32275,7 @@ pub mod root { root::already_AddRefed<root::nsIURI> ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_215881() { + fn __bindgen_test_layout_already_AddRefed_instantiation_184939() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsINode>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32271,7 +32286,7 @@ pub mod root { root::already_AddRefed<root::nsINode> ) )); } #[test] - fn __bindgen_test_layout_DeletePolicy_instantiation_220000() { + fn __bindgen_test_layout_DeletePolicy_instantiation_189058() { assert_eq!(::std::mem::size_of::<root::JS::DeletePolicy>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32282,7 +32297,7 @@ pub mod root { root::JS::DeletePolicy ) )); } #[test] - fn __bindgen_test_layout_UniquePtr_instantiation_219998() { + fn __bindgen_test_layout_UniquePtr_instantiation_189056() { assert_eq!(::std::mem::size_of::<root::mozilla::UniquePtr<root::JSErrorNotes_Note>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32293,7 +32308,7 @@ pub mod root { root::mozilla::UniquePtr<root::JSErrorNotes_Note> ) )); } #[test] - fn __bindgen_test_layout_iterator_instantiation_220033() { + fn __bindgen_test_layout_iterator_instantiation_189091() { assert_eq!(::std::mem::size_of::<root::std::iterator>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32304,7 +32319,7 @@ pub mod root { root::std::iterator ) )); } #[test] - fn __bindgen_test_layout_nsCOMPtr_instantiation_220601() { + fn __bindgen_test_layout_nsCOMPtr_instantiation_189647() { assert_eq!(::std::mem::size_of::<root::nsCOMPtr<root::nsIPrincipal>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32315,7 +32330,7 @@ pub mod root { root::nsCOMPtr<root::nsIPrincipal> ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_222194() { + fn __bindgen_test_layout_nsTArray_instantiation_191240() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::RefPtr<root::mozilla::dom::AnonymousContent>>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32328,7 +32343,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_LinkedList_instantiation_222470() { + fn __bindgen_test_layout_LinkedList_instantiation_191516() { assert_eq!(::std::mem::size_of::<root::mozilla::LinkedList>() , 24usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32339,7 +32354,7 @@ pub mod root { root::mozilla::LinkedList ) )); } #[test] - fn __bindgen_test_layout_RefPtr_instantiation_222486() { + fn __bindgen_test_layout_RefPtr_instantiation_191532() { assert_eq!(::std::mem::size_of::<root::RefPtr<root::mozilla::dom::Element>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32350,7 +32365,7 @@ pub mod root { root::RefPtr<root::mozilla::dom::Element> ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_222485() { + fn __bindgen_test_layout_nsTArray_instantiation_191531() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::RefPtr<root::mozilla::dom::Element>>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32363,7 +32378,7 @@ pub mod root { )); } #[test] - fn __bindgen_test_layout_nsCOMPtr_instantiation_222515() { + fn __bindgen_test_layout_nsCOMPtr_instantiation_191561() { assert_eq!(::std::mem::size_of::<root::nsCOMPtr<root::nsIObserver>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32374,7 +32389,7 @@ pub mod root { root::nsCOMPtr<root::nsIObserver> ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_222514() { + fn __bindgen_test_layout_nsTArray_instantiation_191560() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::nsCOMPtr<root::nsIObserver>>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32385,7 +32400,7 @@ pub mod root { root::nsTArray<root::nsCOMPtr<root::nsIObserver>> ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_222560() { + fn __bindgen_test_layout_already_AddRefed_instantiation_191606() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsIDocument>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32396,7 +32411,7 @@ pub mod root { root::already_AddRefed<root::nsIDocument> ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_222725() { + fn __bindgen_test_layout_already_AddRefed_instantiation_191771() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsContentList>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32407,7 +32422,7 @@ pub mod root { root::already_AddRefed<root::nsContentList> ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_223052() { + fn __bindgen_test_layout_already_AddRefed_instantiation_192098() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsIRunnable>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32418,7 +32433,7 @@ pub mod root { root::already_AddRefed<root::nsIRunnable> ) )); } #[test] - fn __bindgen_test_layout_nsCOMPtr_instantiation_223145() { + fn __bindgen_test_layout_nsCOMPtr_instantiation_192191() { assert_eq!(::std::mem::size_of::<root::nsCOMPtr<root::mozilla::dom::Link>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32429,7 +32444,7 @@ pub mod root { root::nsCOMPtr<root::mozilla::dom::Link> ) )); } #[test] - fn __bindgen_test_layout_nsCOMPtr_instantiation_223182() { + fn __bindgen_test_layout_nsCOMPtr_instantiation_192228() { assert_eq!(::std::mem::size_of::<root::nsCOMPtr<root::nsIWeakReference>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32440,7 +32455,7 @@ pub mod root { root::nsCOMPtr<root::nsIWeakReference> ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_223440() { + fn __bindgen_test_layout_DefaultDelete_instantiation_192486() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32451,7 +32466,7 @@ pub mod root { root::mozilla::DefaultDelete ) )); } #[test] - fn __bindgen_test_layout_UniquePtr_instantiation_223438() { + fn __bindgen_test_layout_UniquePtr_instantiation_192484() { assert_eq!(::std::mem::size_of::<root::mozilla::UniquePtr<root::nsISMILAttr>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32462,7 +32477,7 @@ pub mod root { root::mozilla::UniquePtr<root::nsISMILAttr> ) )); } #[test] - fn __bindgen_test_layout_nsRefPtrHashKey_instantiation_223988() { + fn __bindgen_test_layout_nsRefPtrHashKey_instantiation_193034() { assert_eq!(::std::mem::size_of::<root::nsRefPtrHashKey<root::mozilla::dom::DOMIntersectionObserver>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32475,7 +32490,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_nsDataHashtable_instantiation_223987() { + fn __bindgen_test_layout_nsDataHashtable_instantiation_193033() { assert_eq!(::std::mem::size_of::<[u64; 6usize]>() , 48usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32486,7 +32501,7 @@ pub mod root { [u64; 6usize] ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_224194() { + fn __bindgen_test_layout_nsTArray_instantiation_193240() { assert_eq!(::std::mem::size_of::<root::nsTArray<*mut root::nsIContent>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32497,7 +32512,7 @@ pub mod root { root::nsTArray<*mut root::nsIContent> ) )); } #[test] - fn __bindgen_test_layout_SupportsWeakPtr_instantiation_224245() { + fn __bindgen_test_layout_SupportsWeakPtr_instantiation_193291() { assert_eq!(::std::mem::size_of::<u64>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( u64 ) )); @@ -32506,7 +32521,7 @@ pub mod root { u64 ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_224425() { + fn __bindgen_test_layout_nsTArray_instantiation_193466() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::nsRect>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32517,7 +32532,7 @@ pub mod root { root::nsTArray<root::nsRect> ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_224541() { + fn __bindgen_test_layout_DefaultDelete_instantiation_193582() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32528,7 +32543,7 @@ pub mod root { root::mozilla::DefaultDelete ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_224710() { + fn __bindgen_test_layout_nsTArray_instantiation_193751() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::nsCOMPtr<root::nsIAtom>>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32539,7 +32554,7 @@ pub mod root { root::nsTArray<root::nsCOMPtr<root::nsIAtom>> ) )); } #[test] - fn __bindgen_test_layout_nsPIDOMWindow_instantiation_225497() { + fn __bindgen_test_layout_nsPIDOMWindow_instantiation_194538() { assert_eq!(::std::mem::size_of::<[u64; 29usize]>() , 232usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32550,7 +32565,7 @@ pub mod root { [u64; 29usize] ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_225589() { + fn __bindgen_test_layout_already_AddRefed_instantiation_194590() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsIContent>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32561,7 +32576,7 @@ pub mod root { root::already_AddRefed<root::nsIContent> ) )); } #[test] - fn __bindgen_test_layout_nsRefPtrHashtable_instantiation_225770() { + fn __bindgen_test_layout_nsRefPtrHashtable_instantiation_194771() { assert_eq!(::std::mem::size_of::<[u64; 6usize]>() , 48usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32572,7 +32587,7 @@ pub mod root { [u64; 6usize] ) )); } #[test] - fn __bindgen_test_layout_nsPtrHashKey_instantiation_226293() { + fn __bindgen_test_layout_nsPtrHashKey_instantiation_195278() { assert_eq!(::std::mem::size_of::<root::nsPtrHashKey<::std::os::raw::c_void>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32583,7 +32598,7 @@ pub mod root { root::nsPtrHashKey<::std::os::raw::c_void> ) )); } #[test] - fn __bindgen_test_layout_nsPtrHashKey_instantiation_226301() { + fn __bindgen_test_layout_nsPtrHashKey_instantiation_195286() { assert_eq!(::std::mem::size_of::<root::nsPtrHashKey<root::WeakFrame>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32594,7 +32609,7 @@ pub mod root { root::nsPtrHashKey<root::WeakFrame> ) )); } #[test] - fn __bindgen_test_layout_OwningNonNull_instantiation_226416() { + fn __bindgen_test_layout_OwningNonNull_instantiation_195401() { assert_eq!(::std::mem::size_of::<root::mozilla::OwningNonNull<root::nsINode>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32605,7 +32620,7 @@ pub mod root { root::mozilla::OwningNonNull<root::nsINode> ) )); } #[test] - fn __bindgen_test_layout_PointTyped_instantiation_227495() { + fn __bindgen_test_layout_PointTyped_instantiation_196480() { assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32616,7 +32631,7 @@ pub mod root { [u32; 2usize] ) )); } #[test] - fn __bindgen_test_layout_IntPointTyped_instantiation_227500() { + fn __bindgen_test_layout_IntPointTyped_instantiation_196485() { assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32627,7 +32642,7 @@ pub mod root { [u32; 2usize] ) )); } #[test] - fn __bindgen_test_layout_SizeTyped_instantiation_227503() { + fn __bindgen_test_layout_SizeTyped_instantiation_196488() { assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32638,7 +32653,7 @@ pub mod root { [u32; 2usize] ) )); } #[test] - fn __bindgen_test_layout_RectTyped_instantiation_227511() { + fn __bindgen_test_layout_RectTyped_instantiation_196496() { assert_eq!(::std::mem::size_of::<[u32; 4usize]>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32649,7 +32664,7 @@ pub mod root { [u32; 4usize] ) )); } #[test] - fn __bindgen_test_layout_IntPointTyped_instantiation_227543() { + fn __bindgen_test_layout_IntPointTyped_instantiation_196528() { assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32660,7 +32675,7 @@ pub mod root { [u32; 2usize] ) )); } #[test] - fn __bindgen_test_layout_IntSizeTyped_instantiation_227551() { + fn __bindgen_test_layout_IntSizeTyped_instantiation_196536() { assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32671,7 +32686,7 @@ pub mod root { [u32; 2usize] ) )); } #[test] - fn __bindgen_test_layout_IntRectTyped_instantiation_227559() { + fn __bindgen_test_layout_IntRectTyped_instantiation_196544() { assert_eq!(::std::mem::size_of::<[u32; 4usize]>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32682,7 +32697,7 @@ pub mod root { [u32; 4usize] ) )); } #[test] - fn __bindgen_test_layout_MarginTyped_instantiation_227726() { + fn __bindgen_test_layout_MarginTyped_instantiation_196711() { assert_eq!(::std::mem::size_of::<[u32; 4usize]>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32693,7 +32708,7 @@ pub mod root { [u32; 4usize] ) )); } #[test] - fn __bindgen_test_layout_RectTyped_instantiation_227761() { + fn __bindgen_test_layout_RectTyped_instantiation_196746() { assert_eq!(::std::mem::size_of::<[u32; 4usize]>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32704,7 +32719,7 @@ pub mod root { [u32; 4usize] ) )); } #[test] - fn __bindgen_test_layout_IntRectTyped_instantiation_227766() { + fn __bindgen_test_layout_IntRectTyped_instantiation_196751() { assert_eq!(::std::mem::size_of::<[u32; 4usize]>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32715,7 +32730,7 @@ pub mod root { [u32; 4usize] ) )); } #[test] - fn __bindgen_test_layout_ScaleFactor_instantiation_227812() { + fn __bindgen_test_layout_ScaleFactor_instantiation_196797() { assert_eq!(::std::mem::size_of::<u32>() , 4usize , concat ! ( "Size of template specialization: " , stringify ! ( u32 ) )); @@ -32724,7 +32739,7 @@ pub mod root { u32 ) )); } #[test] - fn __bindgen_test_layout_ScaleFactors2D_instantiation_227912() { + fn __bindgen_test_layout_ScaleFactors2D_instantiation_196897() { assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32735,7 +32750,7 @@ pub mod root { [u32; 2usize] ) )); } #[test] - fn __bindgen_test_layout_ScaleFactors2D_instantiation_227920() { + fn __bindgen_test_layout_ScaleFactors2D_instantiation_196905() { assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32746,7 +32761,7 @@ pub mod root { [u32; 2usize] ) )); } #[test] - fn __bindgen_test_layout_ScaleFactors2D_instantiation_227964() { + fn __bindgen_test_layout_ScaleFactors2D_instantiation_196949() { assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32757,7 +32772,7 @@ pub mod root { [u32; 2usize] ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_228594() { + fn __bindgen_test_layout_nsTArray_instantiation_197579() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::mozilla::FramePropertyTable_PropertyValue>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32770,7 +32785,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_nsPtrHashKey_instantiation_228610() { + fn __bindgen_test_layout_nsPtrHashKey_instantiation_197595() { assert_eq!(::std::mem::size_of::<root::nsPtrHashKey<root::nsIFrame>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32781,7 +32796,7 @@ pub mod root { root::nsPtrHashKey<root::nsIFrame> ) )); } #[test] - fn __bindgen_test_layout_nsPIDOMWindow_instantiation_231884() { + fn __bindgen_test_layout_nsPIDOMWindow_instantiation_200803() { assert_eq!(::std::mem::size_of::<[u64; 29usize]>() , 232usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32792,7 +32807,7 @@ pub mod root { [u64; 29usize] ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_232520() { + fn __bindgen_test_layout_already_AddRefed_instantiation_201439() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::mozilla::dom::CSSValue>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32803,7 +32818,7 @@ pub mod root { root::already_AddRefed<root::mozilla::dom::CSSValue> ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_232611() { + fn __bindgen_test_layout_DefaultDelete_instantiation_201530() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32814,7 +32829,7 @@ pub mod root { root::mozilla::DefaultDelete ) )); } #[test] - fn __bindgen_test_layout_nsRefPtrHashtable_instantiation_232615() { + fn __bindgen_test_layout_nsRefPtrHashtable_instantiation_201534() { assert_eq!(::std::mem::size_of::<[u64; 6usize]>() , 48usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32825,7 +32840,7 @@ pub mod root { [u64; 6usize] ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_233804() { + fn __bindgen_test_layout_already_AddRefed_instantiation_202723() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsISupports>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32836,7 +32851,7 @@ pub mod root { root::already_AddRefed<root::nsISupports> ) )); } #[test] - fn __bindgen_test_layout_nsCOMPtr_instantiation_234090() { + fn __bindgen_test_layout_nsCOMPtr_instantiation_203310() { assert_eq!(::std::mem::size_of::<root::nsCOMPtr<root::nsIRunnable>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32847,7 +32862,7 @@ pub mod root { root::nsCOMPtr<root::nsIRunnable> ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_235680() { + fn __bindgen_test_layout_nsTArray_instantiation_204853() { assert_eq!(::std::mem::size_of::<root::nsTArray<f64>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32858,7 +32873,7 @@ pub mod root { root::nsTArray<f64> ) )); } #[test] - fn __bindgen_test_layout_RefPtr_instantiation_235692() { + fn __bindgen_test_layout_RefPtr_instantiation_204865() { assert_eq!(::std::mem::size_of::<root::RefPtr<root::mozilla::dom::DOMIntersectionObserverEntry>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32871,7 +32886,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_235691() { + fn __bindgen_test_layout_nsTArray_instantiation_204864() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::RefPtr<root::mozilla::dom::DOMIntersectionObserverEntry>>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32884,7 +32899,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_nsPtrHashKey_instantiation_235725() { + fn __bindgen_test_layout_nsPtrHashKey_instantiation_204898() { assert_eq!(::std::mem::size_of::<root::nsPtrHashKey<root::mozilla::dom::Element>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32895,7 +32910,7 @@ pub mod root { root::nsPtrHashKey<root::mozilla::dom::Element> ) )); } #[test] - fn __bindgen_test_layout_UniquePtr_instantiation_235822() { + fn __bindgen_test_layout_UniquePtr_instantiation_204995() { assert_eq!(::std::mem::size_of::<root::mozilla::UniquePtr<root::ProfilerBacktrace>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32906,7 +32921,7 @@ pub mod root { root::mozilla::UniquePtr<root::ProfilerBacktrace> ) )); } #[test] - fn __bindgen_test_layout_nsDataHashtable_instantiation_237602() { + fn __bindgen_test_layout_nsDataHashtable_instantiation_206793() { assert_eq!(::std::mem::size_of::<[u64; 6usize]>() , 48usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32917,7 +32932,7 @@ pub mod root { [u64; 6usize] ) )); } #[test] - fn __bindgen_test_layout_OwningNonNull_instantiation_237641() { + fn __bindgen_test_layout_OwningNonNull_instantiation_206832() { assert_eq!(::std::mem::size_of::<root::mozilla::OwningNonNull<root::mozilla::EffectCompositor_AnimationStyleRuleProcessor>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32930,7 +32945,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_nsRefPtrHashKey_instantiation_237664() { + fn __bindgen_test_layout_nsRefPtrHashKey_instantiation_206855() { assert_eq!(::std::mem::size_of::<root::nsRefPtrHashKey<root::nsIAtom>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32941,7 +32956,7 @@ pub mod root { root::nsRefPtrHashKey<root::nsIAtom> ) )); } #[test] - fn __bindgen_test_layout_nsRefPtrHashKey_instantiation_237700() { + fn __bindgen_test_layout_nsRefPtrHashKey_instantiation_206891() { assert_eq!(::std::mem::size_of::<root::nsRefPtrHashKey<root::nsIContent>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32952,7 +32967,7 @@ pub mod root { root::nsRefPtrHashKey<root::nsIContent> ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_238245() { + fn __bindgen_test_layout_DefaultDelete_instantiation_207436() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32963,7 +32978,7 @@ pub mod root { root::mozilla::DefaultDelete ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_238259() { + fn __bindgen_test_layout_already_AddRefed_instantiation_207450() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::mozilla::URLExtraData>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32974,7 +32989,7 @@ pub mod root { root::already_AddRefed<root::mozilla::URLExtraData> ) )); } #[test] - fn __bindgen_test_layout_nsMainThreadPtrHolder_instantiation_238263() { + fn __bindgen_test_layout_nsMainThreadPtrHolder_instantiation_207454() { assert_eq!(::std::mem::size_of::<root::nsMainThreadPtrHolder<root::nsIURI>>() , 24usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32985,7 +33000,7 @@ pub mod root { root::nsMainThreadPtrHolder<root::nsIURI> ) )); } #[test] - fn __bindgen_test_layout_nsPtrHashKey_instantiation_238337() { + fn __bindgen_test_layout_nsPtrHashKey_instantiation_207528() { assert_eq!(::std::mem::size_of::<root::nsPtrHashKey<root::nsIDocument>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32996,7 +33011,7 @@ pub mod root { root::nsPtrHashKey<root::nsIDocument> ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_238624() { + fn __bindgen_test_layout_DefaultDelete_instantiation_207815() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33007,7 +33022,7 @@ pub mod root { root::mozilla::DefaultDelete ) )); } #[test] - fn __bindgen_test_layout_UniquePtr_instantiation_238622() { + fn __bindgen_test_layout_UniquePtr_instantiation_207813() { assert_eq!(::std::mem::size_of::<root::mozilla::UniquePtr<root::nsCSSValueList>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33018,7 +33033,7 @@ pub mod root { root::mozilla::UniquePtr<root::nsCSSValueList> ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_238630() { + fn __bindgen_test_layout_DefaultDelete_instantiation_207821() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33029,7 +33044,7 @@ pub mod root { root::mozilla::DefaultDelete ) )); } #[test] - fn __bindgen_test_layout_UniquePtr_instantiation_238628() { + fn __bindgen_test_layout_UniquePtr_instantiation_207819() { assert_eq!(::std::mem::size_of::<root::mozilla::UniquePtr<root::nsCSSValuePairList>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33040,7 +33055,7 @@ pub mod root { root::mozilla::UniquePtr<root::nsCSSValuePairList> ) )); } #[test] - fn __bindgen_test_layout_Maybe_instantiation_238973() { + fn __bindgen_test_layout_Maybe_instantiation_208164() { assert_eq!(::std::mem::size_of::<[u64; 2usize]>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33051,7 +33066,7 @@ pub mod root { [u64; 2usize] ) )); } #[test] - fn __bindgen_test_layout_SupportsWeakPtr_instantiation_239140() { + fn __bindgen_test_layout_SupportsWeakPtr_instantiation_208331() { assert_eq!(::std::mem::size_of::<u64>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( u64 ) )); @@ -33060,7 +33075,7 @@ pub mod root { u64 ) )); } #[test] - fn __bindgen_test_layout_Maybe_instantiation_239301() { + fn __bindgen_test_layout_Maybe_instantiation_208492() { assert_eq!(::std::mem::size_of::<[u32; 3usize]>() , 12usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33071,7 +33086,7 @@ pub mod root { [u32; 3usize] ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_239316() { + fn __bindgen_test_layout_already_AddRefed_instantiation_208507() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsStyleImageRequest>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33082,7 +33097,7 @@ pub mod root { root::already_AddRefed<root::nsStyleImageRequest> ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_239324() { + fn __bindgen_test_layout_DefaultDelete_instantiation_208515() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33093,7 +33108,7 @@ pub mod root { root::mozilla::DefaultDelete ) )); } #[test] - fn __bindgen_test_layout_UniquePtr_instantiation_239322() { + fn __bindgen_test_layout_UniquePtr_instantiation_208513() { assert_eq!(::std::mem::size_of::<root::mozilla::UniquePtr<root::nsStyleSides>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33104,7 +33119,7 @@ pub mod root { root::mozilla::UniquePtr<root::nsStyleSides> ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_239363() { + fn __bindgen_test_layout_DefaultDelete_instantiation_208554() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33115,7 +33130,7 @@ pub mod root { root::mozilla::DefaultDelete ) )); } #[test] - fn __bindgen_test_layout_pair_instantiation_239514() { + fn __bindgen_test_layout_pair_instantiation_208705() { assert_eq!(::std::mem::size_of::<root::std::pair<::nsstring::nsStringRepr, ::nsstring::nsStringRepr>>() , 32usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33126,7 +33141,7 @@ pub mod root { root::std::pair<::nsstring::nsStringRepr, ::nsstring::nsStringRepr> ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_239513() { + fn __bindgen_test_layout_nsTArray_instantiation_208704() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::std::pair<::nsstring::nsStringRepr, ::nsstring::nsStringRepr>>>() , 8usize , concat ! ( @@ -33141,7 +33156,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_RefPtr_instantiation_240507() { + fn __bindgen_test_layout_RefPtr_instantiation_209698() { assert_eq!(::std::mem::size_of::<root::RefPtr<root::RawServoAnimationValue>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33152,7 +33167,7 @@ pub mod root { root::RefPtr<root::RawServoAnimationValue> ) )); } #[test] - fn __bindgen_test_layout_BaseTimeDuration_instantiation_244499() { + fn __bindgen_test_layout_BaseTimeDuration_instantiation_211420() { assert_eq!(::std::mem::size_of::<root::mozilla::BaseTimeDuration>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33163,7 +33178,7 @@ pub mod root { root::mozilla::BaseTimeDuration ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_245091() { + fn __bindgen_test_layout_nsTArray_instantiation_212012() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::mozilla::DisplayItemClip_RoundedRect>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33176,7 +33191,18 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_Maybe_instantiation_245273() { + fn __bindgen_test_layout_Maybe_instantiation_212274() { + assert_eq!(::std::mem::size_of::<[u64; 2usize]>() , 16usize , concat ! + ( + "Size of template specialization: " , stringify ! ( + [u64; 2usize] ) )); + assert_eq!(::std::mem::align_of::<[u64; 2usize]>() , 8usize , concat ! + ( + "Alignment of template specialization: " , stringify ! ( + [u64; 2usize] ) )); + } + #[test] + fn __bindgen_test_layout_Maybe_instantiation_212281() { assert_eq!(::std::mem::size_of::<[u64; 5usize]>() , 40usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33187,7 +33213,7 @@ pub mod root { [u64; 5usize] ) )); } #[test] - fn __bindgen_test_layout_RefPtr_instantiation_245448() { + fn __bindgen_test_layout_RefPtr_instantiation_212456() { assert_eq!(::std::mem::size_of::<root::RefPtr<root::mozilla::dom::DOMRect>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33198,7 +33224,7 @@ pub mod root { root::RefPtr<root::mozilla::dom::DOMRect> ) )); } #[test] - fn __bindgen_test_layout_Sequence_instantiation_245692() { + fn __bindgen_test_layout_Sequence_instantiation_212700() { assert_eq!(::std::mem::size_of::<u64>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( u64 ) )); @@ -33207,7 +33233,7 @@ pub mod root { u64 ) )); } #[test] - fn __bindgen_test_layout_nsRefPtrHashKey_instantiation_245991() { + fn __bindgen_test_layout_nsRefPtrHashKey_instantiation_212999() { assert_eq!(::std::mem::size_of::<root::nsRefPtrHashKey<root::mozilla::dom::Element>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33218,7 +33244,7 @@ pub mod root { root::nsRefPtrHashKey<root::mozilla::dom::Element> ) )); } #[test] - fn __bindgen_test_layout_nsClassHashtable_instantiation_245990() { + fn __bindgen_test_layout_nsClassHashtable_instantiation_212998() { assert_eq!(::std::mem::size_of::<[u64; 6usize]>() , 48usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33229,7 +33255,7 @@ pub mod root { [u64; 6usize] ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_247222() { + fn __bindgen_test_layout_nsTArray_instantiation_214230() { assert_eq!(::std::mem::size_of::<root::nsTArray<*mut root::mozilla::css::DocumentRule>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -33240,7 +33266,7 @@ pub mod root { root::nsTArray<*mut root::mozilla::css::DocumentRule> ) )); } #[test] - fn __bindgen_test_layout_nsAutoPtr_instantiation_247260() { + fn __bindgen_test_layout_nsAutoPtr_instantiation_214268() { assert_eq!(::std::mem::size_of::<root::nsAutoPtr<root::nsMediaQuery>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( diff --git a/components/style/gecko/generated/structs_release.rs b/components/style/gecko/generated/structs_release.rs index 07d34bef02a..78760c4956b 100644 --- a/components/style/gecko/generated/structs_release.rs +++ b/components/style/gecko/generated/structs_release.rs @@ -1196,58 +1196,98 @@ pub mod root { fn clone(&self) -> Self { *self } } #[repr(C)] + #[derive(Debug, Copy)] + pub struct forward_iterator_tag { + pub _address: u8, + } + #[test] + fn bindgen_test_layout_forward_iterator_tag() { + assert_eq!(::std::mem::size_of::<forward_iterator_tag>() , 1usize + , concat ! ( + "Size of: " , stringify ! ( forward_iterator_tag ) )); + assert_eq! (::std::mem::align_of::<forward_iterator_tag>() , + 1usize , concat ! ( + "Alignment of " , stringify ! ( forward_iterator_tag ) + )); + } + impl Clone for forward_iterator_tag { + fn clone(&self) -> Self { *self } + } + #[repr(C)] + #[derive(Debug, Copy)] + pub struct bidirectional_iterator_tag { + pub _address: u8, + } + #[test] + fn bindgen_test_layout_bidirectional_iterator_tag() { + assert_eq!(::std::mem::size_of::<bidirectional_iterator_tag>() , + 1usize , concat ! ( + "Size of: " , stringify ! ( bidirectional_iterator_tag + ) )); + assert_eq! (::std::mem::align_of::<bidirectional_iterator_tag>() , + 1usize , concat ! ( + "Alignment of " , stringify ! ( + bidirectional_iterator_tag ) )); + } + impl Clone for bidirectional_iterator_tag { + fn clone(&self) -> Self { *self } + } + #[repr(C)] + #[derive(Debug, Copy)] + pub struct random_access_iterator_tag { + pub _address: u8, + } + #[test] + fn bindgen_test_layout_random_access_iterator_tag() { + assert_eq!(::std::mem::size_of::<random_access_iterator_tag>() , + 1usize , concat ! ( + "Size of: " , stringify ! ( random_access_iterator_tag + ) )); + assert_eq! (::std::mem::align_of::<random_access_iterator_tag>() , + 1usize , concat ! ( + "Alignment of " , stringify ! ( + random_access_iterator_tag ) )); + } + impl Clone for random_access_iterator_tag { + fn clone(&self) -> Self { *self } + } + #[repr(C)] + pub struct iterator_traits { + pub _address: u8, + } + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct iterator { pub _address: u8, } - pub type iterator_iterator_category<_Category> = _Category; pub type iterator_value_type<_Tp> = _Tp; pub type iterator_difference_type<_Distance> = _Distance; pub type iterator_pointer<_Pointer> = _Pointer; pub type iterator_reference<_Reference> = _Reference; + pub type iterator_iterator_category<_Category> = _Category; #[repr(C)] - pub struct iterator_traits { - pub _address: u8, - } - #[repr(C)] - pub struct reverse_iterator<_Iterator> { - pub current: _Iterator, - pub _phantom_0: ::std::marker::PhantomData<::std::cell::UnsafeCell<_Iterator>>, + pub struct reverse_iterator<_Iter> { + pub __t: _Iter, + pub current: _Iter, + pub _phantom_0: ::std::marker::PhantomData<::std::cell::UnsafeCell<_Iter>>, } - pub type reverse_iterator___traits_type = root::std::iterator_traits; - pub type reverse_iterator_iterator_type<_Iterator> = _Iterator; + pub type reverse_iterator_iterator_type<_Iter> = _Iter; pub type reverse_iterator_difference_type = - root::std::reverse_iterator___traits_type; - pub type reverse_iterator_pointer = - root::std::reverse_iterator___traits_type; - pub type reverse_iterator_reference = - root::std::reverse_iterator___traits_type; + root::std::iterator_traits; + pub type reverse_iterator_reference = root::std::iterator_traits; + pub type reverse_iterator_pointer = root::std::iterator_traits; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct atomic { } - #[test] - fn __bindgen_test_layout_atomic_instantiation_88637() { - assert_eq!(::std::mem::size_of::<u32>() , 4usize , concat ! ( - "Size of template specialization: " , stringify ! ( u32 - ) )); - assert_eq!(::std::mem::align_of::<u32>() , 4usize , concat ! ( - "Alignment of template specialization: " , stringify ! - ( u32 ) )); - } - #[test] - fn __bindgen_test_layout_atomic_instantiation_88645() { - assert_eq!(::std::mem::size_of::<u64>() , 8usize , concat ! ( - "Size of template specialization: " , stringify ! ( u64 - ) )); - assert_eq!(::std::mem::align_of::<u64>() , 8usize , concat ! ( - "Alignment of template specialization: " , stringify ! - ( u64 ) )); + pub type atomic___base = u8; + #[repr(C)] + pub struct __bit_const_reference { + pub __seg_: root::std::__bit_const_reference___storage_pointer, + pub __mask_: root::std::__bit_const_reference___storage_type, } - } - pub mod __gnu_cxx { - #[allow(unused_imports)] - use self::super::super::root; + pub type __bit_const_reference___storage_type = [u8; 0usize]; + pub type __bit_const_reference___storage_pointer = [u8; 0usize]; } pub mod mozilla { #[allow(unused_imports)] @@ -1486,7 +1526,7 @@ pub mod root { #[repr(C)] #[derive(Debug)] pub struct MutexImpl { - pub platformData_: [*mut ::std::os::raw::c_void; 5usize], + pub platformData_: [*mut ::std::os::raw::c_void; 8usize], } #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -1495,7 +1535,7 @@ pub mod root { } #[test] fn bindgen_test_layout_MutexImpl() { - assert_eq!(::std::mem::size_of::<MutexImpl>() , 40usize , + assert_eq!(::std::mem::size_of::<MutexImpl>() , 64usize , concat ! ( "Size of: " , stringify ! ( MutexImpl ) )); assert_eq! (::std::mem::align_of::<MutexImpl>() , 8usize , @@ -2091,7 +2131,7 @@ pub mod root { } } #[repr(C)] - #[derive(Debug)] + #[derive(Debug, Copy)] pub struct ThreadSafeAutoRefCnt { pub mValue: u64, } @@ -2112,6 +2152,9 @@ pub mod root { ThreadSafeAutoRefCnt ) , "::" , stringify ! ( mValue ) )); } + impl Clone for ThreadSafeAutoRefCnt { + fn clone(&self) -> Self { *self } + } #[repr(C)] #[derive(Debug)] pub struct OwningNonNull<T> { @@ -3657,20 +3700,9 @@ pub mod root { _unused: [u8; 0], } #[repr(C)] - #[derive(Debug)] + #[derive(Debug, Copy, Clone)] pub struct EventHandlerNonNull { - pub _base: root::mozilla::dom::CallbackFunction, - } - #[test] - fn bindgen_test_layout_EventHandlerNonNull() { - assert_eq!(::std::mem::size_of::<EventHandlerNonNull>() , - 48usize , concat ! ( - "Size of: " , stringify ! ( EventHandlerNonNull ) - )); - assert_eq! (::std::mem::align_of::<EventHandlerNonNull>() , - 8usize , concat ! ( - "Alignment of " , stringify ! ( - EventHandlerNonNull ) )); + _unused: [u8; 0], } #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -6771,7 +6803,7 @@ pub mod root { _unused: [u8; 0], } #[test] - fn __bindgen_test_layout_StaticRefPtr_instantiation_138533() { + fn __bindgen_test_layout_StaticRefPtr_instantiation_116395() { assert_eq!(::std::mem::size_of::<root::mozilla::StaticRefPtr<root::mozilla::URLExtraData>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -7764,7 +7796,7 @@ pub mod root { } #[test] fn bindgen_test_layout_OffTheBooksMutex() { - assert_eq!(::std::mem::size_of::<OffTheBooksMutex>() , 40usize , + assert_eq!(::std::mem::size_of::<OffTheBooksMutex>() , 64usize , concat ! ( "Size of: " , stringify ! ( OffTheBooksMutex ) )); assert_eq! (::std::mem::align_of::<OffTheBooksMutex>() , 8usize , @@ -7783,7 +7815,7 @@ pub mod root { } #[test] fn bindgen_test_layout_Mutex() { - assert_eq!(::std::mem::size_of::<Mutex>() , 40usize , concat ! ( + assert_eq!(::std::mem::size_of::<Mutex>() , 64usize , concat ! ( "Size of: " , stringify ! ( Mutex ) )); assert_eq! (::std::mem::align_of::<Mutex>() , 8usize , concat ! ( "Alignment of " , stringify ! ( Mutex ) )); @@ -8593,9 +8625,9 @@ pub mod root { #[repr(C)] #[derive(Debug)] pub struct ServoElementSnapshot { - pub mContains: root::mozilla::ServoElementSnapshot_Flags, pub mAttrs: root::nsTArray<root::mozilla::ServoAttrSnapshot>, pub mState: root::mozilla::ServoElementSnapshot_ServoStateType, + pub mContains: root::mozilla::ServoElementSnapshot_Flags, pub mIsHTMLElementInHTMLDocument: bool, pub mIsInChromeDocument: bool, } @@ -8608,7 +8640,7 @@ pub mod root { as ServoElementSnapshot_Flags; #[test] fn bindgen_test_layout_ServoElementSnapshot() { - assert_eq!(::std::mem::size_of::<ServoElementSnapshot>() , 32usize + assert_eq!(::std::mem::size_of::<ServoElementSnapshot>() , 24usize , concat ! ( "Size of: " , stringify ! ( ServoElementSnapshot ) )); assert_eq! (::std::mem::align_of::<ServoElementSnapshot>() , @@ -8616,34 +8648,34 @@ pub mod root { "Alignment of " , stringify ! ( ServoElementSnapshot ) )); assert_eq! (unsafe { - & ( * ( 0 as * const ServoElementSnapshot ) ) . - mContains as * const _ as usize } , 0usize , concat ! - ( - "Alignment of field: " , stringify ! ( - ServoElementSnapshot ) , "::" , stringify ! ( - mContains ) )); - assert_eq! (unsafe { & ( * ( 0 as * const ServoElementSnapshot ) ) . mAttrs - as * const _ as usize } , 8usize , concat ! ( + as * const _ as usize } , 0usize , concat ! ( "Alignment of field: " , stringify ! ( ServoElementSnapshot ) , "::" , stringify ! ( mAttrs ) )); assert_eq! (unsafe { & ( * ( 0 as * const ServoElementSnapshot ) ) . mState - as * const _ as usize } , 16usize , concat ! ( + as * const _ as usize } , 8usize , concat ! ( "Alignment of field: " , stringify ! ( ServoElementSnapshot ) , "::" , stringify ! ( mState ) )); assert_eq! (unsafe { & ( * ( 0 as * const ServoElementSnapshot ) ) . + mContains as * const _ as usize } , 16usize , concat ! + ( + "Alignment of field: " , stringify ! ( + ServoElementSnapshot ) , "::" , stringify ! ( + mContains ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const ServoElementSnapshot ) ) . mIsHTMLElementInHTMLDocument as * const _ as usize } , - 24usize , concat ! ( + 17usize , concat ! ( "Alignment of field: " , stringify ! ( ServoElementSnapshot ) , "::" , stringify ! ( mIsHTMLElementInHTMLDocument ) )); assert_eq! (unsafe { & ( * ( 0 as * const ServoElementSnapshot ) ) . - mIsInChromeDocument as * const _ as usize } , 25usize + mIsInChromeDocument as * const _ as usize } , 18usize , concat ! ( "Alignment of field: " , stringify ! ( ServoElementSnapshot ) , "::" , stringify ! ( @@ -8902,7 +8934,7 @@ pub mod root { ( mValue ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_175461() { + fn __bindgen_test_layout_DefaultDelete_instantiation_153384() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -9358,7 +9390,8 @@ pub mod root { pub enum ServoElementSnapshotFlags { State = 1, Attributes = 2, - All = 3, + Id = 4, + MaybeClass = 8, } #[repr(C)] #[derive(Debug, Copy)] @@ -11135,7 +11168,7 @@ pub mod root { pub _address: u8, } #[test] - fn __bindgen_test_layout_nsCharTraits_instantiation_53929() { + fn __bindgen_test_layout_nsCharTraits_instantiation_51822() { assert_eq!(::std::mem::size_of::<root::nsCharTraits>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -11146,7 +11179,7 @@ pub mod root { root::nsCharTraits ) )); } #[test] - fn __bindgen_test_layout_nsCharTraits_instantiation_53933() { + fn __bindgen_test_layout_nsCharTraits_instantiation_51826() { assert_eq!(::std::mem::size_of::<root::nsCharTraits>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -12783,7 +12816,7 @@ pub mod root { } pub type nsCOMPtr_element_type<T> = T; #[test] - fn __bindgen_test_layout_nsCOMPtr_instantiation_91725() { + fn __bindgen_test_layout_nsCOMPtr_instantiation_60935() { assert_eq!(::std::mem::size_of::<root::nsCOMPtr>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -14343,7 +14376,7 @@ pub mod root { * count is 1. */ #[repr(C)] - #[derive(Debug)] + #[derive(Debug, Copy)] pub struct nsStringBuffer { pub mRefCount: u32, pub mStorageSize: u32, @@ -14365,6 +14398,9 @@ pub mod root { "Alignment of field: " , stringify ! ( nsStringBuffer ) , "::" , stringify ! ( mStorageSize ) )); } + impl Clone for nsStringBuffer { + fn clone(&self) -> Self { *self } + } #[repr(C)] #[derive(Debug, Copy)] pub struct nsIAtom { @@ -20373,57 +20409,57 @@ pub mod root { pub struct nsDOMMutationObserver { _unused: [u8; 0], } - pub const NODE_HAS_LISTENERMANAGER: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_HAS_LISTENERMANAGER; - pub const NODE_HAS_PROPERTIES: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_HAS_PROPERTIES; - pub const NODE_IS_ANONYMOUS_ROOT: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_IS_ANONYMOUS_ROOT; - pub const NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE; - pub const NODE_IS_NATIVE_ANONYMOUS_ROOT: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_IS_NATIVE_ANONYMOUS_ROOT; - pub const NODE_FORCE_XBL_BINDINGS: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_FORCE_XBL_BINDINGS; - pub const NODE_MAY_BE_IN_BINDING_MNGR: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_MAY_BE_IN_BINDING_MNGR; - pub const NODE_IS_EDITABLE: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_IS_EDITABLE; - pub const NODE_IS_NATIVE_ANONYMOUS: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_IS_NATIVE_ANONYMOUS; - pub const NODE_IS_IN_SHADOW_TREE: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_IS_IN_SHADOW_TREE; - pub const NODE_HAS_EMPTY_SELECTOR: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_HAS_EMPTY_SELECTOR; - pub const NODE_HAS_SLOW_SELECTOR: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_HAS_SLOW_SELECTOR; - pub const NODE_HAS_EDGE_CHILD_SELECTOR: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_HAS_EDGE_CHILD_SELECTOR; - pub const NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS; - pub const NODE_ALL_SELECTOR_FLAGS: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_ALL_SELECTOR_FLAGS; - pub const NODE_NEEDS_FRAME: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_NEEDS_FRAME; - pub const NODE_DESCENDANTS_NEED_FRAMES: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_DESCENDANTS_NEED_FRAMES; - pub const NODE_HAS_ACCESSKEY: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_HAS_ACCESSKEY; - pub const NODE_HAS_DIRECTION_RTL: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_HAS_DIRECTION_RTL; - pub const NODE_HAS_DIRECTION_LTR: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_HAS_DIRECTION_LTR; - pub const NODE_ALL_DIRECTION_FLAGS: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_ALL_DIRECTION_FLAGS; - pub const NODE_CHROME_ONLY_ACCESS: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_CHROME_ONLY_ACCESS; - pub const NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS; - pub const NODE_TYPE_SPECIFIC_BITS_OFFSET: root::_bindgen_ty_82 = - _bindgen_ty_82::NODE_TYPE_SPECIFIC_BITS_OFFSET; + pub const NODE_HAS_LISTENERMANAGER: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_LISTENERMANAGER; + pub const NODE_HAS_PROPERTIES: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_PROPERTIES; + pub const NODE_IS_ANONYMOUS_ROOT: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_IS_ANONYMOUS_ROOT; + pub const NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE; + pub const NODE_IS_NATIVE_ANONYMOUS_ROOT: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_IS_NATIVE_ANONYMOUS_ROOT; + pub const NODE_FORCE_XBL_BINDINGS: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_FORCE_XBL_BINDINGS; + pub const NODE_MAY_BE_IN_BINDING_MNGR: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_MAY_BE_IN_BINDING_MNGR; + pub const NODE_IS_EDITABLE: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_IS_EDITABLE; + pub const NODE_IS_NATIVE_ANONYMOUS: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_IS_NATIVE_ANONYMOUS; + pub const NODE_IS_IN_SHADOW_TREE: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_IS_IN_SHADOW_TREE; + pub const NODE_HAS_EMPTY_SELECTOR: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_EMPTY_SELECTOR; + pub const NODE_HAS_SLOW_SELECTOR: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_SLOW_SELECTOR; + pub const NODE_HAS_EDGE_CHILD_SELECTOR: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_EDGE_CHILD_SELECTOR; + pub const NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS; + pub const NODE_ALL_SELECTOR_FLAGS: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_ALL_SELECTOR_FLAGS; + pub const NODE_NEEDS_FRAME: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_NEEDS_FRAME; + pub const NODE_DESCENDANTS_NEED_FRAMES: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_DESCENDANTS_NEED_FRAMES; + pub const NODE_HAS_ACCESSKEY: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_ACCESSKEY; + pub const NODE_HAS_DIRECTION_RTL: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_DIRECTION_RTL; + pub const NODE_HAS_DIRECTION_LTR: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_HAS_DIRECTION_LTR; + pub const NODE_ALL_DIRECTION_FLAGS: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_ALL_DIRECTION_FLAGS; + pub const NODE_CHROME_ONLY_ACCESS: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_CHROME_ONLY_ACCESS; + pub const NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS; + pub const NODE_TYPE_SPECIFIC_BITS_OFFSET: root::_bindgen_ty_17 = + _bindgen_ty_17::NODE_TYPE_SPECIFIC_BITS_OFFSET; #[repr(u32)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] - pub enum _bindgen_ty_82 { + pub enum _bindgen_ty_17 { NODE_HAS_LISTENERMANAGER = 4, NODE_HAS_PROPERTIES = 8, NODE_IS_ANONYMOUS_ROOT = 16, @@ -26756,7 +26792,7 @@ pub mod root { pub type imgRequest_HasThreadSafeRefCnt = root::mozilla::TrueType; #[test] fn bindgen_test_layout_imgRequest() { - assert_eq!(::std::mem::size_of::<imgRequest>() , 376usize , concat ! ( + assert_eq!(::std::mem::size_of::<imgRequest>() , 400usize , concat ! ( "Size of: " , stringify ! ( imgRequest ) )); assert_eq! (::std::mem::align_of::<imgRequest>() , 8usize , concat ! ( "Alignment of " , stringify ! ( imgRequest ) )); @@ -28172,7 +28208,7 @@ pub mod root { ) , "::" , stringify ! ( mQuotePairs ) )); } #[test] - fn __bindgen_test_layout_StaticRefPtr_instantiation_171453() { + fn __bindgen_test_layout_StaticRefPtr_instantiation_149376() { assert_eq!(::std::mem::size_of::<root::mozilla::StaticRefPtr<root::nsStyleQuoteValues>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -30640,48 +30676,48 @@ pub mod root { pub struct nsAttrValueOrString { _unused: [u8; 0], } - pub const ELEMENT_SHARED_RESTYLE_BIT_1: root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_SHARED_RESTYLE_BIT_1; - pub const ELEMENT_SHARED_RESTYLE_BIT_2: root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_SHARED_RESTYLE_BIT_2; - pub const ELEMENT_SHARED_RESTYLE_BIT_3: root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_SHARED_RESTYLE_BIT_3; - pub const ELEMENT_SHARED_RESTYLE_BIT_4: root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_SHARED_RESTYLE_BIT_4; - pub const ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO: root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_SHARED_RESTYLE_BIT_1; + pub const ELEMENT_SHARED_RESTYLE_BIT_1: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_1; + pub const ELEMENT_SHARED_RESTYLE_BIT_2: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_2; + pub const ELEMENT_SHARED_RESTYLE_BIT_3: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_3; + pub const ELEMENT_SHARED_RESTYLE_BIT_4: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_4; + pub const ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_1; pub const ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO: - root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_SHARED_RESTYLE_BIT_2; - pub const ELEMENT_HAS_SNAPSHOT: root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_SHARED_RESTYLE_BIT_3; - pub const ELEMENT_HANDLED_SNAPSHOT: root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_SHARED_RESTYLE_BIT_4; - pub const ELEMENT_HAS_PENDING_RESTYLE: root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_SHARED_RESTYLE_BIT_1; - pub const ELEMENT_IS_POTENTIAL_RESTYLE_ROOT: root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_SHARED_RESTYLE_BIT_2; - pub const ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE: root::_bindgen_ty_84 + root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_2; + pub const ELEMENT_HAS_SNAPSHOT: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_3; + pub const ELEMENT_HANDLED_SNAPSHOT: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_4; + pub const ELEMENT_HAS_PENDING_RESTYLE: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_1; + pub const ELEMENT_IS_POTENTIAL_RESTYLE_ROOT: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_2; + pub const ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE: root::_bindgen_ty_19 = - _bindgen_ty_84::ELEMENT_SHARED_RESTYLE_BIT_3; + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_3; pub const ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT: - root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_SHARED_RESTYLE_BIT_4; - pub const ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR: root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR; - pub const ELEMENT_PENDING_RESTYLE_FLAGS: root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_PENDING_RESTYLE_FLAGS; - pub const ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS: root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS; - pub const ELEMENT_ALL_RESTYLE_FLAGS: root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_ALL_RESTYLE_FLAGS; - pub const ELEMENT_HAS_SCROLLGRAB: root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_HAS_SCROLLGRAB; - pub const ELEMENT_TYPE_SPECIFIC_BITS_OFFSET: root::_bindgen_ty_84 = - _bindgen_ty_84::ELEMENT_TYPE_SPECIFIC_BITS_OFFSET; + root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_SHARED_RESTYLE_BIT_4; + pub const ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR; + pub const ELEMENT_PENDING_RESTYLE_FLAGS: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_PENDING_RESTYLE_FLAGS; + pub const ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS; + pub const ELEMENT_ALL_RESTYLE_FLAGS: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_ALL_RESTYLE_FLAGS; + pub const ELEMENT_HAS_SCROLLGRAB: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_HAS_SCROLLGRAB; + pub const ELEMENT_TYPE_SPECIFIC_BITS_OFFSET: root::_bindgen_ty_19 = + _bindgen_ty_19::ELEMENT_TYPE_SPECIFIC_BITS_OFFSET; #[repr(u32)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] - pub enum _bindgen_ty_84 { + pub enum _bindgen_ty_19 { ELEMENT_SHARED_RESTYLE_BIT_1 = 8388608, ELEMENT_SHARED_RESTYLE_BIT_2 = 16777216, ELEMENT_SHARED_RESTYLE_BIT_3 = 33554432, @@ -31450,7 +31486,7 @@ pub mod root { } pub type __builtin_va_list = [root::__va_list_tag; 1usize]; #[test] - fn __bindgen_test_layout_IntegralConstant_instantiation_199188() { + fn __bindgen_test_layout_IntegralConstant_instantiation_170931() { assert_eq!(::std::mem::size_of::<u8>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( u8 ) )); @@ -31459,7 +31495,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_IntegralConstant_instantiation_199192() { + fn __bindgen_test_layout_IntegralConstant_instantiation_170935() { assert_eq!(::std::mem::size_of::<u8>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( u8 ) )); @@ -31468,7 +31504,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_nsReadingIterator_instantiation_200016() { + fn __bindgen_test_layout_nsReadingIterator_instantiation_171149() { assert_eq!(::std::mem::size_of::<root::nsReadingIterator<u16>>() , 24usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31479,7 +31515,7 @@ pub mod root { root::nsReadingIterator<u16> ) )); } #[test] - fn __bindgen_test_layout_nsWritingIterator_instantiation_200020() { + fn __bindgen_test_layout_nsWritingIterator_instantiation_171153() { assert_eq!(::std::mem::size_of::<root::nsWritingIterator<u16>>() , 24usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31490,7 +31526,7 @@ pub mod root { root::nsWritingIterator<u16> ) )); } #[test] - fn __bindgen_test_layout_nsReadingIterator_instantiation_200093() { + fn __bindgen_test_layout_nsReadingIterator_instantiation_171226() { assert_eq!(::std::mem::size_of::<root::nsReadingIterator<::std::os::raw::c_char>>() , 24usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31501,7 +31537,7 @@ pub mod root { root::nsReadingIterator<::std::os::raw::c_char> ) )); } #[test] - fn __bindgen_test_layout_nsWritingIterator_instantiation_200097() { + fn __bindgen_test_layout_nsWritingIterator_instantiation_171230() { assert_eq!(::std::mem::size_of::<root::nsWritingIterator<::std::os::raw::c_char>>() , 24usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31512,7 +31548,25 @@ pub mod root { root::nsWritingIterator<::std::os::raw::c_char> ) )); } #[test] - fn __bindgen_test_layout__bindgen_ty_id_205866_instantiation_205863() { + fn __bindgen_test_layout_atomic_instantiation_173213() { + assert_eq!(::std::mem::size_of::<u32>() , 4usize , concat ! ( + "Size of template specialization: " , stringify ! ( u32 ) + )); + assert_eq!(::std::mem::align_of::<u32>() , 4usize , concat ! ( + "Alignment of template specialization: " , stringify ! ( + u32 ) )); + } + #[test] + fn __bindgen_test_layout_atomic_instantiation_173221() { + assert_eq!(::std::mem::size_of::<u64>() , 8usize , concat ! ( + "Size of template specialization: " , stringify ! ( u64 ) + )); + assert_eq!(::std::mem::align_of::<u64>() , 8usize , concat ! ( + "Alignment of template specialization: " , stringify ! ( + u64 ) )); + } + #[test] + fn __bindgen_test_layout__bindgen_ty_id_173478_instantiation_173475() { assert_eq!(::std::mem::size_of::<u8>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( u8 ) )); @@ -31521,7 +31575,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout__bindgen_ty_id_205899_instantiation_205896() { + fn __bindgen_test_layout__bindgen_ty_id_173511_instantiation_173508() { assert_eq!(::std::mem::size_of::<u8>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( u8 ) )); @@ -31530,7 +31584,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_206167() { + fn __bindgen_test_layout_nsTArray_instantiation_173779() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::nsCString>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31541,7 +31595,7 @@ pub mod root { root::nsTArray<root::nsCString> ) )); } #[test] - fn __bindgen_test_layout_Handle_instantiation_207119() { + fn __bindgen_test_layout_Handle_instantiation_174604() { assert_eq!(::std::mem::size_of::<root::JS::Handle<*mut root::JSObject>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31552,7 +31606,7 @@ pub mod root { root::JS::Handle<*mut root::JSObject> ) )); } #[test] - fn __bindgen_test_layout_Handle_instantiation_207135() { + fn __bindgen_test_layout_Handle_instantiation_174620() { assert_eq!(::std::mem::size_of::<root::JS::Handle<root::JS::Value>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31563,7 +31617,7 @@ pub mod root { root::JS::Handle<root::JS::Value> ) )); } #[test] - fn __bindgen_test_layout_MutableHandle_instantiation_207145() { + fn __bindgen_test_layout_MutableHandle_instantiation_174630() { assert_eq!(::std::mem::size_of::<root::JS::MutableHandle<*mut root::JSObject>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31574,7 +31628,7 @@ pub mod root { root::JS::MutableHandle<*mut root::JSObject> ) )); } #[test] - fn __bindgen_test_layout_MutableHandle_instantiation_207161() { + fn __bindgen_test_layout_MutableHandle_instantiation_174646() { assert_eq!(::std::mem::size_of::<root::JS::MutableHandle<root::JS::Value>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31585,7 +31639,7 @@ pub mod root { root::JS::MutableHandle<root::JS::Value> ) )); } #[test] - fn __bindgen_test_layout_Rooted_instantiation_207164() { + fn __bindgen_test_layout_Rooted_instantiation_174649() { assert_eq!(::std::mem::size_of::<[u64; 3usize]>() , 24usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31596,7 +31650,7 @@ pub mod root { [u64; 3usize] ) )); } #[test] - fn __bindgen_test_layout_DeletePolicy_instantiation_207501() { + fn __bindgen_test_layout_DeletePolicy_instantiation_174986() { assert_eq!(::std::mem::size_of::<root::JS::DeletePolicy>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31607,7 +31661,7 @@ pub mod root { root::JS::DeletePolicy ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_209504() { + fn __bindgen_test_layout_nsTArray_instantiation_179915() { assert_eq!(::std::mem::size_of::<root::nsTArray<::nsstring::nsStringRepr>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31618,7 +31672,7 @@ pub mod root { root::nsTArray<::nsstring::nsStringRepr> ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_209508() { + fn __bindgen_test_layout_nsTArray_instantiation_179919() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::mozilla::FontFamilyName>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31629,7 +31683,7 @@ pub mod root { root::nsTArray<root::mozilla::FontFamilyName> ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_209521() { + fn __bindgen_test_layout_nsTArray_instantiation_179932() { assert_eq!(::std::mem::size_of::<root::nsTArray<::std::os::raw::c_uint>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31640,7 +31694,7 @@ pub mod root { root::nsTArray<::std::os::raw::c_uint> ) )); } #[test] - fn __bindgen_test_layout_TenuredHeap_instantiation_210388() { + fn __bindgen_test_layout_TenuredHeap_instantiation_180792() { assert_eq!(::std::mem::size_of::<root::JS::TenuredHeap>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31651,7 +31705,7 @@ pub mod root { root::JS::TenuredHeap ) )); } #[test] - fn __bindgen_test_layout_Heap_instantiation_210478() { + fn __bindgen_test_layout_Heap_instantiation_180882() { assert_eq!(::std::mem::size_of::<root::JS::Heap<*mut root::JSObject>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31662,7 +31716,7 @@ pub mod root { root::JS::Heap<*mut root::JSObject> ) )); } #[test] - fn __bindgen_test_layout_TErrorResult_instantiation_210588() { + fn __bindgen_test_layout_TErrorResult_instantiation_180992() { assert_eq!(::std::mem::size_of::<root::mozilla::binding_danger::TErrorResult>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31673,7 +31727,7 @@ pub mod root { root::mozilla::binding_danger::TErrorResult ) )); } #[test] - fn __bindgen_test_layout_TErrorResult_instantiation_210604() { + fn __bindgen_test_layout_TErrorResult_instantiation_181008() { assert_eq!(::std::mem::size_of::<root::mozilla::binding_danger::TErrorResult>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31684,7 +31738,7 @@ pub mod root { root::mozilla::binding_danger::TErrorResult ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_210609() { + fn __bindgen_test_layout_already_AddRefed_instantiation_181014() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsStringBuffer>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31695,7 +31749,7 @@ pub mod root { root::already_AddRefed<root::nsStringBuffer> ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_210661() { + fn __bindgen_test_layout_already_AddRefed_instantiation_181066() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsIAtom>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31706,7 +31760,7 @@ pub mod root { root::already_AddRefed<root::nsIAtom> ) )); } #[test] - fn __bindgen_test_layout_RefPtr_instantiation_211135() { + fn __bindgen_test_layout_RefPtr_instantiation_181540() { assert_eq!(::std::mem::size_of::<root::RefPtr<root::mozilla::StyleSheet>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31717,7 +31771,7 @@ pub mod root { root::RefPtr<root::mozilla::StyleSheet> ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_211481() { + fn __bindgen_test_layout_already_AddRefed_instantiation_181886() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::mozilla::dom::NodeInfo>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31728,7 +31782,7 @@ pub mod root { root::already_AddRefed<root::mozilla::dom::NodeInfo> ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_211724() { + fn __bindgen_test_layout_already_AddRefed_instantiation_182129() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsIURI>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31739,7 +31793,7 @@ pub mod root { root::already_AddRefed<root::nsIURI> ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_211871() { + fn __bindgen_test_layout_already_AddRefed_instantiation_182276() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsINode>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31750,7 +31804,7 @@ pub mod root { root::already_AddRefed<root::nsINode> ) )); } #[test] - fn __bindgen_test_layout_DeletePolicy_instantiation_215966() { + fn __bindgen_test_layout_DeletePolicy_instantiation_186364() { assert_eq!(::std::mem::size_of::<root::JS::DeletePolicy>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31761,7 +31815,7 @@ pub mod root { root::JS::DeletePolicy ) )); } #[test] - fn __bindgen_test_layout_UniquePtr_instantiation_215964() { + fn __bindgen_test_layout_UniquePtr_instantiation_186362() { assert_eq!(::std::mem::size_of::<root::mozilla::UniquePtr<root::JSErrorNotes_Note>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31772,7 +31826,7 @@ pub mod root { root::mozilla::UniquePtr<root::JSErrorNotes_Note> ) )); } #[test] - fn __bindgen_test_layout_iterator_instantiation_215999() { + fn __bindgen_test_layout_iterator_instantiation_186397() { assert_eq!(::std::mem::size_of::<root::std::iterator>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31783,7 +31837,7 @@ pub mod root { root::std::iterator ) )); } #[test] - fn __bindgen_test_layout_nsCOMPtr_instantiation_216565() { + fn __bindgen_test_layout_nsCOMPtr_instantiation_186951() { assert_eq!(::std::mem::size_of::<root::nsCOMPtr>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31794,7 +31848,7 @@ pub mod root { root::nsCOMPtr ) )); } #[test] - fn __bindgen_test_layout_Heap_instantiation_217818() { + fn __bindgen_test_layout_Heap_instantiation_188204() { assert_eq!(::std::mem::size_of::<root::JS::Heap<root::JS::Value>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31805,7 +31859,7 @@ pub mod root { root::JS::Heap<root::JS::Value> ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_218160() { + fn __bindgen_test_layout_nsTArray_instantiation_188546() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::RefPtr<root::mozilla::dom::AnonymousContent>>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31818,7 +31872,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_LinkedList_instantiation_218436() { + fn __bindgen_test_layout_LinkedList_instantiation_188822() { assert_eq!(::std::mem::size_of::<root::mozilla::LinkedList>() , 24usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31829,7 +31883,7 @@ pub mod root { root::mozilla::LinkedList ) )); } #[test] - fn __bindgen_test_layout_RefPtr_instantiation_218452() { + fn __bindgen_test_layout_RefPtr_instantiation_188838() { assert_eq!(::std::mem::size_of::<root::RefPtr<root::mozilla::dom::Element>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31840,7 +31894,7 @@ pub mod root { root::RefPtr<root::mozilla::dom::Element> ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_218451() { + fn __bindgen_test_layout_nsTArray_instantiation_188837() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::RefPtr<root::mozilla::dom::Element>>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31853,7 +31907,7 @@ pub mod root { )); } #[test] - fn __bindgen_test_layout_nsCOMPtr_instantiation_218481() { + fn __bindgen_test_layout_nsCOMPtr_instantiation_188867() { assert_eq!(::std::mem::size_of::<root::nsCOMPtr>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31864,7 +31918,7 @@ pub mod root { root::nsCOMPtr ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_218480() { + fn __bindgen_test_layout_nsTArray_instantiation_188866() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::nsCOMPtr>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31875,7 +31929,7 @@ pub mod root { root::nsTArray<root::nsCOMPtr> ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_218526() { + fn __bindgen_test_layout_already_AddRefed_instantiation_188912() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsIDocument>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31886,7 +31940,7 @@ pub mod root { root::already_AddRefed<root::nsIDocument> ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_218691() { + fn __bindgen_test_layout_already_AddRefed_instantiation_189077() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsContentList>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31897,7 +31951,7 @@ pub mod root { root::already_AddRefed<root::nsContentList> ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_219018() { + fn __bindgen_test_layout_already_AddRefed_instantiation_189404() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsIRunnable>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31908,7 +31962,7 @@ pub mod root { root::already_AddRefed<root::nsIRunnable> ) )); } #[test] - fn __bindgen_test_layout_nsCOMPtr_instantiation_219111() { + fn __bindgen_test_layout_nsCOMPtr_instantiation_189497() { assert_eq!(::std::mem::size_of::<root::nsCOMPtr>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31919,7 +31973,7 @@ pub mod root { root::nsCOMPtr ) )); } #[test] - fn __bindgen_test_layout_nsCOMPtr_instantiation_219148() { + fn __bindgen_test_layout_nsCOMPtr_instantiation_189534() { assert_eq!(::std::mem::size_of::<root::nsCOMPtr>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31930,7 +31984,7 @@ pub mod root { root::nsCOMPtr ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_219404() { + fn __bindgen_test_layout_DefaultDelete_instantiation_189790() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31941,7 +31995,7 @@ pub mod root { root::mozilla::DefaultDelete ) )); } #[test] - fn __bindgen_test_layout_UniquePtr_instantiation_219402() { + fn __bindgen_test_layout_UniquePtr_instantiation_189788() { assert_eq!(::std::mem::size_of::<root::mozilla::UniquePtr<root::nsISMILAttr>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31952,7 +32006,7 @@ pub mod root { root::mozilla::UniquePtr<root::nsISMILAttr> ) )); } #[test] - fn __bindgen_test_layout_nsRefPtrHashKey_instantiation_219942() { + fn __bindgen_test_layout_nsRefPtrHashKey_instantiation_190328() { assert_eq!(::std::mem::size_of::<root::nsRefPtrHashKey<root::mozilla::dom::DOMIntersectionObserver>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31965,7 +32019,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_nsDataHashtable_instantiation_219941() { + fn __bindgen_test_layout_nsDataHashtable_instantiation_190327() { assert_eq!(::std::mem::size_of::<[u64; 5usize]>() , 40usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31976,7 +32030,7 @@ pub mod root { [u64; 5usize] ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_220146() { + fn __bindgen_test_layout_nsTArray_instantiation_190532() { assert_eq!(::std::mem::size_of::<root::nsTArray<*mut root::nsIContent>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -31987,7 +32041,7 @@ pub mod root { root::nsTArray<*mut root::nsIContent> ) )); } #[test] - fn __bindgen_test_layout_SupportsWeakPtr_instantiation_220193() { + fn __bindgen_test_layout_SupportsWeakPtr_instantiation_190579() { assert_eq!(::std::mem::size_of::<u64>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( u64 ) )); @@ -31996,7 +32050,7 @@ pub mod root { u64 ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_220370() { + fn __bindgen_test_layout_nsTArray_instantiation_190751() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::nsRect>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32007,7 +32061,7 @@ pub mod root { root::nsTArray<root::nsRect> ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_220486() { + fn __bindgen_test_layout_DefaultDelete_instantiation_190867() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32018,7 +32072,7 @@ pub mod root { root::mozilla::DefaultDelete ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_220652() { + fn __bindgen_test_layout_nsTArray_instantiation_191033() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::nsCOMPtr>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32029,7 +32083,7 @@ pub mod root { root::nsTArray<root::nsCOMPtr> ) )); } #[test] - fn __bindgen_test_layout_nsPIDOMWindow_instantiation_221439() { + fn __bindgen_test_layout_nsPIDOMWindow_instantiation_191820() { assert_eq!(::std::mem::size_of::<[u64; 28usize]>() , 224usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32040,7 +32094,7 @@ pub mod root { [u64; 28usize] ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_221531() { + fn __bindgen_test_layout_already_AddRefed_instantiation_191872() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsIContent>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32051,7 +32105,7 @@ pub mod root { root::already_AddRefed<root::nsIContent> ) )); } #[test] - fn __bindgen_test_layout_nsRefPtrHashtable_instantiation_221712() { + fn __bindgen_test_layout_nsRefPtrHashtable_instantiation_192053() { assert_eq!(::std::mem::size_of::<[u64; 5usize]>() , 40usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32062,7 +32116,7 @@ pub mod root { [u64; 5usize] ) )); } #[test] - fn __bindgen_test_layout_nsPtrHashKey_instantiation_222229() { + fn __bindgen_test_layout_nsPtrHashKey_instantiation_192554() { assert_eq!(::std::mem::size_of::<root::nsPtrHashKey<root::WeakFrame>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32073,7 +32127,7 @@ pub mod root { root::nsPtrHashKey<root::WeakFrame> ) )); } #[test] - fn __bindgen_test_layout_OwningNonNull_instantiation_222344() { + fn __bindgen_test_layout_OwningNonNull_instantiation_192669() { assert_eq!(::std::mem::size_of::<root::mozilla::OwningNonNull<root::nsINode>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32084,7 +32138,7 @@ pub mod root { root::mozilla::OwningNonNull<root::nsINode> ) )); } #[test] - fn __bindgen_test_layout_nsPtrHashKey_instantiation_222629() { + fn __bindgen_test_layout_nsPtrHashKey_instantiation_192954() { assert_eq!(::std::mem::size_of::<root::nsPtrHashKey<::std::os::raw::c_void>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32095,7 +32149,7 @@ pub mod root { root::nsPtrHashKey<::std::os::raw::c_void> ) )); } #[test] - fn __bindgen_test_layout_PointTyped_instantiation_223420() { + fn __bindgen_test_layout_PointTyped_instantiation_193745() { assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32106,7 +32160,7 @@ pub mod root { [u32; 2usize] ) )); } #[test] - fn __bindgen_test_layout_IntPointTyped_instantiation_223425() { + fn __bindgen_test_layout_IntPointTyped_instantiation_193750() { assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32117,7 +32171,7 @@ pub mod root { [u32; 2usize] ) )); } #[test] - fn __bindgen_test_layout_SizeTyped_instantiation_223428() { + fn __bindgen_test_layout_SizeTyped_instantiation_193753() { assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32128,7 +32182,7 @@ pub mod root { [u32; 2usize] ) )); } #[test] - fn __bindgen_test_layout_RectTyped_instantiation_223436() { + fn __bindgen_test_layout_RectTyped_instantiation_193761() { assert_eq!(::std::mem::size_of::<[u32; 4usize]>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32139,7 +32193,7 @@ pub mod root { [u32; 4usize] ) )); } #[test] - fn __bindgen_test_layout_IntPointTyped_instantiation_223468() { + fn __bindgen_test_layout_IntPointTyped_instantiation_193793() { assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32150,7 +32204,7 @@ pub mod root { [u32; 2usize] ) )); } #[test] - fn __bindgen_test_layout_IntSizeTyped_instantiation_223476() { + fn __bindgen_test_layout_IntSizeTyped_instantiation_193801() { assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32161,7 +32215,7 @@ pub mod root { [u32; 2usize] ) )); } #[test] - fn __bindgen_test_layout_IntRectTyped_instantiation_223484() { + fn __bindgen_test_layout_IntRectTyped_instantiation_193809() { assert_eq!(::std::mem::size_of::<[u32; 4usize]>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32172,7 +32226,7 @@ pub mod root { [u32; 4usize] ) )); } #[test] - fn __bindgen_test_layout_MarginTyped_instantiation_223651() { + fn __bindgen_test_layout_MarginTyped_instantiation_193976() { assert_eq!(::std::mem::size_of::<[u32; 4usize]>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32183,7 +32237,7 @@ pub mod root { [u32; 4usize] ) )); } #[test] - fn __bindgen_test_layout_RectTyped_instantiation_223686() { + fn __bindgen_test_layout_RectTyped_instantiation_194011() { assert_eq!(::std::mem::size_of::<[u32; 4usize]>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32194,7 +32248,7 @@ pub mod root { [u32; 4usize] ) )); } #[test] - fn __bindgen_test_layout_IntRectTyped_instantiation_223691() { + fn __bindgen_test_layout_IntRectTyped_instantiation_194016() { assert_eq!(::std::mem::size_of::<[u32; 4usize]>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32205,7 +32259,7 @@ pub mod root { [u32; 4usize] ) )); } #[test] - fn __bindgen_test_layout_ScaleFactor_instantiation_223737() { + fn __bindgen_test_layout_ScaleFactor_instantiation_194062() { assert_eq!(::std::mem::size_of::<u32>() , 4usize , concat ! ( "Size of template specialization: " , stringify ! ( u32 ) )); @@ -32214,7 +32268,7 @@ pub mod root { u32 ) )); } #[test] - fn __bindgen_test_layout_ScaleFactors2D_instantiation_223837() { + fn __bindgen_test_layout_ScaleFactors2D_instantiation_194162() { assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32225,7 +32279,7 @@ pub mod root { [u32; 2usize] ) )); } #[test] - fn __bindgen_test_layout_ScaleFactors2D_instantiation_223845() { + fn __bindgen_test_layout_ScaleFactors2D_instantiation_194170() { assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32236,7 +32290,7 @@ pub mod root { [u32; 2usize] ) )); } #[test] - fn __bindgen_test_layout_ScaleFactors2D_instantiation_223889() { + fn __bindgen_test_layout_ScaleFactors2D_instantiation_194214() { assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32247,7 +32301,7 @@ pub mod root { [u32; 2usize] ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_224519() { + fn __bindgen_test_layout_nsTArray_instantiation_194844() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::mozilla::FramePropertyTable_PropertyValue>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32260,7 +32314,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_nsPtrHashKey_instantiation_224535() { + fn __bindgen_test_layout_nsPtrHashKey_instantiation_194860() { assert_eq!(::std::mem::size_of::<root::nsPtrHashKey<root::nsIFrame>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32271,7 +32325,7 @@ pub mod root { root::nsPtrHashKey<root::nsIFrame> ) )); } #[test] - fn __bindgen_test_layout_nsPIDOMWindow_instantiation_227807() { + fn __bindgen_test_layout_nsPIDOMWindow_instantiation_197996() { assert_eq!(::std::mem::size_of::<[u64; 28usize]>() , 224usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32282,7 +32336,7 @@ pub mod root { [u64; 28usize] ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_228437() { + fn __bindgen_test_layout_already_AddRefed_instantiation_198626() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::mozilla::dom::CSSValue>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32293,7 +32347,7 @@ pub mod root { root::already_AddRefed<root::mozilla::dom::CSSValue> ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_228528() { + fn __bindgen_test_layout_DefaultDelete_instantiation_198717() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32304,7 +32358,7 @@ pub mod root { root::mozilla::DefaultDelete ) )); } #[test] - fn __bindgen_test_layout_nsRefPtrHashtable_instantiation_228532() { + fn __bindgen_test_layout_nsRefPtrHashtable_instantiation_198721() { assert_eq!(::std::mem::size_of::<[u64; 5usize]>() , 40usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32315,7 +32369,7 @@ pub mod root { [u64; 5usize] ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_229721() { + fn __bindgen_test_layout_already_AddRefed_instantiation_199910() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsISupports>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32326,7 +32380,7 @@ pub mod root { root::already_AddRefed<root::nsISupports> ) )); } #[test] - fn __bindgen_test_layout_nsCOMPtr_instantiation_230007() { + fn __bindgen_test_layout_nsCOMPtr_instantiation_200497() { assert_eq!(::std::mem::size_of::<root::nsCOMPtr>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32337,7 +32391,7 @@ pub mod root { root::nsCOMPtr ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_231597() { + fn __bindgen_test_layout_nsTArray_instantiation_202040() { assert_eq!(::std::mem::size_of::<root::nsTArray<f64>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32348,7 +32402,7 @@ pub mod root { root::nsTArray<f64> ) )); } #[test] - fn __bindgen_test_layout_RefPtr_instantiation_231609() { + fn __bindgen_test_layout_RefPtr_instantiation_202052() { assert_eq!(::std::mem::size_of::<root::RefPtr<root::mozilla::dom::DOMIntersectionObserverEntry>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32361,7 +32415,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_231608() { + fn __bindgen_test_layout_nsTArray_instantiation_202051() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::RefPtr<root::mozilla::dom::DOMIntersectionObserverEntry>>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32374,7 +32428,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_nsPtrHashKey_instantiation_231642() { + fn __bindgen_test_layout_nsPtrHashKey_instantiation_202085() { assert_eq!(::std::mem::size_of::<root::nsPtrHashKey<root::mozilla::dom::Element>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32385,7 +32439,7 @@ pub mod root { root::nsPtrHashKey<root::mozilla::dom::Element> ) )); } #[test] - fn __bindgen_test_layout_UniquePtr_instantiation_231739() { + fn __bindgen_test_layout_UniquePtr_instantiation_202182() { assert_eq!(::std::mem::size_of::<root::mozilla::UniquePtr<root::ProfilerBacktrace>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32396,7 +32450,7 @@ pub mod root { root::mozilla::UniquePtr<root::ProfilerBacktrace> ) )); } #[test] - fn __bindgen_test_layout_nsDataHashtable_instantiation_233499() { + fn __bindgen_test_layout_nsDataHashtable_instantiation_203960() { assert_eq!(::std::mem::size_of::<[u64; 5usize]>() , 40usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32407,7 +32461,7 @@ pub mod root { [u64; 5usize] ) )); } #[test] - fn __bindgen_test_layout_OwningNonNull_instantiation_233538() { + fn __bindgen_test_layout_OwningNonNull_instantiation_203999() { assert_eq!(::std::mem::size_of::<root::mozilla::OwningNonNull<root::mozilla::EffectCompositor_AnimationStyleRuleProcessor>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32420,7 +32474,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_nsRefPtrHashKey_instantiation_233561() { + fn __bindgen_test_layout_nsRefPtrHashKey_instantiation_204022() { assert_eq!(::std::mem::size_of::<root::nsRefPtrHashKey<root::nsIAtom>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32431,7 +32485,7 @@ pub mod root { root::nsRefPtrHashKey<root::nsIAtom> ) )); } #[test] - fn __bindgen_test_layout_nsRefPtrHashKey_instantiation_233597() { + fn __bindgen_test_layout_nsRefPtrHashKey_instantiation_204058() { assert_eq!(::std::mem::size_of::<root::nsRefPtrHashKey<root::nsIContent>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32442,7 +32496,7 @@ pub mod root { root::nsRefPtrHashKey<root::nsIContent> ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_234142() { + fn __bindgen_test_layout_DefaultDelete_instantiation_204603() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32453,7 +32507,7 @@ pub mod root { root::mozilla::DefaultDelete ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_234156() { + fn __bindgen_test_layout_already_AddRefed_instantiation_204617() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::mozilla::URLExtraData>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32464,7 +32518,7 @@ pub mod root { root::already_AddRefed<root::mozilla::URLExtraData> ) )); } #[test] - fn __bindgen_test_layout_nsMainThreadPtrHolder_instantiation_234160() { + fn __bindgen_test_layout_nsMainThreadPtrHolder_instantiation_204621() { assert_eq!(::std::mem::size_of::<root::nsMainThreadPtrHolder<root::nsIURI>>() , 24usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32475,7 +32529,7 @@ pub mod root { root::nsMainThreadPtrHolder<root::nsIURI> ) )); } #[test] - fn __bindgen_test_layout_nsPtrHashKey_instantiation_234234() { + fn __bindgen_test_layout_nsPtrHashKey_instantiation_204695() { assert_eq!(::std::mem::size_of::<root::nsPtrHashKey<root::nsIDocument>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32486,7 +32540,7 @@ pub mod root { root::nsPtrHashKey<root::nsIDocument> ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_234521() { + fn __bindgen_test_layout_DefaultDelete_instantiation_204982() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32497,7 +32551,7 @@ pub mod root { root::mozilla::DefaultDelete ) )); } #[test] - fn __bindgen_test_layout_UniquePtr_instantiation_234519() { + fn __bindgen_test_layout_UniquePtr_instantiation_204980() { assert_eq!(::std::mem::size_of::<root::mozilla::UniquePtr<root::nsCSSValueList>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32508,7 +32562,7 @@ pub mod root { root::mozilla::UniquePtr<root::nsCSSValueList> ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_234527() { + fn __bindgen_test_layout_DefaultDelete_instantiation_204988() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32519,7 +32573,7 @@ pub mod root { root::mozilla::DefaultDelete ) )); } #[test] - fn __bindgen_test_layout_UniquePtr_instantiation_234525() { + fn __bindgen_test_layout_UniquePtr_instantiation_204986() { assert_eq!(::std::mem::size_of::<root::mozilla::UniquePtr<root::nsCSSValuePairList>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32530,7 +32584,7 @@ pub mod root { root::mozilla::UniquePtr<root::nsCSSValuePairList> ) )); } #[test] - fn __bindgen_test_layout_Maybe_instantiation_234797() { + fn __bindgen_test_layout_Maybe_instantiation_205258() { assert_eq!(::std::mem::size_of::<[u64; 2usize]>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32541,7 +32595,7 @@ pub mod root { [u64; 2usize] ) )); } #[test] - fn __bindgen_test_layout_SupportsWeakPtr_instantiation_234964() { + fn __bindgen_test_layout_SupportsWeakPtr_instantiation_205425() { assert_eq!(::std::mem::size_of::<u64>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( u64 ) )); @@ -32550,7 +32604,7 @@ pub mod root { u64 ) )); } #[test] - fn __bindgen_test_layout_Maybe_instantiation_235122() { + fn __bindgen_test_layout_Maybe_instantiation_205583() { assert_eq!(::std::mem::size_of::<[u32; 3usize]>() , 12usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32561,7 +32615,7 @@ pub mod root { [u32; 3usize] ) )); } #[test] - fn __bindgen_test_layout_already_AddRefed_instantiation_235137() { + fn __bindgen_test_layout_already_AddRefed_instantiation_205598() { assert_eq!(::std::mem::size_of::<root::already_AddRefed<root::nsStyleImageRequest>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32572,7 +32626,7 @@ pub mod root { root::already_AddRefed<root::nsStyleImageRequest> ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_235145() { + fn __bindgen_test_layout_DefaultDelete_instantiation_205606() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32583,7 +32637,7 @@ pub mod root { root::mozilla::DefaultDelete ) )); } #[test] - fn __bindgen_test_layout_UniquePtr_instantiation_235143() { + fn __bindgen_test_layout_UniquePtr_instantiation_205604() { assert_eq!(::std::mem::size_of::<root::mozilla::UniquePtr<root::nsStyleSides>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32594,7 +32648,7 @@ pub mod root { root::mozilla::UniquePtr<root::nsStyleSides> ) )); } #[test] - fn __bindgen_test_layout_DefaultDelete_instantiation_235184() { + fn __bindgen_test_layout_DefaultDelete_instantiation_205645() { assert_eq!(::std::mem::size_of::<root::mozilla::DefaultDelete>() , 1usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32605,7 +32659,7 @@ pub mod root { root::mozilla::DefaultDelete ) )); } #[test] - fn __bindgen_test_layout_pair_instantiation_235335() { + fn __bindgen_test_layout_pair_instantiation_205796() { assert_eq!(::std::mem::size_of::<root::std::pair<::nsstring::nsStringRepr, ::nsstring::nsStringRepr>>() , 32usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32616,7 +32670,7 @@ pub mod root { root::std::pair<::nsstring::nsStringRepr, ::nsstring::nsStringRepr> ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_235334() { + fn __bindgen_test_layout_nsTArray_instantiation_205795() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::std::pair<::nsstring::nsStringRepr, ::nsstring::nsStringRepr>>>() , 8usize , concat ! ( @@ -32631,7 +32685,7 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_RefPtr_instantiation_236328() { + fn __bindgen_test_layout_RefPtr_instantiation_206789() { assert_eq!(::std::mem::size_of::<root::RefPtr<root::RawServoAnimationValue>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32642,7 +32696,7 @@ pub mod root { root::RefPtr<root::RawServoAnimationValue> ) )); } #[test] - fn __bindgen_test_layout_BaseTimeDuration_instantiation_240320() { + fn __bindgen_test_layout_BaseTimeDuration_instantiation_208511() { assert_eq!(::std::mem::size_of::<root::mozilla::BaseTimeDuration>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32653,7 +32707,7 @@ pub mod root { root::mozilla::BaseTimeDuration ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_240912() { + fn __bindgen_test_layout_nsTArray_instantiation_209103() { assert_eq!(::std::mem::size_of::<root::nsTArray<root::mozilla::DisplayItemClip_RoundedRect>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32666,7 +32720,18 @@ pub mod root { ) )); } #[test] - fn __bindgen_test_layout_Maybe_instantiation_241088() { + fn __bindgen_test_layout_Maybe_instantiation_209359() { + assert_eq!(::std::mem::size_of::<[u64; 2usize]>() , 16usize , concat ! + ( + "Size of template specialization: " , stringify ! ( + [u64; 2usize] ) )); + assert_eq!(::std::mem::align_of::<[u64; 2usize]>() , 8usize , concat ! + ( + "Alignment of template specialization: " , stringify ! ( + [u64; 2usize] ) )); + } + #[test] + fn __bindgen_test_layout_Maybe_instantiation_209366() { assert_eq!(::std::mem::size_of::<[u64; 5usize]>() , 40usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32677,7 +32742,7 @@ pub mod root { [u64; 5usize] ) )); } #[test] - fn __bindgen_test_layout_RefPtr_instantiation_241263() { + fn __bindgen_test_layout_RefPtr_instantiation_209541() { assert_eq!(::std::mem::size_of::<root::RefPtr<root::mozilla::dom::DOMRect>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32688,7 +32753,7 @@ pub mod root { root::RefPtr<root::mozilla::dom::DOMRect> ) )); } #[test] - fn __bindgen_test_layout_Sequence_instantiation_241507() { + fn __bindgen_test_layout_Sequence_instantiation_209785() { assert_eq!(::std::mem::size_of::<u64>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( u64 ) )); @@ -32697,7 +32762,7 @@ pub mod root { u64 ) )); } #[test] - fn __bindgen_test_layout_nsRefPtrHashKey_instantiation_241806() { + fn __bindgen_test_layout_nsRefPtrHashKey_instantiation_210084() { assert_eq!(::std::mem::size_of::<root::nsRefPtrHashKey<root::mozilla::dom::Element>>() , 16usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32708,7 +32773,7 @@ pub mod root { root::nsRefPtrHashKey<root::mozilla::dom::Element> ) )); } #[test] - fn __bindgen_test_layout_nsClassHashtable_instantiation_241805() { + fn __bindgen_test_layout_nsClassHashtable_instantiation_210083() { assert_eq!(::std::mem::size_of::<[u64; 5usize]>() , 40usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32719,7 +32784,7 @@ pub mod root { [u64; 5usize] ) )); } #[test] - fn __bindgen_test_layout_nsTArray_instantiation_243035() { + fn __bindgen_test_layout_nsTArray_instantiation_211313() { assert_eq!(::std::mem::size_of::<root::nsTArray<*mut root::mozilla::css::DocumentRule>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( @@ -32730,7 +32795,7 @@ pub mod root { root::nsTArray<*mut root::mozilla::css::DocumentRule> ) )); } #[test] - fn __bindgen_test_layout_nsAutoPtr_instantiation_243071() { + fn __bindgen_test_layout_nsAutoPtr_instantiation_211349() { assert_eq!(::std::mem::size_of::<root::nsAutoPtr<root::nsMediaQuery>>() , 8usize , concat ! ( "Size of template specialization: " , stringify ! ( diff --git a/components/style/gecko/global_style_data.rs b/components/style/gecko/global_style_data.rs index 3cbace9b90a..2ba8749e24f 100644 --- a/components/style/gecko/global_style_data.rs +++ b/components/style/gecko/global_style_data.rs @@ -57,12 +57,25 @@ lazy_static! { pub static ref GLOBAL_STYLE_DATA: GlobalStyleData = { let stylo_threads = env::var("STYLO_THREADS") .map(|s| s.parse::<usize>().expect("invalid STYLO_THREADS value")); - let num_threads = match stylo_threads { + let mut num_threads = match stylo_threads { Ok(num) => num, _ => cmp::max(num_cpus::get() * 3 / 4, 1), }; - let pool = if num_threads <= 1 { + // If num_threads is one, there's no point in creating a thread pool, so + // force it to zero. + // + // We allow developers to force a one-thread pool for testing via a + // special environmental variable. + if num_threads == 1 { + let force_pool = env::var("FORCE_STYLO_THREAD_POOL") + .ok().map_or(false, |s| s.parse::<usize>().expect("invalid FORCE_STYLO_THREAD_POOL value") == 1); + if !force_pool { + num_threads = 0; + } + } + + let pool = if num_threads < 1 { None } else { let configuration = rayon::Configuration::new() diff --git a/components/style/gecko/non_ts_pseudo_class_list.rs b/components/style/gecko/non_ts_pseudo_class_list.rs index 5b1b056ba91..f6551ce90d4 100644 --- a/components/style/gecko/non_ts_pseudo_class_list.rs +++ b/components/style/gecko/non_ts_pseudo_class_list.rs @@ -25,8 +25,6 @@ * * Pending pseudo-classes: * - * :-moz-is-html -> Used only in UA sheets, should be easy to support. - * * :-moz-lwtheme, :-moz-lwtheme-brighttext, :-moz-lwtheme-darktext, * :-moz-window-inactive. * @@ -108,6 +106,7 @@ macro_rules! apply_non_ts_list { ("-moz-last-node", MozLastNode, lastNode, _, _), ("-moz-only-whitespace", MozOnlyWhitespace, mozOnlyWhitespace, _, _), ("-moz-native-anonymous", MozNativeAnonymous, mozNativeAnonymous, _, PSEUDO_CLASS_INTERNAL), + ("-moz-is-html", MozIsHTML, mozIsHTML, _, _), ], string: [ ("-moz-system-metric", MozSystemMetric, mozSystemMetric, _, PSEUDO_CLASS_INTERNAL), diff --git a/components/style/gecko/pseudo_element.rs b/components/style/gecko/pseudo_element.rs index f01c182802e..b15e17e6f38 100644 --- a/components/style/gecko/pseudo_element.rs +++ b/components/style/gecko/pseudo_element.rs @@ -10,12 +10,24 @@ use cssparser::ToCss; use gecko_bindings::structs::{self, CSSPseudoElementType}; -use selector_parser::PseudoElementCascadeType; +use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl}; use std::fmt; use string_cache::Atom; include!(concat!(env!("OUT_DIR"), "/gecko/pseudo_element_definition.rs")); +impl ::selectors::parser::PseudoElement for PseudoElement { + type Impl = SelectorImpl; + + fn supports_pseudo_class(&self, pseudo_class: &NonTSPseudoClass) -> bool { + if !self.supports_user_action_state() { + return false; + } + + return pseudo_class.is_safe_user_action_state(); + } +} + impl PseudoElement { /// Returns the kind of cascade type that a given pseudo is going to use. /// diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index bed3737db99..787ecd5b2cb 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -5,7 +5,6 @@ //! Gecko-specific bits for selector-parsing. use cssparser::{Parser, ToCss}; -use element_state::{IN_ACTIVE_STATE, IN_FOCUS_STATE, IN_HOVER_STATE}; use element_state::ElementState; use gecko_bindings::structs::CSSPseudoClassType; use selector_parser::{SelectorParser, PseudoElementCascadeType}; @@ -132,7 +131,7 @@ impl NonTSPseudoClass { /// https://drafts.csswg.org/selectors-4/#useraction-pseudos /// /// We intentionally skip the link-related ones. - fn is_safe_user_action_state(&self) -> bool { + pub fn is_safe_user_action_state(&self) -> bool { matches!(*self, NonTSPseudoClass::Hover | NonTSPseudoClass::Active | NonTSPseudoClass::Focus) @@ -159,8 +158,15 @@ impl NonTSPseudoClass { /// Returns true if the given pseudoclass should trigger style sharing cache revalidation. pub fn needs_cache_revalidation(&self) -> bool { + // :dir() depends on state only, but doesn't use state_flag because its + // semantics don't quite match. Nevertheless, it doesn't need cache + // revalidation, because we already compare states for elements and + // candidates. self.state_flag().is_empty() && - !matches!(*self, NonTSPseudoClass::MozAny(_)) + !matches!(*self, + NonTSPseudoClass::MozAny(_) | + NonTSPseudoClass::Dir(_) | + NonTSPseudoClass::MozIsHTML) } /// Convert NonTSPseudoClass to Gecko's CSSPseudoClassType. @@ -188,58 +194,6 @@ impl NonTSPseudoClass { #[derive(Clone, Debug, PartialEq, Eq)] pub struct SelectorImpl; -/// Some subset of pseudo-elements in Gecko are sensitive to some state -/// selectors. -/// -/// We store the sensitive states in this struct in order to properly handle -/// these. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct PseudoElementSelector { - pseudo: PseudoElement, - state: ElementState, -} - -impl PseudoElementSelector { - /// Returns the pseudo-element this selector represents. - pub fn pseudo_element(&self) -> &PseudoElement { - &self.pseudo - } - - /// Returns the pseudo-element selector state. - pub fn state(&self) -> ElementState { - self.state - } -} - -impl ToCss for PseudoElementSelector { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result - where W: fmt::Write, - { - if cfg!(debug_assertions) { - let mut state = self.state; - state.remove(IN_HOVER_STATE | IN_ACTIVE_STATE | IN_FOCUS_STATE); - assert_eq!(state, ElementState::empty(), - "Unhandled pseudo-element state selector?"); - } - - self.pseudo.to_css(dest)?; - - if self.state.contains(IN_HOVER_STATE) { - dest.write_str(":hover")? - } - - if self.state.contains(IN_ACTIVE_STATE) { - dest.write_str(":active")? - } - - if self.state.contains(IN_FOCUS_STATE) { - dest.write_str(":focus")? - } - - Ok(()) - } -} - impl ::selectors::SelectorImpl for SelectorImpl { type AttrValue = Atom; type Identifier = Atom; @@ -250,7 +204,7 @@ impl ::selectors::SelectorImpl for SelectorImpl { type BorrowedNamespaceUrl = WeakNamespace; type BorrowedLocalName = WeakAtom; - type PseudoElementSelector = PseudoElementSelector; + type PseudoElement = PseudoElement; type NonTSPseudoClass = NonTSPseudoClass; } @@ -312,38 +266,9 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> { } } - fn parse_pseudo_element(&self, name: Cow<str>, input: &mut Parser) -> Result<PseudoElementSelector, ()> { - let pseudo = - match PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) { - Some(pseudo) => pseudo, - None => return Err(()), - }; - - let state = if pseudo.supports_user_action_state() { - input.try(|input| { - let mut state = ElementState::empty(); - - while !input.is_exhausted() { - input.expect_colon()?; - let ident = input.expect_ident()?; - let pseudo_class = self.parse_non_ts_pseudo_class(ident)?; - - if !pseudo_class.is_safe_user_action_state() { - return Err(()) - } - state.insert(pseudo_class.state_flag()); - } - - Ok(state) - }).ok() - } else { - None - }; - - Ok(PseudoElementSelector { - pseudo: pseudo, - state: state.unwrap_or(ElementState::empty()), - }) + fn parse_pseudo_element(&self, name: Cow<str>) -> Result<PseudoElement, ()> { + PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) + .ok_or(()) } fn default_namespace(&self) -> Option<Namespace> { diff --git a/components/style/gecko/snapshot.rs b/components/style/gecko/snapshot.rs index ea4871bc2a7..4a79b492996 100644 --- a/components/style/gecko/snapshot.rs +++ b/components/style/gecko/snapshot.rs @@ -8,15 +8,14 @@ use dom::TElement; use element_state::ElementState; use gecko::snapshot_helpers; -use gecko::wrapper::{AttrSelectorHelpers, GeckoElement}; +use gecko::wrapper::{NamespaceConstraintHelpers, GeckoElement}; use gecko_bindings::bindings; use gecko_bindings::structs::ServoElementSnapshot; use gecko_bindings::structs::ServoElementSnapshotFlags as Flags; use gecko_bindings::structs::ServoElementSnapshotTable; use restyle_hints::ElementSnapshot; -use selector_parser::SelectorImpl; -use selectors::parser::AttrSelector; -use string_cache::Atom; +use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint}; +use string_cache::{Atom, Namespace}; /// A snapshot of a Gecko element. pub type GeckoElementSnapshot = ServoElementSnapshot; @@ -48,11 +47,6 @@ impl SnapshotMap { impl GeckoElementSnapshot { #[inline] - fn is_html_element_in_html_document(&self) -> bool { - self.mIsHTMLElementInHTMLDocument - } - - #[inline] fn has_any(&self, flags: Flags) -> bool { (self.mContains as u8 & flags as u8) != 0 } @@ -60,76 +54,67 @@ impl GeckoElementSnapshot { fn as_ptr(&self) -> *const Self { self } -} - -impl ::selectors::MatchAttr for GeckoElementSnapshot { - type Impl = SelectorImpl; - - fn match_attr_has(&self, attr: &AttrSelector<SelectorImpl>) -> bool { - unsafe { - bindings::Gecko_SnapshotHasAttr(self, - attr.ns_or_null(), - attr.select_name(self.is_html_element_in_html_document())) - } - } - fn match_attr_equals(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool { - unsafe { - bindings::Gecko_SnapshotAttrEquals(self, - attr.ns_or_null(), - attr.select_name(self.is_html_element_in_html_document()), - value.as_ptr(), - /* ignoreCase = */ false) - } - } - - fn match_attr_equals_ignore_ascii_case(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool { - unsafe { - bindings::Gecko_SnapshotAttrEquals(self, - attr.ns_or_null(), - attr.select_name(self.is_html_element_in_html_document()), - value.as_ptr(), - /* ignoreCase = */ true) - } - } - fn match_attr_includes(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool { - unsafe { - bindings::Gecko_SnapshotAttrIncludes(self, - attr.ns_or_null(), - attr.select_name(self.is_html_element_in_html_document()), - value.as_ptr()) - } - } - fn match_attr_dash(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool { + /// selectors::Element::attr_matches + pub fn attr_matches(&self, + ns: &NamespaceConstraint<&Namespace>, + local_name: &Atom, + operation: &AttrSelectorOperation<&Atom>) + -> bool { unsafe { - bindings::Gecko_SnapshotAttrDashEquals(self, - attr.ns_or_null(), - attr.select_name(self.is_html_element_in_html_document()), - value.as_ptr()) - } - } - fn match_attr_prefix(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool { - unsafe { - bindings::Gecko_SnapshotAttrHasPrefix(self, - attr.ns_or_null(), - attr.select_name(self.is_html_element_in_html_document()), - value.as_ptr()) - } - } - fn match_attr_substring(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool { - unsafe { - bindings::Gecko_SnapshotAttrHasSubstring(self, - attr.ns_or_null(), - attr.select_name(self.is_html_element_in_html_document()), - value.as_ptr()) - } - } - fn match_attr_suffix(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool { - unsafe { - bindings::Gecko_SnapshotAttrHasSuffix(self, - attr.ns_or_null(), - attr.select_name(self.is_html_element_in_html_document()), - value.as_ptr()) + match *operation { + AttrSelectorOperation::Exists => { + bindings:: Gecko_SnapshotHasAttr(self, + ns.atom_or_null(), + local_name.as_ptr()) + } + AttrSelectorOperation::WithValue { operator, case_sensitivity, expected_value } => { + let ignore_case = match case_sensitivity { + CaseSensitivity::CaseSensitive => false, + CaseSensitivity::AsciiCaseInsensitive => true, + }; + // FIXME: case sensitivity for operators other than Equal + match operator { + AttrSelectorOperator::Equal => bindings::Gecko_SnapshotAttrEquals( + self, + ns.atom_or_null(), + local_name.as_ptr(), + expected_value.as_ptr(), + ignore_case + ), + AttrSelectorOperator::Includes => bindings::Gecko_SnapshotAttrIncludes( + self, + ns.atom_or_null(), + local_name.as_ptr(), + expected_value.as_ptr(), + ), + AttrSelectorOperator::DashMatch => bindings::Gecko_SnapshotAttrDashEquals( + self, + ns.atom_or_null(), + local_name.as_ptr(), + expected_value.as_ptr(), + ), + AttrSelectorOperator::Prefix => bindings::Gecko_SnapshotAttrHasPrefix( + self, + ns.atom_or_null(), + local_name.as_ptr(), + expected_value.as_ptr(), + ), + AttrSelectorOperator::Suffix => bindings::Gecko_SnapshotAttrHasSuffix( + self, + ns.atom_or_null(), + local_name.as_ptr(), + expected_value.as_ptr(), + ), + AttrSelectorOperator::Substring => bindings::Gecko_SnapshotAttrHasSubstring( + self, + ns.atom_or_null(), + local_name.as_ptr(), + expected_value.as_ptr(), + ), + } + } + } } } } @@ -148,7 +133,12 @@ impl ElementSnapshot for GeckoElementSnapshot { self.has_any(Flags::Attributes) } + #[inline] fn id_attr(&self) -> Option<Atom> { + if !self.has_any(Flags::Id) { + return None + } + let ptr = unsafe { bindings::Gecko_SnapshotAtomAttrValue(self, atom!("id").as_ptr()) @@ -161,15 +151,25 @@ impl ElementSnapshot for GeckoElementSnapshot { } } + #[inline] fn has_class(&self, name: &Atom) -> bool { + if !self.has_any(Flags::MaybeClass) { + return false; + } + snapshot_helpers::has_class(self.as_ptr(), name, bindings::Gecko_SnapshotClassOrClassList) } + #[inline] fn each_class<F>(&self, callback: F) where F: FnMut(&Atom) { + if !self.has_any(Flags::MaybeClass) { + return; + } + snapshot_helpers::each_class(self.as_ptr(), callback, bindings::Gecko_SnapshotClassOrClassList) diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 41224c0b403..3d8ab270658 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -46,7 +46,7 @@ use gecko_bindings::bindings::Gecko_MatchStringArgPseudo; use gecko_bindings::bindings::Gecko_UpdateAnimations; use gecko_bindings::structs; use gecko_bindings::structs::{RawGeckoElement, RawGeckoNode}; -use gecko_bindings::structs::{nsIAtom, nsIContent, nsStyleContext}; +use gecko_bindings::structs::{nsIAtom, nsIContent, nsINode_BooleanFlag, nsStyleContext}; use gecko_bindings::structs::ELEMENT_HANDLED_SNAPSHOT; use gecko_bindings::structs::ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO; use gecko_bindings::structs::ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO; @@ -64,8 +64,8 @@ use properties::style_structs::Font; use rule_tree::CascadeLevel as ServoCascadeLevel; use selector_parser::ElementExt; use selectors::Element; -use selectors::matching::{ElementSelectorFlags, StyleRelations}; -use selectors::parser::{AttrSelector, NamespaceConstraint}; +use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint}; +use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode}; use shared_lock::Locked; use sink::Push; use std::cell::RefCell; @@ -104,14 +104,17 @@ impl<'ln> fmt::Debug for GeckoNode<'ln> { } impl<'ln> GeckoNode<'ln> { + #[inline] fn from_content(content: &'ln nsIContent) -> Self { GeckoNode(&content._base) } + #[inline] fn flags(&self) -> u32 { (self.0)._base._base_1.mFlags } + #[inline] fn node_info(&self) -> &structs::NodeInfo { debug_assert!(!self.0.mNodeInfo.mRawPtr.is_null()); unsafe { &*self.0.mNodeInfo.mRawPtr } @@ -119,32 +122,43 @@ impl<'ln> GeckoNode<'ln> { // These live in different locations depending on processor architecture. #[cfg(target_pointer_width = "64")] + #[inline] fn bool_flags(&self) -> u32 { (self.0)._base._base_1.mBoolFlags } #[cfg(target_pointer_width = "32")] + #[inline] fn bool_flags(&self) -> u32 { (self.0).mBoolFlags } + #[inline] + fn get_bool_flag(&self, flag: nsINode_BooleanFlag) -> bool { + self.bool_flags() & (1u32 << flag as u32) != 0 + } + fn owner_doc(&self) -> &structs::nsIDocument { debug_assert!(!self.node_info().mDocument.is_null()); unsafe { &*self.node_info().mDocument } } + #[inline] fn first_child(&self) -> Option<GeckoNode<'ln>> { unsafe { self.0.mFirstChild.as_ref().map(GeckoNode::from_content) } } + #[inline] fn last_child(&self) -> Option<GeckoNode<'ln>> { unsafe { Gecko_GetLastChild(self.0).map(GeckoNode) } } + #[inline] fn prev_sibling(&self) -> Option<GeckoNode<'ln>> { unsafe { self.0.mPreviousSibling.as_ref().map(GeckoNode::from_content) } } + #[inline] fn next_sibling(&self) -> Option<GeckoNode<'ln>> { unsafe { self.0.mNextSibling.as_ref().map(GeckoNode::from_content) } } @@ -186,9 +200,9 @@ impl<'ln> GeckoNode<'ln> { } impl<'ln> NodeInfo for GeckoNode<'ln> { + #[inline] fn is_element(&self) -> bool { - use gecko_bindings::structs::nsINode_BooleanFlag; - self.bool_flags() & (1u32 << nsINode_BooleanFlag::NodeIsElement as u32) != 0 + self.get_bool_flag(nsINode_BooleanFlag::NodeIsElement) } fn is_text_node(&self) -> bool { @@ -408,10 +422,24 @@ impl<'le> GeckoElement<'le> { } } + #[inline] fn may_have_animations(&self) -> bool { - use gecko_bindings::structs::nsINode_BooleanFlag; - self.as_node().bool_flags() & - (1u32 << nsINode_BooleanFlag::ElementHasAnimations as u32) != 0 + self.as_node().get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations) + } + + #[inline] + fn has_id(&self) -> bool { + self.as_node().get_bool_flag(nsINode_BooleanFlag::ElementHasID) + } + + #[inline] + fn may_have_class(&self) -> bool { + self.as_node().get_bool_flag(nsINode_BooleanFlag::ElementMayHaveClass) + } + + #[inline] + fn may_have_style_attribute(&self) -> bool { + self.as_node().get_bool_flag(nsINode_BooleanFlag::ElementMayHaveStyle) } } @@ -564,8 +592,12 @@ impl<'le> TElement for GeckoElement<'le> { } fn style_attribute(&self) -> Option<&Arc<Locked<PropertyDeclarationBlock>>> { + if !self.may_have_style_attribute() { + return None; + } + let declarations = unsafe { Gecko_GetStyleAttrDeclarationBlock(self.0) }; - declarations.map(|s| s.as_arc_opt()).unwrap_or(None) + declarations.map_or(None, |s| s.as_arc_opt()) } fn get_smil_override(&self) -> Option<&Arc<Locked<PropertyDeclarationBlock>>> { @@ -623,8 +655,16 @@ impl<'le> TElement for GeckoElement<'le> { } } + fn each_class<F>(&self, callback: F) + where F: FnMut(&Atom) + { + snapshot_helpers::each_class(self.0, + callback, + Gecko_ClassOrClassList) + } + fn existing_style_for_restyle_damage<'a>(&'a self, - _existing_values: &'a Arc<ComputedValues>, + _existing_values: &'a ComputedValues, pseudo: Option<&PseudoElement>) -> Option<&'a nsStyleContext> { // TODO(emilio): Migrate this to CSSPseudoElementType. @@ -1050,11 +1090,18 @@ impl<'le> PresentationalHintsSynthesizer for GeckoElement<'le> { } impl<'le> ::selectors::Element for GeckoElement<'le> { + type Impl = SelectorImpl; + fn parent_element(&self) -> Option<Self> { let parent_node = self.as_node().parent_node(); parent_node.and_then(|n| n.as_element()) } + fn pseudo_element_originating_element(&self) -> Option<Self> { + debug_assert!(self.implemented_pseudo_element().is_some()); + self.closest_non_native_anonymous_ancestor() + } + fn first_child_element(&self) -> Option<Self> { let mut child = self.as_node().first_child(); while let Some(child_node) = child { @@ -1099,6 +1146,68 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { None } + fn attr_matches(&self, + ns: &NamespaceConstraint<&Namespace>, + local_name: &Atom, + operation: &AttrSelectorOperation<&Atom>) + -> bool { + unsafe { + match *operation { + AttrSelectorOperation::Exists => { + bindings::Gecko_HasAttr(self.0, + ns.atom_or_null(), + local_name.as_ptr()) + } + AttrSelectorOperation::WithValue { operator, case_sensitivity, expected_value } => { + let ignore_case = match case_sensitivity { + CaseSensitivity::CaseSensitive => false, + CaseSensitivity::AsciiCaseInsensitive => true, + }; + // FIXME: case sensitivity for operators other than Equal + match operator { + AttrSelectorOperator::Equal => bindings::Gecko_AttrEquals( + self.0, + ns.atom_or_null(), + local_name.as_ptr(), + expected_value.as_ptr(), + ignore_case + ), + AttrSelectorOperator::Includes => bindings::Gecko_AttrIncludes( + self.0, + ns.atom_or_null(), + local_name.as_ptr(), + expected_value.as_ptr(), + ), + AttrSelectorOperator::DashMatch => bindings::Gecko_AttrDashEquals( + self.0, + ns.atom_or_null(), + local_name.as_ptr(), + expected_value.as_ptr(), + ), + AttrSelectorOperator::Prefix => bindings::Gecko_AttrHasPrefix( + self.0, + ns.atom_or_null(), + local_name.as_ptr(), + expected_value.as_ptr(), + ), + AttrSelectorOperator::Suffix => bindings::Gecko_AttrHasSuffix( + self.0, + ns.atom_or_null(), + local_name.as_ptr(), + expected_value.as_ptr(), + ), + AttrSelectorOperator::Substring => bindings::Gecko_AttrHasSubstring( + self.0, + ns.atom_or_null(), + local_name.as_ptr(), + expected_value.as_ptr(), + ), + } + } + } + } + } + fn is_root(&self) -> bool { unsafe { Gecko_IsRootElement(self.0) @@ -1125,7 +1234,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { fn match_non_ts_pseudo_class<F>(&self, pseudo_class: &NonTSPseudoClass, - relations: &mut StyleRelations, + context: &mut MatchingContext, flags_setter: &mut F) -> bool where F: FnMut(&Self, ElementSelectorFlags), @@ -1217,9 +1326,12 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { NonTSPseudoClass::MozNativeAnonymous => unsafe { Gecko_MatchesElement(pseudo_class.to_gecko_pseudoclasstype().unwrap(), self.0) }, + NonTSPseudoClass::MozIsHTML => { + self.is_html_element_in_html_document() + } NonTSPseudoClass::MozAny(ref sels) => { sels.iter().any(|s| { - matches_complex_selector(s, self, relations, flags_setter) + matches_complex_selector(s, self, context, flags_setter) }) } NonTSPseudoClass::MozSystemMetric(ref s) | @@ -1241,7 +1353,25 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { } } + fn match_pseudo_element(&self, + pseudo_element: &PseudoElement, + _context: &mut MatchingContext) + -> bool + { + // TODO(emilio): I believe we could assert we are a pseudo-element and + // match the proper pseudo-element, given how we rulehash the stuff + // based on the pseudo. + match self.implemented_pseudo_element() { + Some(ref pseudo) => pseudo == pseudo_element, + None => false, + } + } + fn get_id(&self) -> Option<Atom> { + if !self.has_id() { + return None; + } + let ptr = unsafe { bindings::Gecko_AtomAttrValue(self.0, atom!("id").as_ptr()) @@ -1255,19 +1385,15 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { } fn has_class(&self, name: &Atom) -> bool { + if !self.may_have_class() { + return false; + } + snapshot_helpers::has_class(self.0, name, Gecko_ClassOrClassList) } - fn each_class<F>(&self, callback: F) - where F: FnMut(&Atom) - { - snapshot_helpers::each_class(self.0, - callback, - Gecko_ClassOrClassList) - } - fn is_html_element_in_html_document(&self) -> bool { let node = self.as_node(); let node_info = node.node_info(); @@ -1277,97 +1403,16 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { } /// A few helpers to help with attribute selectors and snapshotting. -pub trait AttrSelectorHelpers { +pub trait NamespaceConstraintHelpers { /// Returns the namespace of the selector, or null otherwise. - fn ns_or_null(&self) -> *mut nsIAtom; - /// Returns the proper selector name depending on whether the requesting - /// element is an HTML element in an HTML document or not. - fn select_name(&self, is_html_element_in_html_document: bool) -> *mut nsIAtom; + fn atom_or_null(&self) -> *mut nsIAtom; } -impl AttrSelectorHelpers for AttrSelector<SelectorImpl> { - fn ns_or_null(&self) -> *mut nsIAtom { - match self.namespace { +impl<'a> NamespaceConstraintHelpers for NamespaceConstraint<&'a Namespace> { + fn atom_or_null(&self) -> *mut nsIAtom { + match *self { NamespaceConstraint::Any => ptr::null_mut(), - NamespaceConstraint::Specific(ref ns) => ns.url.0.as_ptr(), - } - } - - fn select_name(&self, is_html_element_in_html_document: bool) -> *mut nsIAtom { - if is_html_element_in_html_document { - self.lower_name.as_ptr() - } else { - self.name.as_ptr() - } - } -} - -impl<'le> ::selectors::MatchAttr for GeckoElement<'le> { - type Impl = SelectorImpl; - - fn match_attr_has(&self, attr: &AttrSelector<Self::Impl>) -> bool { - unsafe { - bindings::Gecko_HasAttr(self.0, - attr.ns_or_null(), - attr.select_name(self.is_html_element_in_html_document())) - } - } - fn match_attr_equals(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool { - unsafe { - bindings::Gecko_AttrEquals(self.0, - attr.ns_or_null(), - attr.select_name(self.is_html_element_in_html_document()), - value.as_ptr(), - /* ignoreCase = */ false) - } - } - fn match_attr_equals_ignore_ascii_case(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool { - unsafe { - bindings::Gecko_AttrEquals(self.0, - attr.ns_or_null(), - attr.select_name(self.is_html_element_in_html_document()), - value.as_ptr(), - /* ignoreCase = */ false) - } - } - fn match_attr_includes(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool { - unsafe { - bindings::Gecko_AttrIncludes(self.0, - attr.ns_or_null(), - attr.select_name(self.is_html_element_in_html_document()), - value.as_ptr()) - } - } - fn match_attr_dash(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool { - unsafe { - bindings::Gecko_AttrDashEquals(self.0, - attr.ns_or_null(), - attr.select_name(self.is_html_element_in_html_document()), - value.as_ptr()) - } - } - fn match_attr_prefix(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool { - unsafe { - bindings::Gecko_AttrHasPrefix(self.0, - attr.ns_or_null(), - attr.select_name(self.is_html_element_in_html_document()), - value.as_ptr()) - } - } - fn match_attr_substring(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool { - unsafe { - bindings::Gecko_AttrHasSubstring(self.0, - attr.ns_or_null(), - attr.select_name(self.is_html_element_in_html_document()), - value.as_ptr()) - } - } - fn match_attr_suffix(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool { - unsafe { - bindings::Gecko_AttrHasSuffix(self.0, - attr.ns_or_null(), - attr.select_name(self.is_html_element_in_html_document()), - value.as_ptr()) + NamespaceConstraint::Specific(ref ns) => ns.0.as_ptr(), } } } @@ -1375,8 +1420,9 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> { impl<'le> ElementExt for GeckoElement<'le> { #[inline] fn is_link(&self) -> bool { + let mut context = MatchingContext::new(MatchingMode::Normal, None); self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink, - &mut StyleRelations::empty(), + &mut context, &mut |_, _| {}) } diff --git a/components/style/lib.rs b/components/style/lib.rs index 91c8727a2e7..4210304217f 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -50,6 +50,7 @@ extern crate fnv; #[cfg(feature = "gecko")] #[macro_use] pub mod gecko_string_cache; #[cfg(feature = "servo")] extern crate heapsize; #[cfg(feature = "servo")] #[macro_use] extern crate heapsize_derive; +extern crate itoa; #[cfg(feature = "servo")] #[macro_use] extern crate html5ever; #[macro_use] extern crate lazy_static; diff --git a/components/style/matching.rs b/components/style/matching.rs index 764f58ec080..289a6fb1975 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -15,7 +15,6 @@ use cascade_info::CascadeInfo; use context::{CurrentElementInfo, SelectorFlagsMap, SharedStyleContext, StyleContext}; use data::{ComputedStyle, ElementData, ElementStyles, RestyleData}; use dom::{AnimationRules, SendElement, TElement, TNode}; -use element_state::ElementState; use font_metrics::FontMetricsProvider; use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade}; use properties::longhands::display::computed_value as display; @@ -24,7 +23,7 @@ use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_SMIL}; use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode}; use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl}; use selectors::bloom::BloomFilter; -use selectors::matching::{ElementSelectorFlags, StyleRelations}; +use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, StyleRelations}; use selectors::matching::AFFECTED_BY_PSEUDO_ELEMENTS; use shared_lock::StylesheetGuards; use sink::ForgetfulSink; @@ -497,6 +496,27 @@ trait PrivateMatchMethods: TElement { primary_style: &ComputedStyle, eager_pseudo_style: Option<&ComputedStyle>) -> Arc<ComputedValues> { + if let Some(pseudo) = self.implemented_pseudo_element() { + debug_assert!(eager_pseudo_style.is_none()); + + // This is an element-backed pseudo, just grab the styles from the + // parent if it's eager, and recascade otherwise. + // + // We also recascade if the eager pseudo-style has any animation + // rules, because we don't cascade those during the eager traversal. + // + // We could make that a bit better if the complexity cost is not too + // big, but given further restyles are posted directly to + // pseudo-elements, it doesn't seem worth the effort at a glance. + if pseudo.is_eager() && self.get_animation_rules().is_empty() { + let parent = self.parent_element().unwrap(); + let parent_data = parent.borrow_data().unwrap(); + let pseudo_style = + parent_data.styles().pseudos.get(&pseudo).unwrap(); + return pseudo_style.values().clone() + } + } + // Grab the rule node. let rule_node = &eager_pseudo_style.unwrap_or(primary_style).rules; let inherit_mode = if eager_pseudo_style.is_some() { @@ -514,97 +534,66 @@ trait PrivateMatchMethods: TElement { /// Computes values and damage for the primary or pseudo style of an element, /// setting them on the ElementData. - fn cascade_primary_or_pseudo(&self, - context: &mut StyleContext<Self>, - data: &mut ElementData, - pseudo: Option<&PseudoElement>) { - debug_assert!(pseudo.is_none() || self.implemented_pseudo_element().is_none(), - "Pseudo-element-implementing elements can't have pseudos!"); + fn cascade_primary(&self, + context: &mut StyleContext<Self>, + data: &mut ElementData) { // Collect some values. let (mut styles, restyle) = data.styles_and_restyle_mut(); let mut primary_style = &mut styles.primary; - let pseudos = &mut styles.pseudos; - let mut pseudo_style = match pseudo { - Some(p) => { - let style = pseudos.get_mut(p); - debug_assert!(style.is_some()); - style - } - None => None, - }; - - let mut old_values = match pseudo_style { - Some(ref mut s) => s.values.take(), - None => primary_style.values.take(), - }; + let mut old_values = primary_style.values.take(); // Compute the new values. - let mut new_values = match self.implemented_pseudo_element() { - Some(ref pseudo) => { - // This is an element-backed pseudo, just grab the styles from - // the parent if it's eager, and recascade otherwise. - // - // We also recascade if the eager pseudo-style has any animation - // rules, because we don't cascade those during the eager - // traversal. We could make that a bit better if the complexity - // cost is not too big, but given further restyles are posted - // directly to pseudo-elements, it doesn't seem worth the effort - // at a glance. - if pseudo.is_eager() && - self.get_animation_rules().is_empty() { - let parent = self.parent_element().unwrap(); - - let parent_data = parent.borrow_data().unwrap(); - let pseudo_style = - parent_data.styles().pseudos.get(pseudo).unwrap(); - pseudo_style.values().clone() - } else { - self.cascade_internal(context, - primary_style, - None) - } - } - None => { - // Else it's an eager pseudo or a normal element, do the cascade - // work. - self.cascade_internal(context, - primary_style, - pseudo_style.as_ref().map(|s| &**s)) - } - }; + let mut new_values = self.cascade_internal(context, primary_style, None); // NB: Animations for pseudo-elements in Gecko are handled while // traversing the pseudo-elements themselves. - if pseudo.is_none() && - !context.shared.traversal_flags.for_animation_only() { + if !context.shared.traversal_flags.for_animation_only() { self.process_animations(context, &mut old_values, &mut new_values, primary_style); } - // Accumulate restyle damage. + if let Some(old) = old_values { + self.accumulate_damage(&context.shared, + restyle.unwrap(), + &old, + &new_values, + None); + } + + // Set the new computed values. + primary_style.values = Some(new_values); + } + + fn cascade_eager_pseudo(&self, + context: &mut StyleContext<Self>, + data: &mut ElementData, + pseudo: &PseudoElement) { + debug_assert!(pseudo.is_eager()); + let (mut styles, restyle) = data.styles_and_restyle_mut(); + let mut pseudo_style = styles.pseudos.get_mut(pseudo).unwrap(); + let old_values = pseudo_style.values.take(); + + let new_values = + self.cascade_internal(context, &styles.primary, Some(pseudo_style)); + if let Some(old) = old_values { // ::before and ::after are element-backed in Gecko, so they do // the damage calculation for themselves. - // - // FIXME(emilio): We have more element-backed stuff, and this is - // redundant for them right now. - if cfg!(feature = "servo") || - pseudo.map_or(true, |p| !p.is_before_or_after()) { + if cfg!(feature = "servo") || !pseudo.is_before_or_after() { self.accumulate_damage(&context.shared, restyle.unwrap(), &old, &new_values, - pseudo); + Some(pseudo)); } } - // Set the new computed values. - let mut relevant_style = pseudo_style.unwrap_or(primary_style); - relevant_style.values = Some(new_values); + pseudo_style.values = Some(new_values) } + /// get_after_change_style removes the transition rules from the ComputedValues. /// If there is no transition rule in the ComputedValues, it returns None. #[cfg(feature = "gecko")] @@ -754,15 +743,11 @@ trait PrivateMatchMethods: TElement { } /// Computes and applies non-redundant damage. - /// - /// FIXME(emilio): Damage for non-::before and non-::after element-backed - /// pseudo-elements should be refactored to go on themselves (right now they - /// do, but we apply this twice). #[cfg(feature = "gecko")] fn accumulate_damage(&self, shared_context: &SharedStyleContext, restyle: &mut RestyleData, - old_values: &Arc<ComputedValues>, + old_values: &ComputedValues, new_values: &Arc<ComputedValues>, pseudo: Option<&PseudoElement>) { // Don't accumulate damage if we're in a restyle for reconstruction. @@ -798,12 +783,12 @@ trait PrivateMatchMethods: TElement { fn accumulate_damage(&self, _shared_context: &SharedStyleContext, restyle: &mut RestyleData, - old_values: &Arc<ComputedValues>, + old_values: &ComputedValues, new_values: &Arc<ComputedValues>, pseudo: Option<&PseudoElement>) { if restyle.damage != RestyleDamage::rebuild_and_reflow() { - let d = self.compute_restyle_damage(&old_values, &new_values, pseudo); - restyle.damage |= d; + restyle.damage |= + self.compute_restyle_damage(&old_values, &new_values, pseudo); } } @@ -895,8 +880,10 @@ pub trait MatchMethods : TElement { sharing: StyleSharingBehavior) { // Perform selector matching for the primary style. - let mut primary_relations = StyleRelations::empty(); - let _rule_node_changed = self.match_primary(context, data, &mut primary_relations); + let mut relations = StyleRelations::empty(); + let _rule_node_changed = self.match_primary(context, + data, + &mut relations); // Cascade properties and compute primary values. self.cascade_primary(context, data); @@ -910,7 +897,7 @@ pub trait MatchMethods : TElement { // If we have any pseudo elements, indicate so in the primary StyleRelations. if !data.styles().pseudos.is_empty() { - primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS; + relations |= AFFECTED_BY_PSEUDO_ELEMENTS; } // If the style is shareable, add it to the LRU cache. @@ -930,7 +917,7 @@ pub trait MatchMethods : TElement { .style_sharing_candidate_cache .insert_if_possible(self, data.styles().primary.values(), - primary_relations, + relations, revalidation_match_results); } } @@ -1002,33 +989,26 @@ pub trait MatchMethods : TElement { let animation_rules = self.get_animation_rules(); let bloom = context.thread_local.bloom_filter.filter(); + let map = &mut context.thread_local.selector_flags; let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| { self.apply_selector_flags(map, element, flags); }; - let selector_matching_target = match implemented_pseudo { - Some(..) => { - self.closest_non_native_anonymous_ancestor() - .expect("Pseudo-element without non-NAC parent?") - }, - None => *self, - }; - - let pseudo_and_state = match implemented_pseudo { - Some(ref pseudo) => Some((pseudo, self.get_state())), - None => None, - }; + let mut matching_context = + MatchingContext::new(MatchingMode::Normal, Some(bloom)); // Compute the primary rule node. - *relations = stylist.push_applicable_declarations(&selector_matching_target, - Some(bloom), - style_attribute, - smil_override, - animation_rules, - pseudo_and_state, - &mut applicable_declarations, - &mut set_selector_flags); + stylist.push_applicable_declarations(self, + implemented_pseudo.as_ref(), + style_attribute, + smil_override, + animation_rules, + &mut applicable_declarations, + &mut matching_context, + &mut set_selector_flags); + + *relations = matching_context.relations; let primary_rule_node = compute_rule_node::<Self>(&stylist.rule_tree, @@ -1038,8 +1018,8 @@ pub trait MatchMethods : TElement { return data.set_primary_rules(primary_rule_node); } - /// Runs selector matching to (re)compute eager pseudo-element rule nodes for this - /// element. + /// Runs selector matching to (re)compute eager pseudo-element rule nodes + /// for this element. /// /// Returns whether any of the pseudo rule nodes changed (including, but not /// limited to, cases where we match different pseudos altogether). @@ -1067,6 +1047,10 @@ pub trait MatchMethods : TElement { let rule_tree = &stylist.rule_tree; let bloom_filter = context.thread_local.bloom_filter.filter(); + let mut matching_context = + MatchingContext::new(MatchingMode::ForStatelessPseudoElement, + Some(bloom_filter)); + // Compute rule nodes for eagerly-cascaded pseudo-elements. let mut matches_different_pseudos = false; let mut rule_nodes_changed = false; @@ -1076,12 +1060,12 @@ pub trait MatchMethods : TElement { // NB: We handle animation rules for ::before and ::after when // traversing them. stylist.push_applicable_declarations(self, - Some(bloom_filter), + Some(&pseudo), None, None, AnimationRules(None, None), - Some((&pseudo, ElementState::empty())), &mut applicable_declarations, + &mut matching_context, &mut set_selector_flags); if !applicable_declarations.is_empty() { @@ -1396,7 +1380,7 @@ pub trait MatchMethods : TElement { /// pseudo-element, compute the restyle damage used to determine which /// kind of layout or painting operations we'll need. fn compute_restyle_damage(&self, - old_values: &Arc<ComputedValues>, + old_values: &ComputedValues, new_values: &Arc<ComputedValues>, pseudo: Option<&PseudoElement>) -> RestyleDamage @@ -1420,15 +1404,7 @@ pub trait MatchMethods : TElement { } } - /// Performs the cascade for the element's primary style. - fn cascade_primary(&self, - context: &mut StyleContext<Self>, - mut data: &mut ElementData) - { - self.cascade_primary_or_pseudo(context, &mut data, None); - } - - /// Performs the cascade for the element's eager pseudos. + /// Cascade the eager pseudo-elements of this element. fn cascade_pseudos(&self, context: &mut StyleContext<Self>, mut data: &mut ElementData) @@ -1440,7 +1416,7 @@ pub trait MatchMethods : TElement { // let us pass the mutable |data| to the cascade function. let matched_pseudos = data.styles().pseudos.keys(); for pseudo in matched_pseudos { - self.cascade_primary_or_pseudo(context, data, Some(&pseudo)); + self.cascade_eager_pseudo(context, data, &pseudo); } } diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 6e35cdaf9f3..4fd410c161f 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -1053,7 +1053,7 @@ fn static_assert() { skip_longhands="${skip_position_longhands} z-index box-sizing order align-content justify-content align-self justify-self align-items justify-items grid-auto-rows grid-auto-columns grid-auto-flow - grid-template-areas"> + grid-template-areas grid-template-rows grid-template-columns"> % for side in SIDES: <% impl_split_style_coord("%s" % side.ident, "mOffset", @@ -1165,9 +1165,9 @@ fn static_assert() { let ident = v.ident.unwrap_or(String::new()); self.gecko.${value.gecko}.mLineName.assign_utf8(&ident); self.gecko.${value.gecko}.mHasSpan = v.is_span; - self.gecko.${value.gecko}.mInteger = v.integer.map(|i| { + self.gecko.${value.gecko}.mInteger = v.line_num.map(|i| { // clamping the integer between a range - cmp::max(nsStyleGridLine_kMinLine, cmp::min(i, nsStyleGridLine_kMaxLine)) + cmp::max(nsStyleGridLine_kMinLine, cmp::min(i.value(), nsStyleGridLine_kMaxLine)) }).unwrap_or(0); } @@ -1179,7 +1179,7 @@ fn static_assert() { % endfor % for kind in ["rows", "columns"]: - pub fn set_grid_auto_${kind}(&mut self, v: longhands::grid_auto_rows::computed_value::T) { + pub fn set_grid_auto_${kind}(&mut self, v: longhands::grid_auto_${kind}::computed_value::T) { use values::specified::grid::TrackSize; match v { @@ -1206,6 +1206,136 @@ fn static_assert() { self.gecko.mGridAuto${kind.title()}Min.copy_from(&other.gecko.mGridAuto${kind.title()}Min); self.gecko.mGridAuto${kind.title()}Max.copy_from(&other.gecko.mGridAuto${kind.title()}Max); } + + pub fn set_grid_template_${kind}(&mut self, v: longhands::grid_template_${kind}::computed_value::T) { + <% self_grid = "self.gecko.mGridTemplate%s" % kind.title() %> + use gecko::values::GeckoStyleCoordConvertible; + use gecko_bindings::structs::{nsTArray, nsStyleGridLine_kMaxLine}; + use nsstring::{nsCString, nsStringRepr}; + use std::usize; + use values::specified::grid::TrackListType::Auto; + use values::specified::grid::{RepeatCount, TrackSize}; + + #[inline] + fn set_bitfield(bitfield: &mut u8, pos: u8, val: bool) { + let mask = 1 << (pos - 1); + *bitfield &= !mask; + *bitfield |= (val as u8) << (pos - 1); + } + + #[inline] + fn set_line_names(servo_names: &[String], gecko_names: &mut nsTArray<nsStringRepr>) { + unsafe { + bindings::Gecko_ResizeTArrayForStrings(gecko_names, servo_names.len() as u32); + } + + for (servo_name, gecko_name) in servo_names.iter().zip(gecko_names.iter_mut()) { + gecko_name.assign_utf8(&nsCString::from(&*servo_name)); + } + } + + fn set_track_size<G, T>(value: TrackSize<T>, gecko_min: &mut G, gecko_max: &mut G) + where G: CoordDataMut, T: GeckoStyleCoordConvertible + { + match value { + TrackSize::FitContent(lop) => { + gecko_min.set_value(CoordDataValue::None); + lop.to_gecko_style_coord(gecko_max); + }, + TrackSize::Breadth(breadth) => { + breadth.to_gecko_style_coord(gecko_min); + breadth.to_gecko_style_coord(gecko_max); + }, + TrackSize::MinMax(min, max) => { + min.to_gecko_style_coord(gecko_min); + max.to_gecko_style_coord(gecko_max); + }, + } + } + + // Set defaults + ${self_grid}.mRepeatAutoIndex = -1; + set_bitfield(&mut ${self_grid}._bitfield_1, 1, false); // mIsAutoFill + set_bitfield(&mut ${self_grid}._bitfield_1, 2, false); // mIsSubgrid + // FIXME: mIsSubgrid is false only for <none>, but we don't support subgrid name lists at the moment. + + match v { + Either::First(track) => { + let mut auto_idx = usize::MAX; + let mut auto_track_size = None; + if let Auto(idx) = track.list_type { + auto_idx = idx as usize; + let auto_repeat = track.auto_repeat.as_ref().expect("expected <auto-track-repeat> value"); + + if auto_repeat.count == RepeatCount::AutoFill { + set_bitfield(&mut ${self_grid}._bitfield_1, 1, true); + } + + ${self_grid}.mRepeatAutoIndex = idx as i16; + // NOTE: Gecko supports only one set of values in <auto-repeat> + // i.e., it can only take repeat(auto-fill, [a] 10px [b]), and no more. + set_line_names(&auto_repeat.line_names[0], &mut ${self_grid}.mRepeatAutoLineNameListBefore); + set_line_names(&auto_repeat.line_names[1], &mut ${self_grid}.mRepeatAutoLineNameListAfter); + auto_track_size = Some(auto_repeat.track_sizes.get(0).unwrap().clone()); + } else { + unsafe { + bindings::Gecko_ResizeTArrayForStrings( + &mut ${self_grid}.mRepeatAutoLineNameListBefore, 0); + bindings::Gecko_ResizeTArrayForStrings( + &mut ${self_grid}.mRepeatAutoLineNameListAfter, 0); + } + } + + let mut num_values = track.values.len(); + if auto_track_size.is_some() { + num_values += 1; + } + + let max_lines = nsStyleGridLine_kMaxLine as usize - 1; // for accounting the final <line-names> + num_values = cmp::min(num_values, max_lines); + unsafe { + bindings::Gecko_SetStyleGridTemplateArrayLengths(&mut ${self_grid}, num_values as u32); + } + + let mut line_names = track.line_names.into_iter(); + let mut values_iter = track.values.into_iter(); + let min_max_iter = ${self_grid}.mMinTrackSizingFunctions.iter_mut() + .zip(${self_grid}.mMaxTrackSizingFunctions.iter_mut()); + + for (i, (gecko_min, gecko_max)) in min_max_iter.enumerate().take(max_lines) { + let name_list = line_names.next().expect("expected line-names"); + set_line_names(&name_list, &mut ${self_grid}.mLineNameLists[i]); + if i == auto_idx { + set_track_size(auto_track_size.take().expect("expected <track-size> for <auto-track-repeat>"), + gecko_min, gecko_max); + continue + } + + let track_size = values_iter.next().expect("expected <track-size> value"); + set_track_size(track_size, gecko_min, gecko_max); + } + + let final_names = line_names.next().unwrap(); + set_line_names(&final_names, ${self_grid}.mLineNameLists.last_mut().unwrap()); + }, + Either::Second(_none) => { + unsafe { + bindings::Gecko_SetStyleGridTemplateArrayLengths(&mut ${self_grid}, 0); + bindings::Gecko_ResizeTArrayForStrings( + &mut ${self_grid}.mRepeatAutoLineNameListBefore, 0); + bindings::Gecko_ResizeTArrayForStrings( + &mut ${self_grid}.mRepeatAutoLineNameListAfter, 0); + } + }, + } + } + + pub fn copy_grid_template_${kind}_from(&mut self, other: &Self) { + unsafe { + bindings::Gecko_CopyStyleGridTemplateValues(&mut ${self_grid}, + &other.gecko.mGridTemplate${kind.title()}); + } + } % endfor pub fn set_grid_auto_flow(&mut self, v: longhands::grid_auto_flow::computed_value::T) { @@ -2850,9 +2980,21 @@ fn static_assert() { pub fn copy_${shorthand}_image_from(&mut self, other: &Self) { + use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; unsafe { - Gecko_CopyImageValueFrom(&mut self.gecko.${image_layers_field}.mLayers.mFirstElement.mImage, - &other.gecko.${image_layers_field}.mLayers.mFirstElement.mImage); + let count = other.gecko.${image_layers_field}.mImageCount; + unsafe { + Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, + count as usize, + LayerType::${shorthand.capitalize()}); + } + + for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut() + .zip(other.gecko.${image_layers_field}.mLayers.iter()) + .take(count as usize) { + Gecko_CopyImageValueFrom(&mut layer.mImage, &other.mImage); + } + self.gecko.${image_layers_field}.mImageCount = count; } } @@ -3626,9 +3768,7 @@ fn static_assert() { } self.clear_overflow_sides_if_string(); - if v.second.is_none() { - self.gecko.mTextOverflow.mLogicalDirections = true; - } + self.gecko.mTextOverflow.mLogicalDirections = v.second.is_none(); let SpecifiedValue { ref first, ref second } = v; let second = second.as_ref().unwrap_or(&first); diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index e23b4877e56..071454dd2ba 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -1035,11 +1035,9 @@ impl Animatable for CalcLengthOrPercentage { } } - Ok(CalcLengthOrPercentage { - length: try!(self.length.add_weighted(&other.length, self_portion, other_portion)), - percentage: try!(add_weighted_half(self.percentage, other.percentage, - self_portion, other_portion)), - }) + let length = self.unclamped_length().add_weighted(&other.unclamped_length(), self_portion, other_portion)?; + let percentage = add_weighted_half(self.percentage, other.percentage, self_portion, other_portion)?; + Ok(CalcLengthOrPercentage::with_clamping_mode(length, percentage, self.clamping_mode)) } #[inline] @@ -1049,7 +1047,7 @@ impl Animatable for CalcLengthOrPercentage { #[inline] fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - let length_diff = (self.length().0 - other.length().0) as f64; + let length_diff = (self.unclamped_length().0 - other.unclamped_length().0) as f64; let percentage_diff = (self.percentage() - other.percentage()) as f64; Ok(length_diff * length_diff + percentage_diff * percentage_diff) } @@ -1114,7 +1112,7 @@ impl Animatable for LengthOrPercentage { (this, other) => { let this: CalcLengthOrPercentage = From::from(this); let other: CalcLengthOrPercentage = From::from(other); - let length_diff = (this.length().0 - other.length().0) as f64; + let length_diff = (this.unclamped_length().0 - other.unclamped_length().0) as f64; let percentage_diff = (this.percentage() - other.percentage()) as f64; Ok(length_diff * length_diff + percentage_diff * percentage_diff) } @@ -1187,12 +1185,12 @@ impl Animatable for LengthOrPercentageOrAuto { (this, other) => { let this: Option<CalcLengthOrPercentage> = From::from(this); let other: Option<CalcLengthOrPercentage> = From::from(other); - if this.is_none() || other.is_none() { - Err(()) - } else { - let length_diff = (this.unwrap().length().0 - other.unwrap().length().0) as f64; - let percentage_diff = (this.unwrap().percentage() - other.unwrap().percentage()) as f64; + if let (Some(this), Some(other)) = (this, other) { + let length_diff = (this.unclamped_length().0 - other.unclamped_length().0) as f64; + let percentage_diff = (this.percentage() - other.percentage()) as f64; Ok(length_diff * length_diff + percentage_diff * percentage_diff) + } else { + Err(()) } } } diff --git a/components/style/properties/longhand/background.mako.rs b/components/style/properties/longhand/background.mako.rs index e5c3d628f64..3b41a5714cf 100644 --- a/components/style/properties/longhand/background.mako.rs +++ b/components/style/properties/longhand/background.mako.rs @@ -10,7 +10,9 @@ ${helpers.predefined_type("background-color", "CSSColor", "::cssparser::Color::RGBA(::cssparser::RGBA::transparent())", initial_specified_value="SpecifiedValue::transparent()", spec="https://drafts.csswg.org/css-backgrounds/#background-color", - animation_value_type="IntermediateColor", complex_color=True)} + animation_value_type="IntermediateColor", + complex_color=True, + allow_quirks=True)} ${helpers.predefined_type("background-image", "ImageLayer", initial_value="Either::First(None_)", diff --git a/components/style/properties/longhand/border.mako.rs b/components/style/properties/longhand/border.mako.rs index f18ae154428..e5270e30fb9 100644 --- a/components/style/properties/longhand/border.mako.rs +++ b/components/style/properties/longhand/border.mako.rs @@ -20,7 +20,9 @@ "::cssparser::Color::CurrentColor", alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-color"), spec=maybe_logical_spec(side, "color"), - animation_value_type="IntermediateColor", logical = side[1])} + animation_value_type="IntermediateColor", + logical=side[1], + allow_quirks=not side[1])} ${helpers.predefined_type("border-%s-style" % side[0], "BorderStyle", "specified::BorderStyle::none", diff --git a/components/style/properties/longhand/color.mako.rs b/components/style/properties/longhand/color.mako.rs index 0a5dd2f8d63..e4c3ef4b1fb 100644 --- a/components/style/properties/longhand/color.mako.rs +++ b/components/style/properties/longhand/color.mako.rs @@ -14,7 +14,7 @@ use cssparser::RGBA; use std::fmt; use style_traits::ToCss; - use values::specified::{Color, CSSColor, CSSRGBA}; + use values::specified::{AllowQuirks, Color, CSSColor, CSSRGBA}; impl ToComputedValue for SpecifiedValue { type ComputedValue = computed_value::T; @@ -50,7 +50,7 @@ RGBA::new(0, 0, 0, 255) // black } pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { - CSSColor::parse(context, input).map(SpecifiedValue) + CSSColor::parse_quirky(context, input, AllowQuirks::Yes).map(SpecifiedValue) } // FIXME(#15973): Add servo support for system colors diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index aed5221e4b6..9d2ec4215d4 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -800,8 +800,7 @@ ${helpers.single_keyword_system("font-variant-caps", } SpecifiedValue::Length(LengthOrPercentage::Calc(ref calc)) => { let calc = calc.to_computed_value(context); - calc.length() + base_size.resolve(context) - .scale_by(calc.percentage()) + calc.to_used_value(Some(base_size.resolve(context))).unwrap() } SpecifiedValue::Keyword(ref key, fraction) => { key.to_computed_value(context).scale_by(fraction) diff --git a/components/style/properties/longhand/inherited_text.mako.rs b/components/style/properties/longhand/inherited_text.mako.rs index b2d57a72393..a1f6dfc0418 100644 --- a/components/style/properties/longhand/inherited_text.mako.rs +++ b/components/style/properties/longhand/inherited_text.mako.rs @@ -132,7 +132,8 @@ let calc = calc.to_computed_value(context); let fr = specified::FontRelativeLength::Em(calc.percentage()); let fr = specified::Length::NoCalc(specified::NoCalcLength::FontRelative(fr)); - computed_value::T::Length(calc.length() + fr.to_computed_value(context)) + let length = calc.unclamped_length(); + computed_value::T::Length(calc.clamping_mode.clamp(length + fr.to_computed_value(context))) } } } diff --git a/components/style/properties/longhand/position.mako.rs b/components/style/properties/longhand/position.mako.rs index 370a538e893..0cea82301cc 100644 --- a/components/style/properties/longhand/position.mako.rs +++ b/components/style/properties/longhand/position.mako.rs @@ -319,6 +319,17 @@ ${helpers.predefined_type("object-position", spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-%ss" % kind, products="gecko", boxed=True)} + + // NOTE: The spec lists only `none | <track-list> | <auto-track-list>`, but gecko seems to support + // `subgrid <line-name-list>?` in addition to this (probably old spec). We should support it soon. + ${helpers.predefined_type("grid-template-%ss" % kind, + "TrackListOrNone", + "Either::Second(None_)", + products="gecko", + spec="https://drafts.csswg.org/css-grid/#propdef-grid-template-%ss" % kind, + boxed=True, + animation_value_type="none")} + % endfor <%helpers:longhand name="grid-auto-flow" diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 79505e92657..1f5c951d1f4 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -1954,33 +1954,30 @@ impl ComputedValues { )) } - /// https://drafts.csswg.org/css-transforms/#grouping-property-values - pub fn get_used_transform_style(&self) -> computed_values::transform_style::T { + /// Return true if the effects force the transform style to be Flat + pub fn overrides_transform_style(&self) -> bool { use computed_values::mix_blend_mode; - use computed_values::transform_style; let effects = self.get_effects(); - let box_ = self.get_box(); - // TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported. - if effects.opacity < 1.0 || + effects.opacity < 1.0 || !effects.filter.is_empty() || - !effects.clip.is_auto() { - effects.mix_blend_mode != mix_blend_mode::T::normal || - return transform_style::T::flat; - } + !effects.clip.is_auto() || + effects.mix_blend_mode != mix_blend_mode::T::normal + } - if box_.transform_style == transform_style::T::auto { - if box_.transform.0.is_some() { - return transform_style::T::flat; - } - if let Either::First(ref _length) = box_.perspective { - return transform_style::T::flat; - } - } + /// https://drafts.csswg.org/css-transforms/#grouping-property-values + pub fn get_used_transform_style(&self) -> computed_values::transform_style::T { + use computed_values::transform_style; - // Return the computed value if not overridden by the above exceptions - box_.transform_style + let box_ = self.get_box(); + + if self.overrides_transform_style() { + transform_style::T::flat + } else { + // Return the computed value if not overridden by the above exceptions + box_.transform_style + } } /// Whether given this transform value, the compositor would require a diff --git a/components/style/properties/shorthand/background.mako.rs b/components/style/properties/shorthand/background.mako.rs index 7732a84ff73..89874f82084 100644 --- a/components/style/properties/shorthand/background.mako.rs +++ b/components/style/properties/shorthand/background.mako.rs @@ -10,12 +10,12 @@ background-attachment background-image background-size background-origin background-clip" spec="https://drafts.csswg.org/css-backgrounds/#the-background"> - use properties::longhands::{background_color, background_position_x, background_position_y, background_repeat}; + use properties::longhands::{background_position_x, background_position_y, background_repeat}; use properties::longhands::{background_attachment, background_image, background_size, background_origin}; use properties::longhands::background_clip; use properties::longhands::background_clip::single_value::computed_value::T as Clip; use properties::longhands::background_origin::single_value::computed_value::T as Origin; - use values::specified::{Position, PositionComponent}; + use values::specified::{CSSColor, Position, PositionComponent}; use parser::Parse; impl From<background_origin::single_value::SpecifiedValue> for background_clip::single_value::SpecifiedValue { @@ -43,7 +43,7 @@ let mut ${name} = None; % endfor loop { - if let Ok(value) = input.try(|input| background_color::parse(context, input)) { + if let Ok(value) = input.try(|i| CSSColor::parse(context, i)) { if background_color.is_none() { background_color = Some(value); continue @@ -109,7 +109,7 @@ })); Ok(Longhands { - background_color: unwrap_or_initial!(background_color), + background_color: background_color.unwrap_or(CSSColor::transparent()), background_image: background_image, background_position_x: background_position_x, background_position_y: background_position_y, diff --git a/components/style/properties/shorthand/position.mako.rs b/components/style/properties/shorthand/position.mako.rs index 017b592edea..58206bb67f4 100644 --- a/components/style/properties/shorthand/position.mako.rs +++ b/components/style/properties/shorthand/position.mako.rs @@ -154,7 +154,7 @@ GridLine::parse(context, input)? } else { let mut line = GridLine::default(); - if start.integer.is_none() && !start.is_span { + if start.line_num.is_none() && !start.is_span { line.ident = start.ident.clone(); // ident from start value should be taken } @@ -188,7 +188,7 @@ pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> { fn line_with_ident_from(other: &GridLine) -> GridLine { let mut this = GridLine::default(); - if other.integer.is_none() && !other.is_span { + if other.line_num.is_none() && !other.is_span { this.ident = other.ident.clone(); } diff --git a/components/style/restyle_hints.rs b/components/style/restyle_hints.rs index 54d92151dbd..73bd39ec1b3 100644 --- a/components/style/restyle_hints.rs +++ b/components/style/restyle_hints.rs @@ -7,19 +7,20 @@ #![deny(missing_docs)] use Atom; +use LocalName; +use Namespace; use dom::TElement; use element_state::*; -use fnv::FnvHashMap; #[cfg(feature = "gecko")] use gecko_bindings::structs::nsRestyleHint; #[cfg(feature = "servo")] use heapsize::HeapSizeOf; -use selector_parser::{AttrValue, NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap}; -use selectors::{Element, MatchAttr}; -use selectors::matching::{ElementSelectorFlags, StyleRelations}; +use selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap, AttrValue}; +use selectors::Element; +use selectors::attr::{AttrSelectorOperation, NamespaceConstraint}; +use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode}; use selectors::matching::matches_selector; -use selectors::parser::{AttrSelector, Combinator, Component, Selector}; -use selectors::parser::{SelectorInner, SelectorMethods}; +use selectors::parser::{Combinator, Component, Selector, SelectorInner, SelectorMethods}; use selectors::visitor::SelectorVisitor; use smallvec::SmallVec; use std::borrow::Borrow; @@ -171,7 +172,7 @@ impl HeapSizeOf for RestyleHint { /// still need to take the ElementWrapper approach for attribute-dependent /// style. So we do it the same both ways for now to reduce complexity, but it's /// worth measuring the performance impact (if any) of the mStateMask approach. -pub trait ElementSnapshot : Sized + MatchAttr<Impl=SelectorImpl> { +pub trait ElementSnapshot : Sized { /// The state of the snapshot, if any. fn state(&self) -> Option<ElementState>; @@ -243,90 +244,6 @@ impl<'a, E> ElementWrapper<'a, E> } } -impl<'a, E> MatchAttr for ElementWrapper<'a, E> - where E: TElement, -{ - type Impl = SelectorImpl; - - fn match_attr_has(&self, attr: &AttrSelector<SelectorImpl>) -> bool { - match self.snapshot() { - Some(snapshot) if snapshot.has_attrs() - => snapshot.match_attr_has(attr), - _ => self.element.match_attr_has(attr) - } - } - - fn match_attr_equals(&self, - attr: &AttrSelector<SelectorImpl>, - value: &AttrValue) -> bool { - match self.snapshot() { - Some(snapshot) if snapshot.has_attrs() - => snapshot.match_attr_equals(attr, value), - _ => self.element.match_attr_equals(attr, value) - } - } - - fn match_attr_equals_ignore_ascii_case(&self, - attr: &AttrSelector<SelectorImpl>, - value: &AttrValue) -> bool { - match self.snapshot() { - Some(snapshot) if snapshot.has_attrs() - => snapshot.match_attr_equals_ignore_ascii_case(attr, value), - _ => self.element.match_attr_equals_ignore_ascii_case(attr, value) - } - } - - fn match_attr_includes(&self, - attr: &AttrSelector<SelectorImpl>, - value: &AttrValue) -> bool { - match self.snapshot() { - Some(snapshot) if snapshot.has_attrs() - => snapshot.match_attr_includes(attr, value), - _ => self.element.match_attr_includes(attr, value) - } - } - - fn match_attr_dash(&self, - attr: &AttrSelector<SelectorImpl>, - value: &AttrValue) -> bool { - match self.snapshot() { - Some(snapshot) if snapshot.has_attrs() - => snapshot.match_attr_dash(attr, value), - _ => self.element.match_attr_dash(attr, value) - } - } - - fn match_attr_prefix(&self, - attr: &AttrSelector<SelectorImpl>, - value: &AttrValue) -> bool { - match self.snapshot() { - Some(snapshot) if snapshot.has_attrs() - => snapshot.match_attr_prefix(attr, value), - _ => self.element.match_attr_prefix(attr, value) - } - } - - fn match_attr_substring(&self, - attr: &AttrSelector<SelectorImpl>, - value: &AttrValue) -> bool { - match self.snapshot() { - Some(snapshot) if snapshot.has_attrs() - => snapshot.match_attr_substring(attr, value), - _ => self.element.match_attr_substring(attr, value) - } - } - - fn match_attr_suffix(&self, - attr: &AttrSelector<SelectorImpl>, - value: &AttrValue) -> bool { - match self.snapshot() { - Some(snapshot) if snapshot.has_attrs() - => snapshot.match_attr_suffix(attr, value), - _ => self.element.match_attr_suffix(attr, value) - } - } -} - #[cfg(feature = "gecko")] fn dir_selector_to_state(s: &[u16]) -> ElementState { // Jump through some hoops to deal with our Box<[u16]> thing. @@ -346,9 +263,11 @@ fn dir_selector_to_state(s: &[u16]) -> ElementState { impl<'a, E> Element for ElementWrapper<'a, E> where E: TElement, { + type Impl = SelectorImpl; + fn match_non_ts_pseudo_class<F>(&self, pseudo_class: &NonTSPseudoClass, - relations: &mut StyleRelations, + context: &mut MatchingContext, _setter: &mut F) -> bool where F: FnMut(&Self, ElementSelectorFlags), @@ -360,7 +279,7 @@ impl<'a, E> Element for ElementWrapper<'a, E> use selectors::matching::matches_complex_selector; if let NonTSPseudoClass::MozAny(ref selectors) = *pseudo_class { return selectors.iter().any(|s| { - matches_complex_selector(s, self, relations, _setter) + matches_complex_selector(s, self, context, _setter) }) } } @@ -393,19 +312,27 @@ impl<'a, E> Element for ElementWrapper<'a, E> let flag = pseudo_class.state_flag(); if flag.is_empty() { return self.element.match_non_ts_pseudo_class(pseudo_class, - relations, + context, &mut |_, _| {}) } match self.snapshot().and_then(|s| s.state()) { Some(snapshot_state) => snapshot_state.intersects(flag), None => { self.element.match_non_ts_pseudo_class(pseudo_class, - relations, + context, &mut |_, _| {}) } } } + fn match_pseudo_element(&self, + pseudo_element: &PseudoElement, + context: &mut MatchingContext) + -> bool + { + self.element.match_pseudo_element(pseudo_element, context) + } + fn parent_element(&self) -> Option<Self> { self.element.parent_element() .map(|e| ElementWrapper::new(e, self.snapshot_map)) @@ -443,6 +370,19 @@ impl<'a, E> Element for ElementWrapper<'a, E> self.element.get_namespace() } + fn attr_matches(&self, + ns: &NamespaceConstraint<&Namespace>, + local_name: &LocalName, + operation: &AttrSelectorOperation<&AttrValue>) + -> bool { + match self.snapshot() { + Some(snapshot) if snapshot.has_attrs() => { + snapshot.attr_matches(ns, local_name, operation) + } + _ => self.element.attr_matches(ns, local_name, operation) + } + } + fn get_id(&self) -> Option<Atom> { match self.snapshot() { Some(snapshot) if snapshot.has_attrs() @@ -467,13 +407,9 @@ impl<'a, E> Element for ElementWrapper<'a, E> self.element.is_root() } - fn each_class<F>(&self, callback: F) - where F: FnMut(&Atom) { - match self.snapshot() { - Some(snapshot) if snapshot.has_attrs() - => snapshot.each_class(callback), - _ => self.element.each_class(callback) - } + fn pseudo_element_originating_element(&self) -> Option<Self> { + self.element.closest_non_native_anonymous_ancestor() + .map(|e| ElementWrapper::new(e, self.snapshot_map)) } } @@ -492,13 +428,9 @@ fn is_attr_selector(sel: &Component<SelectorImpl>) -> bool { match *sel { Component::ID(_) | Component::Class(_) | - Component::AttrExists(_) | - Component::AttrEqual(_, _, _) | - Component::AttrIncludes(_, _) | - Component::AttrDashMatch(_, _) | - Component::AttrPrefixMatch(_, _) | - Component::AttrSubstringMatch(_, _) | - Component::AttrSuffixMatch(_, _) => true, + Component::AttributeInNoNamespaceExists { .. } | + Component::AttributeInNoNamespace { .. } | + Component::AttributeOther(_) => true, _ => false, } } @@ -507,6 +439,9 @@ fn combinator_to_restyle_hint(combinator: Option<Combinator>) -> RestyleHint { match combinator { None => RESTYLE_SELF, Some(c) => match c { + // NB: RESTYLE_SELF is needed to handle properly eager pseudos, + // otherwise we may leave a stale style on the parent. + Combinator::PseudoElement => RESTYLE_SELF | RESTYLE_DESCENDANTS, Combinator::Child => RESTYLE_DESCENDANTS, Combinator::Descendant => RESTYLE_DESCENDANTS, Combinator::NextSibling => RESTYLE_LATER_SIBLINGS, @@ -634,13 +569,6 @@ impl SelectorVisitor for SensitivitiesVisitor { #[derive(Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct DependencySet { - /// A map used for pseudo-element's dependencies. - /// - /// Note that pseudo-elements are somewhat special, because some of them in - /// Gecko track state, and also because they don't do selector-matching as - /// normal, but against their parent element. - pseudo_dependencies: FnvHashMap<PseudoElement, SelectorMap<PseudoElementDependency>>, - /// This is for all other normal element's selectors/selector parts. dependencies: SelectorMap<Dependency>, } @@ -668,34 +596,9 @@ impl DependencySet { index += 1; // Account for the simple selector. } - - let pseudo_selector_is_state_dependent = - sequence_start == 0 && - selector.pseudo_element.as_ref().map_or(false, |pseudo_selector| { - !pseudo_selector.state().is_empty() - }); - - if pseudo_selector_is_state_dependent { - let pseudo_selector = selector.pseudo_element.as_ref().unwrap(); - self.pseudo_dependencies - .entry(pseudo_selector.pseudo_element().clone()) - .or_insert_with(SelectorMap::new) - .insert(PseudoElementDependency { - selector: selector.clone(), - }); - } - // If we found a sensitivity, add an entry in the dependency set. if !visitor.sensitivities.is_empty() { - let mut hint = combinator_to_restyle_hint(combinator); - - if sequence_start == 0 && selector.pseudo_element.is_some() { - // FIXME(emilio): Be more granular about this. See the - // comment in `PseudoElementDependency` about how could this - // be modified in order to be more efficient and restyle - // less. - hint |= RESTYLE_DESCENDANTS; - } + let hint = combinator_to_restyle_hint(combinator); let dep_selector = if sequence_start == 0 { // Reuse the bloom hashes if this is the base selector. @@ -724,82 +627,22 @@ impl DependencySet { pub fn new() -> Self { DependencySet { dependencies: SelectorMap::new(), - pseudo_dependencies: FnvHashMap::default(), } } /// Return the total number of dependencies that this set contains. pub fn len(&self) -> usize { - self.dependencies.len() + - self.pseudo_dependencies.values().fold(0, |acc, val| acc + val.len()) + self.dependencies.len() } /// Clear this dependency set. pub fn clear(&mut self) { self.dependencies = SelectorMap::new(); - self.pseudo_dependencies.clear() } - fn compute_pseudo_hint<E>( - &self, - pseudo: &E, - pseudo_element: PseudoElement, - snapshots: &SnapshotMap) - -> RestyleHint - where E: TElement, - { - debug!("compute_pseudo_hint: {:?}, {:?}", pseudo, pseudo_element); - debug_assert!(pseudo.has_snapshot()); - - let map = match self.pseudo_dependencies.get(&pseudo_element) { - Some(map) => map, - None => return RestyleHint::empty(), - }; - - // Only pseudo-element's state is relevant. - let pseudo_state_changes = - ElementWrapper::new(*pseudo, snapshots).state_changes(); - - debug!("pseudo_state_changes: {:?}", pseudo_state_changes); - if pseudo_state_changes.is_empty() { - return RestyleHint::empty(); - } - - let selector_matching_target = - pseudo.closest_non_native_anonymous_ancestor().unwrap(); - - // Note that we rely on that, if the originating element changes, it'll - // post a restyle hint that would make us redo selector matching, so we - // don't need to care about that. - // - // If that ever changes, we'd need to share more code with - // `compute_element_hint`. - let mut hint = RestyleHint::empty(); - map.lookup(selector_matching_target, &mut |dep| { - // If the selector didn't match before, it either doesn't match now - // either (or it doesn't matter because our parent posted a restyle - // for us above). - if !matches_selector(&dep.selector.inner, &selector_matching_target, - None, &mut StyleRelations::empty(), - &mut |_, _| {}) { - return true; - } - - let pseudo_selector = dep.selector.pseudo_element.as_ref().unwrap(); - debug_assert!(!pseudo_selector.state().is_empty()); - - if pseudo_selector.state().intersects(pseudo_state_changes) { - hint = RESTYLE_SELF; - return false; - } - - true - }); - - hint - } - - fn compute_element_hint<E>( + /// Compute a restyle hint given an element and a snapshot, per the rules + /// explained in the rest of the documentation. + pub fn compute_hint<E>( &self, el: &E, snapshots: &SnapshotMap) @@ -838,8 +681,18 @@ impl DependencySet { }); } + // FIXME(emilio): A bloom filter here would be neat. + let mut matching_context = + MatchingContext::new(MatchingMode::Normal, None); + + let lookup_element = if el.implemented_pseudo_element().is_some() { + el.closest_non_native_anonymous_ancestor().unwrap() + } else { + *el + }; + self.dependencies - .lookup_with_additional(*el, additional_id, &additional_classes, &mut |dep| { + .lookup_with_additional(lookup_element, additional_id, &additional_classes, &mut |dep| { trace!("scanning dependency: {:?}", dep); if !dep.sensitivities.sensitive_to(attrs_changed, state_changes) { @@ -856,12 +709,12 @@ impl DependencySet { // been set during original matching for any element that might // change its matching behavior here. let matched_then = - matches_selector(&dep.selector, &snapshot_el, None, - &mut StyleRelations::empty(), + matches_selector(&dep.selector, &snapshot_el, + &mut matching_context, &mut |_, _| {}); let matches_now = - matches_selector(&dep.selector, el, None, - &mut StyleRelations::empty(), + matches_selector(&dep.selector, el, + &mut matching_context, &mut |_, _| {}); if matched_then != matches_now { hint.insert(dep.hint); @@ -875,21 +728,4 @@ impl DependencySet { hint } - - - /// Compute a restyle hint given an element and a snapshot, per the rules - /// explained in the rest of the documentation. - pub fn compute_hint<E>(&self, - el: &E, - snapshots: &SnapshotMap) - -> RestyleHint - where E: TElement + Clone, - { - debug!("DependencySet::compute_hint({:?})", el); - if let Some(pseudo) = el.implemented_pseudo_element() { - return self.compute_pseudo_hint(el, pseudo, snapshots); - } - - self.compute_element_hint(el, snapshots) - } } diff --git a/components/style/selector_parser.rs b/components/style/selector_parser.rs index 0af60279af8..9e4e5deeaeb 100644 --- a/components/style/selector_parser.rs +++ b/components/style/selector_parser.rs @@ -37,7 +37,7 @@ pub use gecko::restyle_damage::GeckoRestyleDamage as RestyleDamage; /// A type that represents the previous computed values needed for restyle /// damage calculation. #[cfg(feature = "servo")] -pub type PreExistingComputedValues = ::stylearc::Arc<::properties::ServoComputedValues>; +pub type PreExistingComputedValues = ::properties::ServoComputedValues; /// A type that represents the previous computed values needed for restyle /// damage calculation. diff --git a/components/style/servo/restyle_damage.rs b/components/style/servo/restyle_damage.rs index 1fd7b38cae2..69199d6e982 100644 --- a/components/style/servo/restyle_damage.rs +++ b/components/style/servo/restyle_damage.rs @@ -11,7 +11,6 @@ use computed_values::display; use heapsize::HeapSizeOf; use properties::ServoComputedValues; use std::fmt; -use stylearc::Arc; bitflags! { #[doc = "Individual layout actions that may be necessary after restyling."] @@ -60,16 +59,17 @@ impl HeapSizeOf for ServoRestyleDamage { impl ServoRestyleDamage { /// Compute the appropriate restyle damage for a given style change between /// `old` and `new`. - pub fn compute(old: &Arc<ServoComputedValues>, - new: &Arc<ServoComputedValues>) -> ServoRestyleDamage { + pub fn compute(old: &ServoComputedValues, + new: &ServoComputedValues) + -> ServoRestyleDamage { compute_damage(old, new) } /// Returns a bitmask that represents a flow that needs to be rebuilt and /// reflowed. /// - /// FIXME(bholley): Do we ever actually need this? Shouldn't RECONSTRUCT_FLOW - /// imply everything else? + /// FIXME(bholley): Do we ever actually need this? Shouldn't + /// RECONSTRUCT_FLOW imply everything else? pub fn rebuild_and_reflow() -> ServoRestyleDamage { REPAINT | REPOSITION | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs index e801738866c..fdc14b11e29 100644 --- a/components/style/servo/selector_parser.rs +++ b/components/style/servo/selector_parser.rs @@ -14,9 +14,10 @@ use element_state::ElementState; use fnv::FnvHashMap; use restyle_hints::ElementSnapshot; use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser}; -use selectors::{Element, MatchAttrGeneric}; -use selectors::matching::StyleRelations; -use selectors::parser::{AttrSelector, SelectorMethods}; +use selectors::Element; +use selectors::attr::{AttrSelectorOperation, NamespaceConstraint}; +use selectors::matching::{MatchingContext, MatchingMode}; +use selectors::parser::SelectorMethods; use selectors::visitor::SelectorVisitor; use std::borrow::Cow; use std::fmt; @@ -51,6 +52,14 @@ pub enum PseudoElement { ServoInlineAbsolute, } +impl ::selectors::parser::PseudoElement for PseudoElement { + type Impl = SelectorImpl; + + fn supports_pseudo_class(&self, _: &NonTSPseudoClass) -> bool { + false + } +} + impl ToCss for PseudoElement { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { use self::PseudoElement::*; @@ -78,18 +87,6 @@ impl ToCss for PseudoElement { pub const EAGER_PSEUDO_COUNT: usize = 3; impl PseudoElement { - /// The pseudo-element, used for compatibility with Gecko's - /// `PseudoElementSelector`. - pub fn pseudo_element(&self) -> &Self { - self - } - - /// The pseudo-element selector's state, used for compatibility with Gecko's - /// `PseudoElementSelector`. - pub fn state(&self) -> ElementState { - ElementState::empty() - } - /// Gets the canonical index of this eagerly-cascaded pseudo-element. #[inline] pub fn eager_index(&self) -> usize { @@ -178,6 +175,7 @@ pub enum NonTSPseudoClass { ReadWrite, ReadOnly, ServoNonZeroBorder, + ServoCaseSensitiveTypeAttr(Atom), Target, Visited, } @@ -185,10 +183,18 @@ pub enum NonTSPseudoClass { impl ToCss for NonTSPseudoClass { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { use self::NonTSPseudoClass::*; - if let Lang(ref lang) = *self { - dest.write_str(":lang(")?; - serialize_identifier(lang, dest)?; - return dest.write_str(")"); + match *self { + Lang(ref lang) => { + dest.write_str(":lang(")?; + serialize_identifier(lang, dest)?; + return dest.write_str(")") + } + ServoCaseSensitiveTypeAttr(ref value) => { + dest.write_str(":-servo-case-sensitive-type-attr(")?; + serialize_identifier(value, dest)?; + return dest.write_str(")") + } + _ => {} } dest.write_str(match *self { @@ -201,7 +207,6 @@ impl ToCss for NonTSPseudoClass { Fullscreen => ":fullscreen", Hover => ":hover", Indeterminate => ":indeterminate", - Lang(_) => unreachable!(), Link => ":link", PlaceholderShown => ":placeholder-shown", ReadWrite => ":read-write", @@ -209,6 +214,8 @@ impl ToCss for NonTSPseudoClass { ServoNonZeroBorder => ":-servo-nonzero-border", Target => ":target", Visited => ":visited", + Lang(_) | + ServoCaseSensitiveTypeAttr(_) => unreachable!(), }) } } @@ -247,7 +254,8 @@ impl NonTSPseudoClass { Lang(_) | Link | Visited | - ServoNonZeroBorder => ElementState::empty(), + ServoNonZeroBorder | + ServoCaseSensitiveTypeAttr(_) => ElementState::empty(), } } @@ -264,7 +272,7 @@ impl NonTSPseudoClass { pub struct SelectorImpl; impl ::selectors::SelectorImpl for SelectorImpl { - type PseudoElementSelector = PseudoElement; + type PseudoElement = PseudoElement; type NonTSPseudoClass = NonTSPseudoClass; type AttrValue = String; @@ -316,16 +324,22 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> { -> Result<NonTSPseudoClass, ()> { use self::NonTSPseudoClass::*; let pseudo_class = match_ignore_ascii_case!{ &name, - "lang" => Lang(String::from(try!(parser.expect_ident_or_string())).into_boxed_str()), + "lang" => { + Lang(parser.expect_ident_or_string()?.into_owned().into_boxed_str()) + } + "-servo-case-sensitive-type-attr" => { + if !self.in_user_agent_stylesheet() { + return Err(()); + } + ServoCaseSensitiveTypeAttr(Atom::from(parser.expect_ident()?)) + } _ => return Err(()) }; Ok(pseudo_class) } - fn parse_pseudo_element(&self, - name: Cow<str>, - _input: &mut CssParser) + fn parse_pseudo_element(&self, name: Cow<str>) -> Result<PseudoElement, ()> { use self::PseudoElement::*; let pseudo_element = match_ignore_ascii_case! { &name, @@ -525,10 +539,10 @@ impl ServoElementSnapshot { .map(|&(_, ref v)| v) } - fn get_attr_ignore_ns(&self, name: &LocalName) -> Option<&AttrValue> { + fn any_attr_ignore_ns<F>(&self, name: &LocalName, mut f: F) -> bool + where F: FnMut(&AttrValue) -> bool { self.attrs.as_ref().unwrap().iter() - .find(|&&(ref ident, _)| ident.local_name == *name) - .map(|&(_, ref v)| v) + .any(|&(ref ident, ref v)| ident.local_name == *name && f(v)) } } @@ -561,26 +575,30 @@ impl ElementSnapshot for ServoElementSnapshot { } } -impl MatchAttrGeneric for ServoElementSnapshot { - type Impl = SelectorImpl; - - fn match_attr<F>(&self, attr: &AttrSelector<SelectorImpl>, test: F) -> bool - where F: Fn(&str) -> bool - { - use selectors::parser::NamespaceConstraint; - let html = self.is_html_element_in_html_document; - let local_name = if html { &attr.lower_name } else { &attr.name }; - match attr.namespace { - NamespaceConstraint::Specific(ref ns) => self.get_attr(&ns.url, local_name), - NamespaceConstraint::Any => self.get_attr_ignore_ns(local_name), - }.map_or(false, |v| test(v)) +impl ServoElementSnapshot { + /// selectors::Element::attr_matches + pub fn attr_matches(&self, + ns: &NamespaceConstraint<&Namespace>, + local_name: &LocalName, + operation: &AttrSelectorOperation<&String>) + -> bool { + match *ns { + NamespaceConstraint::Specific(ref ns) => { + self.get_attr(ns, local_name) + .map_or(false, |value| value.eval_selector(operation)) + } + NamespaceConstraint::Any => { + self.any_attr_ignore_ns(local_name, |value| value.eval_selector(operation)) + } + } } } impl<E: Element<Impl=SelectorImpl> + Debug> ElementExt for E { fn is_link(&self) -> bool { + let mut context = MatchingContext::new(MatchingMode::Normal, None); self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink, - &mut StyleRelations::empty(), + &mut context, &mut |_, _| {}) } diff --git a/components/style/stylist.rs b/components/style/stylist.rs index d8b76bd01b0..a5c103c5a55 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -6,7 +6,7 @@ #![deny(missing_docs)] -use {Atom, LocalName}; +use {Atom, LocalName, Namespace}; use bit_vec::BitVec; use context::QuirksMode; use data::ComputedStyle; @@ -26,11 +26,11 @@ use properties::PropertyDeclarationBlock; use restyle_hints::{RestyleHint, DependencySet}; use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource}; use selector_parser::{SelectorImpl, PseudoElement, SnapshotMap}; -use selectors::Element; +use selectors::attr::NamespaceConstraint; use selectors::bloom::BloomFilter; use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS}; -use selectors::matching::{ElementSelectorFlags, StyleRelations, matches_selector}; -use selectors::parser::{AttrSelector, Combinator, Component, Selector, SelectorInner, SelectorIter}; +use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext, MatchingMode}; +use selectors::parser::{Combinator, Component, Selector, SelectorInner, SelectorIter}; use selectors::parser::{SelectorMethods, LocalName as LocalNameSelector}; use selectors::visitor::SelectorVisitor; use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; @@ -479,9 +479,9 @@ impl Stylist { rule: &Arc<Locked<StyleRule>>, stylesheet: &Stylesheet) { - let map = if let Some(ref pseudo_selector) = selector.pseudo_element { + let map = if let Some(pseudo) = selector.pseudo_element() { self.pseudos_map - .entry(pseudo_selector.pseudo_element().clone()) + .entry(pseudo.clone()) .or_insert_with(PerPseudoElementSelectorMap::new) .borrow_for_origin(&stylesheet.origin) } else { @@ -503,7 +503,7 @@ impl Stylist { /// Returns whether the given attribute might appear in an attribute /// selector of some rule in the stylist. pub fn might_have_attribute_dependency(&self, - local_name: &<SelectorImpl as ::selectors::SelectorImpl>::LocalName) + local_name: &LocalName) -> bool { #[cfg(feature = "servo")] let style_lower_name = local_name!("style"); @@ -525,9 +525,6 @@ impl Stylist { #[inline] fn note_attribute_and_state_dependencies(&mut self, selector: &Selector<SelectorImpl>) { - if let Some(ref pseudo_selector) = selector.pseudo_element { - self.state_dependencies.insert(pseudo_selector.state()); - } selector.visit(&mut AttributeAndStateDependencyVisitor(self)); } @@ -635,14 +632,13 @@ impl Stylist { guards: &StylesheetGuards, element: &E, pseudo: &PseudoElement, - pseudo_state: ElementState, parent: &Arc<ComputedValues>, font_metrics: &FontMetricsProvider) -> Option<ComputedStyle> where E: TElement, { let rule_node = - match self.lazy_pseudo_rules(guards, element, pseudo, pseudo_state) { + match self.lazy_pseudo_rules(guards, element, pseudo) { Some(rule_node) => rule_node, None => return None }; @@ -673,8 +669,7 @@ impl Stylist { pub fn lazy_pseudo_rules<E>(&self, guards: &StylesheetGuards, element: &E, - pseudo: &PseudoElement, - pseudo_state: ElementState) + pseudo: &PseudoElement) -> Option<StrongRuleNode> where E: TElement { @@ -707,15 +702,16 @@ impl Stylist { } }; - let mut declarations = ApplicableDeclarationList::new(); + let mut matching_context = + MatchingContext::new(MatchingMode::ForStatelessPseudoElement, None); self.push_applicable_declarations(element, - None, + Some(pseudo), None, None, AnimationRules(None, None), - Some((pseudo, pseudo_state)), &mut declarations, + &mut matching_context, &mut set_selector_flags); if declarations.is_empty() { return None @@ -834,21 +830,20 @@ impl Stylist { /// /// This corresponds to `ElementRuleCollector` in WebKit. /// - /// The returned `StyleRelations` indicate hints about which kind of rules - /// have matched. + /// The `StyleRelations` recorded in `MatchingContext` indicate hints about + /// which kind of rules have matched. pub fn push_applicable_declarations<E, V, F>( &self, element: &E, - parent_bf: Option<&BloomFilter>, + pseudo_element: Option<&PseudoElement>, style_attribute: Option<&Arc<Locked<PropertyDeclarationBlock>>>, smil_override: Option<&Arc<Locked<PropertyDeclarationBlock>>>, animation_rules: AnimationRules, - pseudo_element: Option<(&PseudoElement, ElementState)>, applicable_declarations: &mut V, + context: &mut MatchingContext, flags_setter: &mut F) - -> StyleRelations where E: TElement, - V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>, + V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> + ::std::fmt::Debug, F: FnMut(&E, ElementSelectorFlags), { debug_assert!(!self.is_device_dirty); @@ -857,27 +852,36 @@ impl Stylist { debug_assert!(cfg!(feature = "gecko") || style_attribute.is_none() || pseudo_element.is_none(), "Style attributes do not apply to pseudo-elements"); - debug_assert!(pseudo_element.as_ref().map_or(true, |p| !p.0.is_precomputed())); + debug_assert!(pseudo_element.map_or(true, |p| !p.is_precomputed())); let map = match pseudo_element { - Some((ref pseudo, _)) => self.pseudos_map.get(pseudo).unwrap(), + Some(pseudo) => self.pseudos_map.get(pseudo).unwrap(), None => &self.element_map, }; - let mut relations = StyleRelations::empty(); + let is_implemented_pseudo = + element.implemented_pseudo_element().is_some(); + + // NB: This causes use to rule has pseudo selectors based on the + // properties of the originating element (which is fine, given the + // find_first_from_right usage). + let rule_hash_target = if is_implemented_pseudo { + element.closest_non_native_anonymous_ancestor().unwrap() + } else { + *element + }; debug!("Determining if style is shareable: pseudo: {}", pseudo_element.is_some()); // Step 1: Normal user-agent rules. map.user_agent.get_all_matching_rules(element, - pseudo_element, - parent_bf, + &rule_hash_target, applicable_declarations, - &mut relations, + context, flags_setter, CascadeLevel::UANormal); - debug!("UA normal: {:?}", relations); + debug!("UA normal: {:?}", context.relations); if pseudo_element.is_none() { // Step 2: Presentational hints. @@ -890,40 +894,46 @@ impl Stylist { } } // Never share style for elements with preshints - relations |= AFFECTED_BY_PRESENTATIONAL_HINTS; + context.relations |= AFFECTED_BY_PRESENTATIONAL_HINTS; } - debug!("preshints: {:?}", relations); + debug!("preshints: {:?}", context.relations); } - if element.matches_user_and_author_rules() { + // NB: the following condition, although it may look somewhat + // inaccurate, would be equivalent to something like: + // + // element.matches_user_and_author_rules() || + // (is_implemented_pseudo && + // rule_hash_target.matches_user_and_author_rules()) + // + // Which may be more what you would probably expect. + if rule_hash_target.matches_user_and_author_rules() { // Step 3: User and author normal rules. map.user.get_all_matching_rules(element, - pseudo_element, - parent_bf, + &rule_hash_target, applicable_declarations, - &mut relations, + context, flags_setter, CascadeLevel::UserNormal); - debug!("user normal: {:?}", relations); + debug!("user normal: {:?}", context.relations); map.author.get_all_matching_rules(element, - pseudo_element, - parent_bf, + &rule_hash_target, applicable_declarations, - &mut relations, + context, flags_setter, CascadeLevel::AuthorNormal); - debug!("author normal: {:?}", relations); + debug!("author normal: {:?}", context.relations); // Step 4: Normal style attributes. if let Some(sa) = style_attribute { - relations |= AFFECTED_BY_STYLE_ATTRIBUTE; + context.relations |= AFFECTED_BY_STYLE_ATTRIBUTE; Push::push( applicable_declarations, ApplicableDeclarationBlock::from_declarations(sa.clone(), CascadeLevel::StyleAttributeNormal)); } - debug!("style attr: {:?}", relations); + debug!("style attr: {:?}", context.relations); // Step 5: SMIL override. // Declarations from SVG SMIL animation elements. @@ -933,7 +943,7 @@ impl Stylist { ApplicableDeclarationBlock::from_declarations(so.clone(), CascadeLevel::SMILOverride)); } - debug!("SMIL: {:?}", relations); + debug!("SMIL: {:?}", context.relations); // Step 6: Animations. // The animations sheet (CSS animations, script-generated animations, @@ -944,7 +954,7 @@ impl Stylist { ApplicableDeclarationBlock::from_declarations(anim, CascadeLevel::Animations)); } - debug!("animation: {:?}", relations); + debug!("animation: {:?}", context.relations); } else { debug!("skipping non-agent rules"); } @@ -961,11 +971,8 @@ impl Stylist { applicable_declarations, ApplicableDeclarationBlock::from_declarations(anim, CascadeLevel::Transitions)); } - debug!("transition: {:?}", relations); - - debug!("push_applicable_declarations: shareable: {:?}", relations); - - relations + debug!("transition: {:?}", context.relations); + debug!("push_applicable_declarations: shareable: {:?}", context.relations); } /// Return whether the device is dirty, that is, whether the screen size or @@ -997,8 +1004,10 @@ impl Stylist { where E: TElement, F: FnMut(&E, ElementSelectorFlags), { - use selectors::matching::StyleRelations; - use selectors::matching::matches_selector; + // NB: `MatchingMode` doesn't really matter, given we don't share style + // between pseudos. + let mut matching_context = + MatchingContext::new(MatchingMode::Normal, Some(bloom)); // Note that, by the time we're revalidating, we're guaranteed that the // candidate and the entry have the same id, classes, and local name. @@ -1009,8 +1018,7 @@ impl Stylist { self.selectors_for_cache_revalidation.lookup(*element, &mut |selector| { results.push(matches_selector(selector, element, - Some(bloom), - &mut StyleRelations::empty(), + &mut matching_context, flags_setter)); true }); @@ -1081,17 +1089,19 @@ struct AttributeAndStateDependencyVisitor<'a>(&'a mut Stylist); impl<'a> SelectorVisitor for AttributeAndStateDependencyVisitor<'a> { type Impl = SelectorImpl; - fn visit_attribute_selector(&mut self, selector: &AttrSelector<Self::Impl>) -> bool { + fn visit_attribute_selector(&mut self, _ns: &NamespaceConstraint<&Namespace>, + name: &LocalName, lower_name: &LocalName) + -> bool { #[cfg(feature = "servo")] let style_lower_name = local_name!("style"); #[cfg(feature = "gecko")] let style_lower_name = atom!("style"); - if selector.lower_name == style_lower_name { + if *lower_name == style_lower_name { self.0.style_attribute_dependency = true; } else { - self.0.attribute_dependencies.insert(&selector.name); - self.0.attribute_dependencies.insert(&selector.lower_name); + self.0.attribute_dependencies.insert(&name); + self.0.attribute_dependencies.insert(&lower_name); } true } @@ -1150,13 +1160,9 @@ impl SelectorVisitor for RevalidationVisitor { /// concerned. fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool { match *s { - Component::AttrExists(_) | - Component::AttrEqual(_, _, _) | - Component::AttrIncludes(_, _) | - Component::AttrDashMatch(_, _) | - Component::AttrPrefixMatch(_, _) | - Component::AttrSubstringMatch(_, _) | - Component::AttrSuffixMatch(_, _) | + Component::AttributeInNoNamespaceExists { .. } | + Component::AttributeInNoNamespace { .. } | + Component::AttributeOther(_) | Component::Empty | Component::FirstChild | Component::LastChild | @@ -1189,6 +1195,7 @@ pub fn needs_revalidation(selector: &Selector<SelectorImpl>) -> bool { /// Map that contains the CSS rules for a specific PseudoElement /// (or lack of PseudoElement). #[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Debug)] struct PerPseudoElementSelectorMap { /// Rules from user agent stylesheets user_agent: SelectorMap<Rule>, @@ -1290,13 +1297,12 @@ impl SelectorMap<Rule> { /// Sort the Rules at the end to maintain cascading order. pub fn get_all_matching_rules<E, V, F>(&self, element: &E, - pseudo_element: Option<(&PseudoElement, ElementState)>, - parent_bf: Option<&BloomFilter>, + rule_hash_target: &E, matching_rules_list: &mut V, - relations: &mut StyleRelations, + context: &mut MatchingContext, flags_setter: &mut F, cascade_level: CascadeLevel) - where E: Element<Impl=SelectorImpl>, + where E: TElement, V: VecLike<ApplicableDeclarationBlock>, F: FnMut(&E, ElementSelectorFlags), { @@ -1306,46 +1312,38 @@ impl SelectorMap<Rule> { // At the end, we're going to sort the rules that we added, so remember where we began. let init_len = matching_rules_list.len(); - if let Some(id) = element.get_id() { + if let Some(id) = rule_hash_target.get_id() { SelectorMap::get_matching_rules_from_hash(element, - pseudo_element, - parent_bf, &self.id_hash, &id, matching_rules_list, - relations, + context, flags_setter, cascade_level) } - element.each_class(|class| { + rule_hash_target.each_class(|class| { SelectorMap::get_matching_rules_from_hash(element, - pseudo_element, - parent_bf, &self.class_hash, class, matching_rules_list, - relations, + context, flags_setter, cascade_level); }); SelectorMap::get_matching_rules_from_hash(element, - pseudo_element, - parent_bf, &self.local_name_hash, - element.get_local_name(), + rule_hash_target.get_local_name(), matching_rules_list, - relations, + context, flags_setter, cascade_level); SelectorMap::get_matching_rules(element, - pseudo_element, - parent_bf, &self.other, matching_rules_list, - relations, + context, flags_setter, cascade_level); @@ -1366,8 +1364,8 @@ impl SelectorMap<Rule> { let mut rules_list = vec![]; for rule in self.other.iter() { - if rule.selector.inner.complex.iter_raw().next().is_none() { - rules_list.push(rule.to_applicable_declaration_block(cascade_level)); + if rule.selector.is_universal() { + rules_list.push(rule.to_applicable_declaration_block(cascade_level)) } } @@ -1379,15 +1377,13 @@ impl SelectorMap<Rule> { fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector, F>( element: &E, - pseudo_element: Option<(&PseudoElement, ElementState)>, - parent_bf: Option<&BloomFilter>, hash: &FnvHashMap<Str, Vec<Rule>>, key: &BorrowedStr, matching_rules: &mut Vector, - relations: &mut StyleRelations, + context: &mut MatchingContext, flags_setter: &mut F, cascade_level: CascadeLevel) - where E: Element<Impl=SelectorImpl>, + where E: TElement, Str: Borrow<BorrowedStr> + Eq + Hash, BorrowedStr: Eq + Hash, Vector: VecLike<ApplicableDeclarationBlock>, @@ -1395,11 +1391,9 @@ impl SelectorMap<Rule> { { if let Some(rules) = hash.get(key) { SelectorMap::get_matching_rules(element, - pseudo_element, - parent_bf, rules, matching_rules, - relations, + context, flags_setter, cascade_level) } @@ -1407,43 +1401,19 @@ impl SelectorMap<Rule> { /// Adds rules in `rules` that match `element` to the `matching_rules` list. fn get_matching_rules<E, V, F>(element: &E, - pseudo_element: Option<(&PseudoElement, ElementState)>, - parent_bf: Option<&BloomFilter>, rules: &[Rule], matching_rules: &mut V, - relations: &mut StyleRelations, + context: &mut MatchingContext, flags_setter: &mut F, cascade_level: CascadeLevel) - where E: Element<Impl=SelectorImpl>, + where E: TElement, V: VecLike<ApplicableDeclarationBlock>, F: FnMut(&E, ElementSelectorFlags), { - for rule in rules.iter() { - debug_assert_eq!(rule.selector.pseudo_element.is_some(), - pseudo_element.is_some(), - "Testing pseudo-elements against the wrong map"); - - if let Some((pseudo, pseudo_state)) = pseudo_element { - let pseudo_selector = - rule.selector.pseudo_element.as_ref().unwrap(); - - debug_assert_eq!(pseudo_selector.pseudo_element(), pseudo, - "Testing pseudo-element against the wrong entry"); - - let state = pseudo_selector.state(); - - // NB: We only allow a subset of the flags here, so using - // contains for them is fine, (and it's necessary, to handle - // multiple state flags properly). - if !state.is_empty() && !pseudo_state.contains(state) { - continue; - } - } - + for rule in rules { if matches_selector(&rule.selector.inner, element, - parent_bf, - relations, + context, flags_setter) { matching_rules.push( rule.to_applicable_declaration_block(cascade_level)); @@ -1600,45 +1570,70 @@ impl<T> SelectorMap<T> where T: Clone + Borrow<SelectorInner<SelectorImpl>> { } } +/// Searches the selector from right to left, beginning to the left of the +/// ::pseudo-element (if any), and ending at the first combinator. +/// +/// The first non-None value returned from |f| is returned. +/// +/// Effectively, pseudo-elements are ignored, given only state pseudo-classes +/// may appear before them. +fn find_from_right<F, R>(selector: &SelectorInner<SelectorImpl>, mut f: F) -> Option<R> + where F: FnMut(&Component<SelectorImpl>) -> Option<R>, +{ + let mut iter = selector.complex.iter(); + for ss in &mut iter { + if let Some(r) = f(ss) { + return Some(r) + } + } + + if iter.next_sequence() == Some(Combinator::PseudoElement) { + for ss in &mut iter { + if let Some(r) = f(ss) { + return Some(r) + } + } + } + + None +} + /// Retrieve the first ID name in the selector, or None otherwise. pub fn get_id_name(selector: &SelectorInner<SelectorImpl>) -> Option<Atom> { - for ss in selector.complex.iter() { + find_from_right(selector, |ss| { // TODO(pradeep): Implement case-sensitivity based on the // document type and quirks mode. if let Component::ID(ref id) = *ss { return Some(id.clone()); } - } - - None + None + }) } /// Retrieve the FIRST class name in the selector, or None otherwise. pub fn get_class_name(selector: &SelectorInner<SelectorImpl>) -> Option<Atom> { - for ss in selector.complex.iter() { + find_from_right(selector, |ss| { // TODO(pradeep): Implement case-sensitivity based on the // document type and quirks mode. if let Component::Class(ref class) = *ss { return Some(class.clone()); } - } - - None + None + }) } /// Retrieve the name if it is a type selector, or None otherwise. pub fn get_local_name(selector: &SelectorInner<SelectorImpl>) -> Option<LocalNameSelector<SelectorImpl>> { - for ss in selector.complex.iter() { + find_from_right(selector, |ss| { if let Component::LocalName(ref n) = *ss { return Some(LocalNameSelector { name: n.name.clone(), lower_name: n.lower_name.clone(), }) } - } - - None + None + }) } /// A rule, that wraps a style rule, but represents a single selector of the @@ -1668,7 +1663,7 @@ impl Borrow<SelectorInner<SelectorImpl>> for Rule { impl Rule { /// Returns the specificity of the rule. pub fn specificity(&self) -> u32 { - self.selector.specificity + self.selector.specificity() } fn to_applicable_declaration_block(&self, level: CascadeLevel) -> ApplicableDeclarationBlock { diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index 1bab68160be..89702468570 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -8,6 +8,7 @@ use app_units::{Au, AU_PER_PX}; use ordered_float::NotNaN; use std::fmt; use style_traits::ToCss; +use style_traits::values::specified::AllowedLengthType; use super::{Number, ToComputedValue, Context}; use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified}; use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength, ViewportPercentageLength}; @@ -48,7 +49,7 @@ impl ToComputedValue for specified::Length { fn to_computed_value(&self, context: &Context) -> Au { match *self { specified::Length::NoCalc(l) => l.to_computed_value(context), - specified::Length::Calc(range, ref calc) => range.clamp(calc.to_computed_value(context).length()), + specified::Length::Calc(ref calc) => calc.to_computed_value(context).length(), } } @@ -62,14 +63,43 @@ impl ToComputedValue for specified::Length { #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[allow(missing_docs)] pub struct CalcLengthOrPercentage { - pub length: Au, + pub clamping_mode: AllowedLengthType, + length: Au, pub percentage: Option<CSSFloat>, } impl CalcLengthOrPercentage { + /// Returns a new `CalcLengthOrPercentage`. + #[inline] + pub fn new(length: Au, percentage: Option<CSSFloat>) -> Self { + Self::with_clamping_mode(length, percentage, AllowedLengthType::All) + } + + /// Returns a new `CalcLengthOrPercentage` with a specific clamping mode. + #[inline] + pub fn with_clamping_mode(length: Au, + percentage: Option<CSSFloat>, + clamping_mode: AllowedLengthType) + -> Self { + Self { + clamping_mode: clamping_mode, + length: length, + percentage: percentage, + } + } + + /// Returns this `calc()` as a `<length>`. + /// + /// Panics in debug mode if a percentage is present in the expression. #[inline] - #[allow(missing_docs)] pub fn length(&self) -> Au { + debug_assert!(self.percentage.is_none()); + self.clamping_mode.clamp(self.length) + } + + /// Returns the `<length>` component of this `calc()`, unclamped. + #[inline] + pub fn unclamped_length(&self) -> Au { self.length } @@ -81,10 +111,12 @@ impl CalcLengthOrPercentage { /// If there are special rules for computing percentages in a value (e.g. the height property), /// they apply whenever a calc() expression contains percentages. - pub fn to_computed(&self, container_len: Option<Au>) -> Option<Au> { + pub fn to_used_value(&self, container_len: Option<Au>) -> Option<Au> { match (container_len, self.percentage) { - (Some(len), Some(percent)) => Some(self.length + len.scale_by(percent)), - (_, None) => Some(self.length), + (Some(len), Some(percent)) => { + Some(self.clamping_mode.clamp(self.length + len.scale_by(percent))) + }, + (_, None) => Some(self.length()), _ => None, } } @@ -94,16 +126,10 @@ impl From<LengthOrPercentage> for CalcLengthOrPercentage { fn from(len: LengthOrPercentage) -> CalcLengthOrPercentage { match len { LengthOrPercentage::Percentage(this) => { - CalcLengthOrPercentage { - length: Au(0), - percentage: Some(this), - } + CalcLengthOrPercentage::new(Au(0), Some(this)) } LengthOrPercentage::Length(this) => { - CalcLengthOrPercentage { - length: this, - percentage: None, - } + CalcLengthOrPercentage::new(this, None) } LengthOrPercentage::Calc(this) => { this @@ -116,16 +142,10 @@ impl From<LengthOrPercentageOrAuto> for Option<CalcLengthOrPercentage> { fn from(len: LengthOrPercentageOrAuto) -> Option<CalcLengthOrPercentage> { match len { LengthOrPercentageOrAuto::Percentage(this) => { - Some(CalcLengthOrPercentage { - length: Au(0), - percentage: Some(this), - }) + Some(CalcLengthOrPercentage::new(Au(0), Some(this))) } LengthOrPercentageOrAuto::Length(this) => { - Some(CalcLengthOrPercentage { - length: this, - percentage: None, - }) + Some(CalcLengthOrPercentage::new(this, None)) } LengthOrPercentageOrAuto::Calc(this) => { Some(this) @@ -176,6 +196,7 @@ impl ToComputedValue for specified::CalcLengthOrPercentage { } CalcLengthOrPercentage { + clamping_mode: self.clamping_mode, length: length, percentage: self.percentage, } @@ -184,6 +205,7 @@ impl ToComputedValue for specified::CalcLengthOrPercentage { #[inline] fn from_computed_value(computed: &CalcLengthOrPercentage) -> Self { specified::CalcLengthOrPercentage { + clamping_mode: computed.clamping_mode, absolute: Some(computed.length), percentage: computed.percentage, ..Default::default() @@ -232,7 +254,18 @@ impl LengthOrPercentage { match *self { Length(l) => (l, NotNaN::new(0.0).unwrap()), Percentage(p) => (Au(0), NotNaN::new(p).unwrap()), - Calc(c) => (c.length(), NotNaN::new(c.percentage()).unwrap()), + Calc(c) => (c.unclamped_length(), NotNaN::new(c.percentage()).unwrap()), + } + } + + /// Returns the used value. + pub fn to_used_value(&self, containing_length: Au) -> Au { + match *self { + LengthOrPercentage::Length(length) => length, + LengthOrPercentage::Percentage(p) => containing_length.scale_by(p), + LengthOrPercentage::Calc(ref calc) => { + calc.to_used_value(Some(containing_length)).unwrap() + }, } } } @@ -481,6 +514,18 @@ pub enum LengthOrPercentageOrNone { None, } +impl LengthOrPercentageOrNone { + /// Returns the used value. + pub fn to_used_value(&self, containing_length: Au) -> Option<Au> { + match *self { + LengthOrPercentageOrNone::None => None, + LengthOrPercentageOrNone::Length(length) => Some(length), + LengthOrPercentageOrNone::Percentage(percent) => Some(containing_length.scale_by(percent)), + LengthOrPercentageOrNone::Calc(ref calc) => calc.to_used_value(Some(containing_length)), + } + } +} + impl fmt::Debug for LengthOrPercentageOrNone { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 02a71c6b313..7101192be1a 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -19,6 +19,7 @@ use super::{CSSFloat, CSSInteger, RGBA}; use super::generics::BorderRadiusSize as GenericBorderRadiusSize; use super::specified; use super::specified::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize}; +use super::specified::grid::TrackList as GenericTrackList; pub use app_units::Au; pub use cssparser::Color as CSSColor; @@ -595,6 +596,13 @@ pub type TrackBreadth = GenericTrackBreadth<LengthOrPercentage>; /// The computed value of a grid `<track-size>` pub type TrackSize = GenericTrackSize<LengthOrPercentage>; +/// The computed value of a grid `<track-list>` +/// (could also be `<auto-track-list>` or `<explicit-track-list>`) +pub type TrackList = GenericTrackList<TrackSize>; + +/// `<track-list> | none` +pub type TrackListOrNone = Either<TrackList, None_>; + impl ClipRectOrAuto { /// Return an auto (default for clip-rect and image-region) value pub fn auto() -> Self { diff --git a/components/style/values/specified/calc.rs b/components/style/values/specified/calc.rs index 4f49df256e7..4db36e998c2 100644 --- a/components/style/values/specified/calc.rs +++ b/components/style/values/specified/calc.rs @@ -12,6 +12,7 @@ use parser::ParserContext; use std::ascii::AsciiExt; use std::fmt; use style_traits::ToCss; +use style_traits::values::specified::AllowedLengthType; use values::{CSSInteger, CSSFloat, HasViewportPercentage}; use values::specified::{Angle, Time}; use values::specified::length::{FontRelativeLength, NoCalcLength, ViewportPercentageLength}; @@ -63,6 +64,7 @@ pub enum CalcUnit { #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[allow(missing_docs)] pub struct CalcLengthOrPercentage { + pub clamping_mode: AllowedLengthType, pub absolute: Option<Au>, pub vw: Option<CSSFloat>, pub vh: Option<CSSFloat>, @@ -271,8 +273,12 @@ impl CalcNode { /// Tries to simplify this expression into a `<length>` or `<percentage`> /// value. - fn to_length_or_percentage(&self) -> Result<CalcLengthOrPercentage, ()> { - let mut ret = CalcLengthOrPercentage::default(); + fn to_length_or_percentage(&self, clamping_mode: AllowedLengthType) + -> Result<CalcLengthOrPercentage, ()> { + let mut ret = CalcLengthOrPercentage { + clamping_mode: clamping_mode, + .. Default::default() + }; self.add_length_or_percentage_to(&mut ret, 1.0)?; Ok(ret) } @@ -498,21 +504,23 @@ impl CalcNode { /// Convenience parsing function for `<length> | <percentage>`. pub fn parse_length_or_percentage( context: &ParserContext, - input: &mut Parser) + input: &mut Parser, + clamping_mode: AllowedLengthType) -> Result<CalcLengthOrPercentage, ()> { Self::parse(context, input, CalcUnit::LengthOrPercentage)? - .to_length_or_percentage() + .to_length_or_percentage(clamping_mode) } /// Convenience parsing function for `<length>`. pub fn parse_length( context: &ParserContext, - input: &mut Parser) + input: &mut Parser, + clamping_mode: AllowedLengthType) -> Result<CalcLengthOrPercentage, ()> { Self::parse(context, input, CalcUnit::Length)? - .to_length_or_percentage() + .to_length_or_percentage(clamping_mode) } /// Convenience parsing function for `<number>`. diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index 4c0d50f0e54..0599563a494 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -53,13 +53,19 @@ mod gecko { no_viewport_percentage!(Color); + impl From<CSSParserColor> for Color { + fn from(value: CSSParserColor) -> Self { + match value { + CSSParserColor::CurrentColor => Color::CurrentColor, + CSSParserColor::RGBA(x) => Color::RGBA(x), + } + } + } + impl Parse for Color { fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> { if let Ok(value) = input.try(CSSParserColor::parse) { - match value { - CSSParserColor::CurrentColor => Ok(Color::CurrentColor), - CSSParserColor::RGBA(x) => Ok(Color::RGBA(x)), - } + Ok(value.into()) } else if let Ok(system) = input.try(SystemColor::parse) { Ok(Color::System(system)) } else { diff --git a/components/style/values/specified/grid.rs b/components/style/values/specified/grid.rs index 92dbc4fbcaa..45f888d9702 100644 --- a/components/style/values/specified/grid.rs +++ b/components/style/values/specified/grid.rs @@ -4,21 +4,20 @@ //! Necessary types for [grid](https://drafts.csswg.org/css-grid/). -use cssparser::{Parser, Token}; +use cssparser::{Parser, Token, serialize_identifier}; use parser::{Parse, ParserContext}; +use std::{fmt, mem, usize}; use std::ascii::AsciiExt; -use std::fmt; use style_traits::ToCss; -use values::{CSSFloat, HasViewportPercentage}; -use values::computed::{ComputedValueAsSpecified, Context, ToComputedValue}; -use values::specified::LengthOrPercentage; +use values::{CSSFloat, CustomIdent, Either, HasViewportPercentage}; +use values::computed::{self, ComputedValueAsSpecified, Context, ToComputedValue}; +use values::specified::{Integer, LengthOrPercentage}; #[derive(PartialEq, Clone, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] /// A `<grid-line>` type. /// /// https://drafts.csswg.org/css-grid/#typedef-grid-row-start-grid-line -#[allow(missing_docs)] pub struct GridLine { /// Flag to check whether it's a `span` keyword. pub is_span: bool, @@ -27,7 +26,14 @@ pub struct GridLine { /// https://drafts.csswg.org/css-grid/#grid-placement-slot pub ident: Option<String>, /// Denotes the nth grid line from grid item's placement. - pub integer: Option<i32>, + pub line_num: Option<Integer>, +} + +impl GridLine { + /// Check whether this `<grid-line>` represents an `auto` value. + pub fn is_auto(&self) -> bool { + self.ident.is_none() && self.line_num.is_none() && !self.is_span + } } impl Default for GridLine { @@ -35,27 +41,28 @@ impl Default for GridLine { GridLine { is_span: false, ident: None, - integer: None, + line_num: None, } } } impl ToCss for GridLine { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - if !self.is_span && self.ident.is_none() && self.integer.is_none() { + if self.is_auto() { return dest.write_str("auto") } if self.is_span { - try!(dest.write_str("span")); + dest.write_str("span")?; } - if let Some(i) = self.integer { - try!(write!(dest, " {}", i)); + if let Some(i) = self.line_num { + write!(dest, " {}", i.value)?; } if let Some(ref s) = self.ident { - try!(write!(dest, " {}", s)); + dest.write_str(" ")?; + serialize_identifier(s, dest)?; } Ok(()) @@ -63,7 +70,7 @@ impl ToCss for GridLine { } impl Parse for GridLine { - fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { + fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { let mut grid_line = Default::default(); if input.try(|i| i.expect_ident_matching("auto")).is_ok() { return Ok(grid_line) @@ -71,17 +78,17 @@ impl Parse for GridLine { for _ in 0..3 { // Maximum possible entities for <grid-line> if input.try(|i| i.expect_ident_matching("span")).is_ok() { - if grid_line.is_span { - return Err(()) + if grid_line.is_span || grid_line.line_num.is_some() || grid_line.ident.is_some() { + return Err(()) // span (if specified) should be first } - grid_line.is_span = true; - } else if let Ok(i) = input.try(|i| i.expect_integer()) { - if i == 0 || grid_line.integer.is_some() { + grid_line.is_span = true; // span (if specified) should be first + } else if let Ok(i) = input.try(|i| Integer::parse(context, i)) { + if i.value == 0 || grid_line.line_num.is_some() { return Err(()) } - grid_line.integer = Some(i); + grid_line.line_num = Some(i); } else if let Ok(name) = input.try(|i| i.expect_ident()) { - if grid_line.ident.is_some() { + if grid_line.ident.is_some() || CustomIdent::from_ident((&*name).into(), &[]).is_err() { return Err(()) } grid_line.ident = Some(name.into_owned()); @@ -90,13 +97,19 @@ impl Parse for GridLine { } } + if grid_line.is_auto() { + return Err(()) + } + if grid_line.is_span { - if let Some(i) = grid_line.integer { - if i < 0 { // disallow negative integers for grid spans + if let Some(i) = grid_line.line_num { + if i.value <= 0 { // disallow negative integers for grid spans return Err(()) } + } else if grid_line.ident.is_some() { // integer could be omitted + grid_line.line_num = Some(Integer::new(1)); } else { - grid_line.integer = Some(1); + return Err(()) } } @@ -128,9 +141,22 @@ pub enum TrackBreadth<L> { Keyword(TrackKeyword), } +impl<L> TrackBreadth<L> { + /// Check whether this is a `<fixed-breadth>` (i.e., it only has `<length-percentage>`) + /// + /// https://drafts.csswg.org/css-grid/#typedef-fixed-breadth + #[inline] + pub fn is_fixed(&self) -> bool { + match *self { + TrackBreadth::Breadth(ref _lop) => true, + _ => false, + } + } +} + /// Parse a single flexible length. pub fn parse_flex(input: &mut Parser) -> Result<CSSFloat, ()> { - match try!(input.next()) { + match input.next()? { Token::Dimension(ref value, ref unit) if unit.eq_ignore_ascii_case("fr") && value.value.is_sign_positive() => Ok(value.value), _ => Err(()), @@ -215,6 +241,32 @@ pub enum TrackSize<L> { FitContent(L), } +impl<L> TrackSize<L> { + /// Check whether this is a `<fixed-size>` + /// + /// https://drafts.csswg.org/css-grid/#typedef-fixed-size + pub fn is_fixed(&self) -> bool { + match *self { + TrackSize::Breadth(ref breadth) => breadth.is_fixed(), + // For minmax function, it could be either + // minmax(<fixed-breadth>, <track-breadth>) or minmax(<inflexible-breadth>, <fixed-breadth>), + // and since both variants are a subset of minmax(<inflexible-breadth>, <track-breadth>), we only + // need to make sure that they're fixed. So, we don't have to modify the parsing function. + TrackSize::MinMax(ref breadth_1, ref breadth_2) => { + if breadth_1.is_fixed() { + return true // the second value is always a <track-breadth> + } + + match *breadth_1 { + TrackBreadth::Flex(_) => false, // should be <inflexible-breadth> at this point + _ => breadth_2.is_fixed(), + } + }, + TrackSize::FitContent(_) => false, + } + } +} + impl<L> Default for TrackSize<L> { fn default() -> Self { TrackSize::Breadth(TrackBreadth::Keyword(TrackKeyword::Auto)) @@ -233,19 +285,19 @@ impl Parse for TrackSize<LengthOrPercentage> { match input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) { Ok(lop) => TrackBreadth::Breadth(lop), Err(..) => { - let keyword = try!(TrackKeyword::parse(input)); + let keyword = TrackKeyword::parse(input)?; TrackBreadth::Keyword(keyword) } }; - try!(input.expect_comma()); - Ok(TrackSize::MinMax(inflexible_breadth, try!(TrackBreadth::parse(context, input)))) + input.expect_comma()?; + Ok(TrackSize::MinMax(inflexible_breadth, TrackBreadth::parse(context, input)?)) }); } - try!(input.expect_function_matching("fit-content")); - // FIXME(emilio): This needs a parse_nested_block, doesn't it? - Ok(try!(LengthOrPercentage::parse(context, input).map(TrackSize::FitContent))) + input.expect_function_matching("fit-content")?; + let lop = input.parse_nested_block(|i| LengthOrPercentage::parse_non_negative(context, i))?; + Ok(TrackSize::FitContent(lop)) } } @@ -254,15 +306,15 @@ impl<L: ToCss> ToCss for TrackSize<L> { match *self { TrackSize::Breadth(ref b) => b.to_css(dest), TrackSize::MinMax(ref infexible, ref flexible) => { - try!(dest.write_str("minmax(")); - try!(infexible.to_css(dest)); - try!(dest.write_str(",")); - try!(flexible.to_css(dest)); + dest.write_str("minmax(")?; + infexible.to_css(dest)?; + dest.write_str(", ")?; + flexible.to_css(dest)?; dest.write_str(")") }, TrackSize::FitContent(ref lop) => { - try!(dest.write_str("fit-content(")); - try!(lop.to_css(dest)); + dest.write_str("fit-content(")?; + lop.to_css(dest)?; dest.write_str(")") }, } @@ -286,7 +338,13 @@ impl<L: ToComputedValue> ToComputedValue for TrackSize<L> { #[inline] fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { match *self { - TrackSize::Breadth(ref b) => TrackSize::Breadth(b.to_computed_value(context)), + TrackSize::Breadth(ref b) => match *b { + // <flex> outside `minmax()` expands to `mimmax(auto, <flex>)` + // https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-flex + TrackBreadth::Flex(f) => + TrackSize::MinMax(TrackBreadth::Keyword(TrackKeyword::Auto), TrackBreadth::Flex(f)), + _ => TrackSize::Breadth(b.to_computed_value(context)), + }, TrackSize::MinMax(ref b_1, ref b_2) => TrackSize::MinMax(b_1.to_computed_value(context), b_2.to_computed_value(context)), TrackSize::FitContent(ref lop) => TrackSize::FitContent(lop.to_computed_value(context)), @@ -306,3 +364,532 @@ impl<L: ToComputedValue> ToComputedValue for TrackSize<L> { } } } + +/// Parse the grid line names into a vector of owned strings. +/// +/// https://drafts.csswg.org/css-grid/#typedef-line-names +pub fn parse_line_names(input: &mut Parser) -> Result<Vec<String>, ()> { + input.expect_square_bracket_block()?; + input.parse_nested_block(|input| { + let mut values = vec![]; + while let Ok(ident) = input.try(|i| i.expect_ident()) { + if CustomIdent::from_ident((&*ident).into(), &["span"]).is_err() { + return Err(()) + } + + values.push(ident.into_owned()); + } + + Ok(values) + }) +} + +fn concat_serialize_idents<W>(prefix: &str, suffix: &str, + slice: &[String], sep: &str, dest: &mut W) -> fmt::Result + where W: fmt::Write +{ + if let Some((ref first, rest)) = slice.split_first() { + dest.write_str(prefix)?; + serialize_identifier(first, dest)?; + for thing in rest { + dest.write_str(sep)?; + serialize_identifier(thing, dest)?; + } + + dest.write_str(suffix)?; + } + + Ok(()) +} + +/// The initial argument of the `repeat` function. +/// +/// https://drafts.csswg.org/css-grid/#typedef-track-repeat +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum RepeatCount { + /// A positive integer. This is allowed only for `<track-repeat>` and `<fixed-repeat>` + Number(Integer), + /// An `<auto-fill>` keyword allowed only for `<auto-repeat>` + AutoFill, + /// An `<auto-fit>` keyword allowed only for `<auto-repeat>` + AutoFit, +} + +impl Parse for RepeatCount { + fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { + if let Ok(i) = input.try(|i| Integer::parse(context, i)) { + if i.value > 0 { + Ok(RepeatCount::Number(i)) + } else { + Err(()) + } + } else { + match_ignore_ascii_case! { &input.expect_ident()?, + "auto-fill" => Ok(RepeatCount::AutoFill), + "auto-fit" => Ok(RepeatCount::AutoFit), + _ => Err(()), + } + } + } +} + +impl ToCss for RepeatCount { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + RepeatCount::Number(ref c) => c.to_css(dest), + RepeatCount::AutoFill => dest.write_str("auto-fill"), + RepeatCount::AutoFit => dest.write_str("auto-fit"), + } + } +} + +impl ComputedValueAsSpecified for RepeatCount {} +no_viewport_percentage!(RepeatCount); + +/// The type of `repeat` function (only used in parsing). +/// +/// https://drafts.csswg.org/css-grid/#typedef-track-repeat +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +enum RepeatType { + /// [`<auto-repeat>`](https://drafts.csswg.org/css-grid/#typedef-auto-repeat) + Auto, + /// [`<track-repeat>`](https://drafts.csswg.org/css-grid/#typedef-track-repeat) + Normal, + /// [`<fixed-repeat>`](https://drafts.csswg.org/css-grid/#typedef-fixed-repeat) + Fixed, +} + +/// The structure containing `<line-names>` and `<track-size>` values. +/// +/// It can also hold `repeat()` function parameters, which expands into the respective +/// values in its computed form. +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct TrackRepeat<L> { + /// The number of times for the value to be repeated (could also be `auto-fit` or `auto-fill`) + pub count: RepeatCount, + /// `<line-names>` accompanying `<track_size>` values. + /// + /// If there's no `<line-names>`, then it's represented by an empty vector. + /// For N `<track-size>` values, there will be N+1 `<line-names>`, and so this vector's + /// length is always one value more than that of the `<track-size>`. + pub line_names: Vec<Vec<String>>, + /// `<track-size>` values. + pub track_sizes: Vec<TrackSize<L>>, +} + +impl TrackRepeat<LengthOrPercentage> { + fn parse_with_repeat_type(context: &ParserContext, input: &mut Parser) + -> Result<(TrackRepeat<LengthOrPercentage>, RepeatType), ()> { + input.try(|i| i.expect_function_matching("repeat")).and_then(|_| { + input.parse_nested_block(|input| { + let count = RepeatCount::parse(context, input)?; + input.expect_comma()?; + + let is_auto = count == RepeatCount::AutoFit || count == RepeatCount::AutoFill; + let mut repeat_type = if is_auto { + RepeatType::Auto + } else { // <fixed-size> is a subset of <track_size>, so it should work for both + RepeatType::Fixed + }; + + let mut names = vec![]; + let mut values = vec![]; + let mut current_names; + + loop { + current_names = input.try(parse_line_names).unwrap_or(vec![]); + if let Ok(track_size) = input.try(|i| TrackSize::parse(context, i)) { + if !track_size.is_fixed() { + if is_auto { + return Err(()) // should be <fixed-size> for <auto-repeat> + } + + if repeat_type == RepeatType::Fixed { + repeat_type = RepeatType::Normal // <track-size> for sure + } + } + + values.push(track_size); + names.push(current_names); + } else { + if values.is_empty() { + return Err(()) // expecting at least one <track-size> + } + + names.push(current_names); // final `<line-names>` + break // no more <track-size>, breaking + } + } + + let repeat = TrackRepeat { + count: count, + track_sizes: values, + line_names: names, + }; + + Ok((repeat, repeat_type)) + }) + }) + } +} + +impl<L: ToCss> ToCss for TrackRepeat<L> { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + dest.write_str("repeat(")?; + self.count.to_css(dest)?; + dest.write_str(", ")?; + + let mut line_names_iter = self.line_names.iter(); + for (i, (ref size, ref names)) in self.track_sizes.iter() + .zip(&mut line_names_iter).enumerate() { + if i > 0 { + dest.write_str(" ")?; + } + + concat_serialize_idents("[", "] ", names, " ", dest)?; + size.to_css(dest)?; + } + + if let Some(line_names_last) = line_names_iter.next() { + concat_serialize_idents(" [", "]", line_names_last, " ", dest)?; + } + + dest.write_str(")")?; + Ok(()) + } +} + +impl HasViewportPercentage for TrackRepeat<LengthOrPercentage> { + #[inline] + fn has_viewport_percentage(&self) -> bool { + self.track_sizes.iter().any(|ref v| v.has_viewport_percentage()) + } +} + +impl<L: ToComputedValue> ToComputedValue for TrackRepeat<L> { + type ComputedValue = TrackRepeat<L::ComputedValue>; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + // If the repeat count is numeric, then expand the values and merge accordingly. + if let RepeatCount::Number(num) = self.count { + let mut line_names = vec![]; + let mut track_sizes = vec![]; + let mut prev_names = vec![]; + + for _ in 0..num.value { + let mut names_iter = self.line_names.iter(); + for (size, names) in self.track_sizes.iter().zip(&mut names_iter) { + prev_names.extend_from_slice(&names); + line_names.push(mem::replace(&mut prev_names, vec![])); + track_sizes.push(size.to_computed_value(context)); + } + + if let Some(names) = names_iter.next() { + prev_names.extend_from_slice(&names); + } + } + + line_names.push(prev_names); + TrackRepeat { + count: self.count, + track_sizes: track_sizes, + line_names: line_names, + } + + } else { // if it's auto-fit/auto-fill, then it's left to the layout. + TrackRepeat { + count: self.count, + track_sizes: self.track_sizes.iter() + .map(|l| l.to_computed_value(context)) + .collect(), + line_names: self.line_names.clone(), + } + } + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + TrackRepeat { + count: computed.count, + track_sizes: computed.track_sizes.iter() + .map(ToComputedValue::from_computed_value) + .collect(), + line_names: computed.line_names.clone(), + } + } +} + +/// The type of a `<track-list>` as determined during parsing. +/// +/// https://drafts.csswg.org/css-grid/#typedef-track-list +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum TrackListType { + /// [`<auto-track-list>`](https://drafts.csswg.org/css-grid/#typedef-auto-track-list) + /// + /// If this type exists, then the value at the index in `line_names` field in `TrackList` + /// has the `<line-names>?` list that comes before `<auto-repeat>`. If it's a specified value, + /// then the `repeat()` function (that follows the line names list) is also at the given index + /// in `values` field. On the contrary, if it's a computed value, then the `repeat()` function + /// is in the `auto_repeat` field. + Auto(u16), + /// [`<track-list>`](https://drafts.csswg.org/css-grid/#typedef-track-list) + Normal, + /// [`<explicit-track-list>`](https://drafts.csswg.org/css-grid/#typedef-explicit-track-list) + /// + /// Note that this is a subset of the normal `<track-list>`, and so it could be used in place + /// of the latter. + Explicit, +} + +/// A grid `<track-list>` type. +/// +/// https://drafts.csswg.org/css-grid/#typedef-track-list +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct TrackList<T> { + /// The type of this `<track-list>` (auto, explicit or general). + /// + /// In order to avoid parsing the same value multiple times, this does a single traversal + /// and arrives at the type of value it has parsed (or bails out gracefully with an error). + pub list_type: TrackListType, + /// A vector of `<track-size> | <track-repeat>` values. In its specified form, it may contain + /// any value, but once it's computed, it contains only `<track_size>` values. + /// + /// Note that this may also contain `<auto-repeat>` at an index. If it exists, it's + /// given by the index in `TrackListType::Auto` + pub values: Vec<T>, + /// `<line-names>` accompanying `<track-size> | <track-repeat>` values. + /// + /// If there's no `<line-names>`, then it's represented by an empty vector. + /// For N values, there will be N+1 `<line-names>`, and so this vector's + /// length is always one value more than that of the `<track-size>`. + pub line_names: Vec<Vec<String>>, + /// `<auto-repeat>` value after computation. This field is necessary, because + /// the `values` field (after computation) will only contain `<track-size>` values, and + /// we need something to represent this function. + pub auto_repeat: Option<TrackRepeat<computed::LengthOrPercentage>>, +} + +/// Either a `<track-size>` or `<track-repeat>` component of `<track-list>` +/// +/// This is required only for the specified form of `<track-list>`, and will become +/// `TrackSize<LengthOrPercentage>` in its computed form. +pub type TrackSizeOrRepeat = Either<TrackSize<LengthOrPercentage>, TrackRepeat<LengthOrPercentage>>; + +impl Parse for TrackList<TrackSizeOrRepeat> { + fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { + let mut current_names; + let mut names = vec![]; + let mut values = vec![]; + + let mut list_type = TrackListType::Explicit; // assume it's the simplest case + // marker to check whether we've already encountered <auto-repeat> along the way + let mut is_auto = false; + // assume that everything is <fixed-size>. This flag is useful when we encounter <auto-repeat> + let mut atleast_one_not_fixed = false; + + loop { + current_names = input.try(parse_line_names).unwrap_or(vec![]); + if let Ok(track_size) = input.try(|i| TrackSize::parse(context, i)) { + if !track_size.is_fixed() { + atleast_one_not_fixed = true; + if is_auto { + return Err(()) // <auto-track-list> only accepts <fixed-size> and <fixed-repeat> + } + } + + names.push(current_names); + values.push(Either::First(track_size)); + } else if let Ok((repeat, type_)) = input.try(|i| TrackRepeat::parse_with_repeat_type(context, i)) { + if list_type == TrackListType::Explicit { + list_type = TrackListType::Normal; // <explicit-track-list> doesn't contain repeat() + } + + match type_ { + RepeatType::Normal => { + atleast_one_not_fixed = true; + if is_auto { // only <fixed-repeat> + return Err(()) + } + }, + RepeatType::Auto => { + if is_auto || atleast_one_not_fixed { + // We've either seen <auto-repeat> earlier, or there's at least one non-fixed value + return Err(()) + } + + is_auto = true; + list_type = TrackListType::Auto(values.len() as u16); + }, + RepeatType::Fixed => (), + } + + names.push(current_names); + values.push(Either::Second(repeat)); + } else { + if values.is_empty() { + return Err(()) + } + + names.push(current_names); + break + } + } + + Ok(TrackList { + list_type: list_type, + values: values, + line_names: names, + auto_repeat: None, // filled only in computation + }) + } +} + +impl<T: ToCss> ToCss for TrackList<T> { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + let auto_idx = match self.list_type { + TrackListType::Auto(i) => i as usize, + _ => usize::MAX, + }; + + let mut values_iter = self.values.iter().peekable(); + let mut line_names_iter = self.line_names.iter().peekable(); + + for idx in 0.. { + let names = line_names_iter.next().unwrap(); // This should exist! + concat_serialize_idents("[", "]", names, " ", dest)?; + + match self.auto_repeat { + Some(ref repeat) if idx == auto_idx => { + if !names.is_empty() { + dest.write_str(" ")?; + } + + repeat.to_css(dest)?; + }, + _ => match values_iter.next() { + Some(value) => { + if !names.is_empty() { + dest.write_str(" ")?; + } + + value.to_css(dest)?; + }, + None => break, + }, + } + + if values_iter.peek().is_some() || line_names_iter.peek().map_or(false, |v| !v.is_empty()) { + dest.write_str(" ")?; + } + } + + Ok(()) + } +} + +impl HasViewportPercentage for TrackList<TrackSizeOrRepeat> { + #[inline] + fn has_viewport_percentage(&self) -> bool { + self.values.iter().any(|ref v| v.has_viewport_percentage()) + } +} + +impl ToComputedValue for TrackList<TrackSizeOrRepeat> { + type ComputedValue = TrackList<TrackSize<computed::LengthOrPercentage>>; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + // Merge the line names while computing values. The resulting values will + // all be a bunch of `<track-size>`. + // + // For example, + // `[a b] 100px [c d] repeat(1, 30px [g]) [h]` will be merged as `[a b] 100px [c d] 30px [g h]` + // whereas, `[a b] repeat(2, [c] 50px [d]) [e f] repeat(auto-fill, [g] 12px) 10px [h]` will be merged as + // `[a b c] 50px [d c] 50px [d e f] repeat(auto-fill, [g] 12px) 10px [h]`, with the `<auto-repeat>` value + // set in the `auto_repeat` field, and the `idx` in TrackListType::Auto pointing to the values after + // `<auto-repeat>` (in this case, `10px [h]`). + let mut line_names = vec![]; + let mut list_type = self.list_type; + let mut values = vec![]; + let mut prev_names = vec![]; + let mut auto_repeat = None; + + let mut names_iter = self.line_names.iter(); + for (size_or_repeat, names) in self.values.iter().zip(&mut names_iter) { + prev_names.extend_from_slice(names); + + match *size_or_repeat { + Either::First(ref size) => values.push(size.to_computed_value(context)), + Either::Second(ref repeat) => { + let mut computed = repeat.to_computed_value(context); + if computed.count == RepeatCount::AutoFit || computed.count == RepeatCount::AutoFill { + line_names.push(mem::replace(&mut prev_names, vec![])); // don't merge for auto + list_type = TrackListType::Auto(values.len() as u16); + auto_repeat = Some(computed); + continue + } + + let mut repeat_names_iter = computed.line_names.drain(..); + for (size, mut names) in computed.track_sizes.drain(..).zip(&mut repeat_names_iter) { + prev_names.append(&mut names); + line_names.push(mem::replace(&mut prev_names, vec![])); + values.push(size); + } + + if let Some(mut names) = repeat_names_iter.next() { + prev_names.append(&mut names); + } + + continue // last `<line-names>` in repeat() may merge with the next set + } + } + + line_names.push(mem::replace(&mut prev_names, vec![])); + } + + if let Some(names) = names_iter.next() { + prev_names.extend_from_slice(names); + } + + line_names.push(mem::replace(&mut prev_names, vec![])); + + TrackList { + list_type: list_type, + values: values, + line_names: line_names, + auto_repeat: auto_repeat, + } + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + let auto_idx = if let TrackListType::Auto(idx) = computed.list_type { + idx as usize + } else { + usize::MAX + }; + + let mut values = Vec::with_capacity(computed.values.len() + 1); + for (i, value) in computed.values.iter().map(ToComputedValue::from_computed_value).enumerate() { + if i == auto_idx { + let value = TrackRepeat::from_computed_value(computed.auto_repeat.as_ref().unwrap()); + values.push(Either::Second(value)); + } + + values.push(Either::First(value)); + } + + TrackList { + list_type: computed.list_type, + values: values, + line_names: computed.line_names.clone(), + auto_repeat: None, + } + } +} diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs index 7a6f6259732..d117593738c 100644 --- a/components/style/values/specified/length.rs +++ b/components/style/values/specified/length.rs @@ -539,7 +539,7 @@ pub enum Length { /// A calc expression. /// /// https://drafts.csswg.org/css-values/#calc-notation - Calc(AllowedLengthType, Box<CalcLengthOrPercentage>), + Calc(Box<CalcLengthOrPercentage>), } impl From<NoCalcLength> for Length { @@ -553,7 +553,7 @@ impl HasViewportPercentage for Length { fn has_viewport_percentage(&self) -> bool { match *self { Length::NoCalc(ref inner) => inner.has_viewport_percentage(), - Length::Calc(_, ref calc) => calc.has_viewport_percentage(), + Length::Calc(ref calc) => calc.has_viewport_percentage(), } } } @@ -562,7 +562,7 @@ impl ToCss for Length { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match *self { Length::NoCalc(ref inner) => inner.to_css(dest), - Length::Calc(_, ref calc) => calc.to_css(dest), + Length::Calc(ref calc) => calc.to_css(dest), } } } @@ -637,10 +637,7 @@ impl Length { }, Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => input.parse_nested_block(|input| { - CalcNode::parse_length(context, input) - .map(|calc| { - Length::Calc(num_context, Box::new(calc)) - }) + CalcNode::parse_length(context, input, num_context).map(|calc| Length::Calc(Box::new(calc))) }), _ => Err(()) } @@ -770,7 +767,7 @@ impl From<Length> for LengthOrPercentage { fn from(len: Length) -> LengthOrPercentage { match len { Length::NoCalc(l) => LengthOrPercentage::Length(l), - Length::Calc(_, l) => LengthOrPercentage::Calc(l), + Length::Calc(l) => LengthOrPercentage::Calc(l), } } } @@ -832,7 +829,7 @@ impl LengthOrPercentage { Ok(LengthOrPercentage::Length(NoCalcLength::from_px(value.value))), Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { let calc = try!(input.parse_nested_block(|i| { - CalcNode::parse_length_or_percentage(context, i) + CalcNode::parse_length_or_percentage(context, i, num_context) })); Ok(LengthOrPercentage::Calc(Box::new(calc))) }, @@ -986,7 +983,7 @@ impl LengthOrPercentageOrAuto { Ok(LengthOrPercentageOrAuto::Auto), Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { let calc = try!(input.parse_nested_block(|i| { - CalcNode::parse_length_or_percentage(context, i) + CalcNode::parse_length_or_percentage(context, i, num_context) })); Ok(LengthOrPercentageOrAuto::Calc(Box::new(calc))) }, @@ -1092,7 +1089,7 @@ impl LengthOrPercentageOrNone { } Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { let calc = try!(input.parse_nested_block(|i| { - CalcNode::parse_length_or_percentage(context, i) + CalcNode::parse_length_or_percentage(context, i, num_context) })); Ok(LengthOrPercentageOrNone::Calc(Box::new(calc))) }, @@ -1169,7 +1166,7 @@ impl LengthOrPercentageOrAutoOrContent { Ok(LengthOrPercentageOrAutoOrContent::Content), Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { let calc = try!(input.parse_nested_block(|i| { - CalcNode::parse_length_or_percentage(context, i) + CalcNode::parse_length_or_percentage(context, i, num_context) })); Ok(LengthOrPercentageOrAutoOrContent::Calc(Box::new(calc))) }, diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 04df41c542a..3fb46c473b9 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -10,12 +10,15 @@ use app_units::Au; use context::QuirksMode; use cssparser::{self, Parser, Token}; use euclid::size::Size2D; +use itoa; use parser::{ParserContext, Parse}; use self::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize}; +use self::grid::{TrackList as GenericTrackList, TrackSizeOrRepeat}; use self::url::SpecifiedUrl; use std::ascii::AsciiExt; use std::f32; use std::fmt; +use std::io::Write; use style_traits::ToCss; use style_traits::values::specified::AllowedNumericType; use super::{Auto, CSSFloat, CSSInteger, HasViewportPercentage, Either, None_}; @@ -85,15 +88,89 @@ pub struct CSSColor { impl Parse for CSSColor { fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { + Self::parse_quirky(context, input, AllowQuirks::No) + } +} + +impl CSSColor { + /// Parse a color, with quirks. + /// + /// https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk + pub fn parse_quirky(context: &ParserContext, + input: &mut Parser, + allow_quirks: AllowQuirks) + -> Result<Self, ()> { let start_position = input.position(); let authored = match input.next() { Ok(Token::Ident(s)) => Some(s.into_owned().into_boxed_str()), _ => None, }; input.reset(start_position); + if let Ok(parsed) = input.try(|i| Parse::parse(context, i)) { + return Ok(CSSColor { + parsed: parsed, + authored: authored, + }); + } + if !allow_quirks.allowed(context.quirks_mode) { + return Err(()); + } + let (number, dimension) = match input.next()? { + Token::Number(number) => { + (number, None) + }, + Token::Dimension(number, dimension) => { + (number, Some(dimension)) + }, + Token::Ident(ident) => { + if ident.len() != 3 && ident.len() != 6 { + return Err(()); + } + return cssparser::Color::parse_hash(ident.as_bytes()).map(|color| { + Self { + parsed: color.into(), + authored: None + } + }); + } + _ => { + return Err(()); + }, + }; + let value = number.int_value.ok_or(())?; + if value < 0 { + return Err(()); + } + let length = if value <= 9 { + 1 + } else if value <= 99 { + 2 + } else if value <= 999 { + 3 + } else if value <= 9999 { + 4 + } else if value <= 99999 { + 5 + } else if value <= 999999 { + 6 + } else { + return Err(()) + }; + let total = length + dimension.as_ref().map_or(0, |d| d.len()); + if total > 6 { + return Err(()); + } + let mut serialization = [b'0'; 6]; + let space_padding = 6 - total; + let mut written = space_padding; + written += itoa::write(&mut serialization[written..], value).unwrap(); + if let Some(dimension) = dimension { + written += (&mut serialization[written..]).write(dimension.as_bytes()).unwrap(); + } + debug_assert!(written == 6); Ok(CSSColor { - parsed: try!(Parse::parse(context, input)), - authored: authored, + parsed: cssparser::Color::parse_hash(&serialization).map(From::from)?, + authored: None, }) } } @@ -866,6 +943,13 @@ pub type TrackBreadth = GenericTrackBreadth<LengthOrPercentage>; /// The specified value of a grid `<track-size>` pub type TrackSize = GenericTrackSize<LengthOrPercentage>; +/// The specified value of a grid `<track-list>` +/// (could also be `<auto-track-list>` or `<explicit-track-list>`) +pub type TrackList = GenericTrackList<TrackSizeOrRepeat>; + +/// `<track-list> | none` +pub type TrackListOrNone = Either<TrackList, None_>; + #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[allow(missing_docs)] diff --git a/components/style/values/specified/position.rs b/components/style/values/specified/position.rs index 678d2a8777d..c2991ede864 100644 --- a/components/style/values/specified/position.rs +++ b/components/style/values/specified/position.rs @@ -234,19 +234,14 @@ impl<S: Side> ToComputedValue for PositionComponent<S> { PositionComponent::Side(ref keyword, Some(ref length)) if !keyword.is_start() => { match length.to_computed_value(context) { ComputedLengthOrPercentage::Length(length) => { - ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage { - length: -length, - percentage: Some(1.0), - }) + ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage::new(-length, Some(1.0))) }, ComputedLengthOrPercentage::Percentage(p) => { ComputedLengthOrPercentage::Percentage(1.0 - p) }, ComputedLengthOrPercentage::Calc(calc) => { - ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage { - length: -calc.length, - percentage: Some(1.0 - calc.percentage.unwrap_or(0.)), - }) + let p = 1. - calc.percentage.unwrap_or(0.); + ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage::new(-calc.unclamped_length(), Some(p))) }, } }, diff --git a/components/style/viewport.rs b/components/style/viewport.rs index c887614ad57..0953d9a4b7a 100644 --- a/components/style/viewport.rs +++ b/components/style/viewport.rs @@ -718,8 +718,7 @@ impl MaybeNew for ViewportConstraints { Some(initial_viewport.$dimension.scale_by(value.0)), LengthOrPercentageOrAuto::Auto => None, LengthOrPercentageOrAuto::Calc(ref calc) => { - let calc = calc.to_computed_value(&context); - Some(initial_viewport.$dimension.scale_by(calc.percentage()) + calc.length()) + calc.to_computed_value(&context).to_used_value(Some(initial_viewport.$dimension)) } }, ViewportLength::ExtendToZoom => { diff --git a/components/style_traits/values.rs b/components/style_traits/values.rs index 663a39fb6ac..aaff1a998ea 100644 --- a/components/style_traits/values.rs +++ b/components/style_traits/values.rs @@ -74,6 +74,7 @@ macro_rules! impl_to_css_for_predefined_type { impl_to_css_for_predefined_type!(f32); impl_to_css_for_predefined_type!(i32); +impl_to_css_for_predefined_type!(u16); impl_to_css_for_predefined_type!(u32); impl_to_css_for_predefined_type!(::cssparser::Token<'a>); impl_to_css_for_predefined_type!(::cssparser::RGBA); @@ -189,6 +190,13 @@ pub mod specified { NonNegative } + impl Default for AllowedLengthType { + #[inline] + fn default() -> Self { + AllowedLengthType::All + } + } + impl AllowedLengthType { /// Whether value is valid for this allowed length type. #[inline] diff --git a/components/webdriver_server/lib.rs b/components/webdriver_server/lib.rs index 250ad21813b..404f965bbb9 100644 --- a/components/webdriver_server/lib.rs +++ b/components/webdriver_server/lib.rs @@ -32,7 +32,7 @@ use hyper::method::Method::{self, Post}; use image::{DynamicImage, ImageFormat, RgbImage}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use keys::keycodes_to_keys; -use msg::constellation_msg::{FrameId, PipelineId, TraversalDirection}; +use msg::constellation_msg::{BrowsingContextId, PipelineId, TraversalDirection}; use net_traits::image::base::PixelFormat; use regex::Captures; use rustc_serialize::json::{Json, ToJson}; @@ -102,7 +102,7 @@ pub fn start_server(port: u16, constellation_chan: Sender<ConstellationMsg>) { /// Represents the current WebDriver session and holds relevant session state. struct WebDriverSession { id: Uuid, - frame_id: Option<FrameId>, + browsing_context_id: Option<BrowsingContextId>, /// Time to wait for injected scripts to run before interrupting them. A [`None`] value /// specifies that the script should run indefinitely. @@ -120,7 +120,7 @@ impl WebDriverSession { pub fn new() -> WebDriverSession { WebDriverSession { id: Uuid::new_v4(), - frame_id: None, + browsing_context_id: None, script_timeout: Some(30_000), load_timeout: Some(300_000), @@ -264,7 +264,7 @@ impl Handler { } } - fn pipeline_id(&self, frame_id: Option<FrameId>) -> WebDriverResult<PipelineId> { + fn pipeline_id(&self, frame_id: Option<BrowsingContextId>) -> WebDriverResult<PipelineId> { let interval = 20; let iterations = 30_000 / interval; let (sender, receiver) = ipc::channel().unwrap(); @@ -288,7 +288,7 @@ impl Handler { } fn frame_pipeline(&self) -> WebDriverResult<PipelineId> { - self.pipeline_id(self.session.as_ref().and_then(|session| session.frame_id)) + self.pipeline_id(self.session.as_ref().and_then(|session| session.browsing_context_id)) } fn session(&self) -> WebDriverResult<&WebDriverSession> { @@ -299,10 +299,10 @@ impl Handler { } } - fn set_frame_id(&mut self, frame_id: Option<FrameId>) -> WebDriverResult<()> { + fn set_browsing_context_id(&mut self, browsing_context_id: Option<BrowsingContextId>) -> WebDriverResult<()> { match self.session { Some(ref mut x) => { - x.frame_id = frame_id; + x.browsing_context_id = browsing_context_id; Ok(()) }, None => Err(WebDriverError::new(ErrorStatus::SessionNotCreated, @@ -525,7 +525,7 @@ impl Handler { use webdriver::common::FrameId; let frame_id = match parameters.id { FrameId::Null => { - self.set_frame_id(None).unwrap(); + self.set_browsing_context_id(None).unwrap(); return Ok(WebDriverResponse::Void) }, FrameId::Short(ref x) => WebDriverFrameId::Short(*x), @@ -547,16 +547,16 @@ impl Handler { } let pipeline_id = try!(self.frame_pipeline()); let (sender, receiver) = ipc::channel().unwrap(); - let cmd = WebDriverScriptCommand::GetFrameId(frame_id, sender); + let cmd = WebDriverScriptCommand::GetPipelineId(frame_id, sender); { self.constellation_chan.send(ConstellationMsg::WebDriverCommand( WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd))).unwrap(); } - let frame = match receiver.recv().unwrap() { + let context_id = match receiver.recv().unwrap() { Ok(Some(pipeline_id)) => { let (sender, receiver) = ipc::channel().unwrap(); - self.constellation_chan.send(ConstellationMsg::GetFrame(pipeline_id, sender)).unwrap(); + self.constellation_chan.send(ConstellationMsg::GetBrowsingContext(pipeline_id, sender)).unwrap(); receiver.recv().unwrap() }, Ok(None) => None, @@ -566,7 +566,7 @@ impl Handler { } }; - self.set_frame_id(frame).unwrap(); + self.set_browsing_context_id(context_id).unwrap(); Ok(WebDriverResponse::Void) } diff --git a/ports/cef/lib.rs b/ports/cef/lib.rs index 809fccc412b..f953358e4a9 100644 --- a/ports/cef/lib.rs +++ b/ports/cef/lib.rs @@ -4,6 +4,7 @@ #![allow(non_camel_case_types)] #![feature(box_syntax)] +#![feature(core_intrinsics)] #![feature(link_args)] #[macro_use] diff --git a/ports/cef/stubs.rs b/ports/cef/stubs.rs index df35e050852..8f4a9b73820 100644 --- a/ports/cef/stubs.rs +++ b/ports/cef/stubs.rs @@ -12,7 +12,9 @@ macro_rules! stub( #[allow(non_snake_case)] pub extern "C" fn $name() { println!("CEF stub function called: {}", stringify!($name)); - ::std::process::abort() + unsafe { + ::std::intrinsics::abort() + } } ) ); diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index f7aca89a524..d04625727aa 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -210,7 +210,7 @@ fn traverse_subtree(element: GeckoElement, debug!("Traversing subtree:"); debug!("{:?}", ShowSubtreeData(element.as_node())); - let traversal_driver = if global_style_data.style_thread_pool.is_none() { + let traversal_driver = if global_style_data.style_thread_pool.is_none() || !element.is_root() { TraversalDriver::Sequential } else { TraversalDriver::Parallel @@ -567,7 +567,8 @@ pub extern "C" fn Servo_StyleSheet_FromUTF8Bytes(loader: *mut Loader, mode: SheetParsingMode, media_list: *const RawServoMediaList, extra_data: *mut URLExtraData, - line_number_offset: u32) + line_number_offset: u32, + quirks_mode: nsCompatibility) -> RawServoStyleSheetStrong { let global_style_data = &*GLOBAL_STYLE_DATA; let input = unsafe { data.as_ref().unwrap().as_str_unchecked() }; @@ -602,7 +603,7 @@ pub extern "C" fn Servo_StyleSheet_FromUTF8Bytes(loader: *mut Loader, Arc::new(Stylesheet::from_str( input, url_data.clone(), origin, media, shared_lock, loader, &RustLogReporter, - QuirksMode::NoQuirks, line_number_offset as u64) + quirks_mode.into(), line_number_offset as u64) ).into_strong() } @@ -1068,7 +1069,6 @@ fn get_pseudo_style(guard: &SharedRwLockReadGuard, d.stylist.lazily_compute_pseudo_element_style(&guards, &element, &pseudo, - ElementState::empty(), base, &metrics) .map(|s| s.values().clone()) @@ -1123,6 +1123,12 @@ pub extern "C" fn Servo_StyleSet_RebuildData(raw_data: RawServoStyleSetBorrowed) } #[no_mangle] +pub extern "C" fn Servo_StyleSet_Clear(raw_data: RawServoStyleSetBorrowed) { + let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); + data.clear_stylist(); +} + +#[no_mangle] pub extern "C" fn Servo_StyleSet_Drop(data: RawServoStyleSetOwned) { let _ = data.into_box::<PerDocumentStyleData>(); } @@ -1130,9 +1136,9 @@ pub extern "C" fn Servo_StyleSet_Drop(data: RawServoStyleSetOwned) { fn parse_property(property_id: PropertyId, value: *const nsACString, data: *mut URLExtraData, - parsing_mode: structs::ParsingMode) -> Result<ParsedDeclaration, ()> { + parsing_mode: structs::ParsingMode, + quirks_mode: QuirksMode) -> Result<ParsedDeclaration, ()> { use style::parser::ParsingMode; - let value = unsafe { value.as_ref().unwrap().as_str_unchecked() }; let url_data = unsafe { RefPtr::from_ptr_ref(&data) }; let parsing_mode = ParsingMode::from_bits_truncate(parsing_mode); @@ -1142,17 +1148,18 @@ fn parse_property(property_id: PropertyId, url_data, &RustLogReporter, parsing_mode, - QuirksMode::NoQuirks) + quirks_mode) } #[no_mangle] pub extern "C" fn Servo_ParseProperty(property: nsCSSPropertyID, value: *const nsACString, data: *mut URLExtraData, - parsing_mode: structs::ParsingMode) + parsing_mode: structs::ParsingMode, + quirks_mode: nsCompatibility) -> RawServoDeclarationBlockStrong { let id = get_property_id_from_nscsspropertyid!(property, RawServoDeclarationBlockStrong::null()); - match parse_property(id, value, data, parsing_mode) { + match parse_property(id, value, data, parsing_mode, quirks_mode.into()) { Ok(parsed) => { let global_style_data = &*GLOBAL_STYLE_DATA; let mut block = PropertyDeclarationBlock::new(); @@ -1311,8 +1318,9 @@ pub extern "C" fn Servo_DeclarationBlock_GetPropertyIsImportant(declarations: Ra fn set_property(declarations: RawServoDeclarationBlockBorrowed, property_id: PropertyId, value: *const nsACString, is_important: bool, data: *mut URLExtraData, - parsing_mode: structs::ParsingMode) -> bool { - match parse_property(property_id, value, data, parsing_mode) { + parsing_mode: structs::ParsingMode, + quirks_mode: QuirksMode) -> bool { + match parse_property(property_id, value, data, parsing_mode, quirks_mode) { Ok(parsed) => { let importance = if is_important { Importance::Important } else { Importance::Normal }; write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| { @@ -1327,18 +1335,20 @@ fn set_property(declarations: RawServoDeclarationBlockBorrowed, property_id: Pro pub extern "C" fn Servo_DeclarationBlock_SetProperty(declarations: RawServoDeclarationBlockBorrowed, property: *const nsACString, value: *const nsACString, is_important: bool, data: *mut URLExtraData, - parsing_mode: structs::ParsingMode) -> bool { + parsing_mode: structs::ParsingMode, + quirks_mode: nsCompatibility) -> bool { set_property(declarations, get_property_id_from_property!(property, false), - value, is_important, data, parsing_mode) + value, is_important, data, parsing_mode, quirks_mode.into()) } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_SetPropertyById(declarations: RawServoDeclarationBlockBorrowed, property: nsCSSPropertyID, value: *const nsACString, is_important: bool, data: *mut URLExtraData, - parsing_mode: structs::ParsingMode) -> bool { + parsing_mode: structs::ParsingMode, + quirks_mode: nsCompatibility) -> bool { set_property(declarations, get_property_id_from_nscsspropertyid!(property, false), - value, is_important, data, parsing_mode) + value, is_important, data, parsing_mode, quirks_mode.into()) } fn remove_property(declarations: RawServoDeclarationBlockBorrowed, property_id: PropertyId) { @@ -1380,7 +1390,7 @@ pub extern "C" fn Servo_MediaList_Matches(list: RawServoMediaListBorrowed, -> bool { let per_doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow(); read_locked_arc(list, |list: &MediaList| { - list.evaluate(&per_doc_data.stylist.device, QuirksMode::NoQuirks) + list.evaluate(&per_doc_data.stylist.device, per_doc_data.stylist.quirks_mode()) }) } @@ -1835,10 +1845,15 @@ pub extern "C" fn Servo_DeclarationBlock_SetTextDecorationColorOverride(declarat } #[no_mangle] -pub extern "C" fn Servo_CSSSupports2(property: *const nsACString, value: *const nsACString) -> bool { +pub extern "C" fn Servo_CSSSupports2(property: *const nsACString, + value: *const nsACString) -> bool { let id = get_property_id_from_property!(property, false); - parse_property(id, value, unsafe { DUMMY_URL_DATA }, structs::ParsingMode_Default).is_ok() + parse_property(id, + value, + unsafe { DUMMY_URL_DATA }, + structs::ParsingMode_Default, + QuirksMode::NoQuirks).is_ok() } #[no_mangle] @@ -2144,7 +2159,7 @@ pub extern "C" fn Servo_AnimationValue_Compute(declarations: RawServoDeclaration font_metrics_provider: &metrics, cached_system_font: None, in_media_query: false, - quirks_mode: QuirksMode::NoQuirks, + quirks_mode: data.stylist.quirks_mode(), }; let global_style_data = &*GLOBAL_STYLE_DATA; diff --git a/ports/servo/main.rs b/ports/servo/main.rs index 6534aed75c0..bec8ffa8089 100644 --- a/ports/servo/main.rs +++ b/ports/servo/main.rs @@ -15,7 +15,7 @@ //! //! [glutin]: https://github.com/tomaka/glutin -#![feature(start)] +#![feature(start, core_intrinsics)] #[cfg(target_os = "android")] extern crate android_injected_glue; @@ -58,7 +58,7 @@ pub mod platform { fn install_crash_handler() { use backtrace::Backtrace; use sig::ffi::Sig; - use std::process::abort; + use std::intrinsics::abort; use std::thread; fn handler(_sig: i32) { @@ -67,7 +67,11 @@ fn install_crash_handler() { .map(|n| format!(" for thread \"{}\"", n)) .unwrap_or("".to_owned()); println!("Stack trace{}\n{:?}", name, Backtrace::new()); - abort(); + unsafe { + // N.B. Using process::abort() here causes the crash handler to be + // triggered recursively. + abort(); + } } signal!(Sig::SEGV, handler); // handle segfaults diff --git a/python/servo/build_commands.py b/python/servo/build_commands.py index 546fcfdf911..93ea76e63c7 100644 --- a/python/servo/build_commands.py +++ b/python/servo/build_commands.py @@ -407,9 +407,6 @@ class MachCommands(CommandBase): @Command('build-geckolib', description='Build a static library of components used by Gecko', category='build') - @CommandArgument('--with-gecko', - default=None, - help='Build with Gecko dist directory') @CommandArgument('--jobs', '-j', default=None, help='Number of jobs to run in parallel') @@ -419,7 +416,7 @@ class MachCommands(CommandBase): @CommandArgument('--release', '-r', action='store_true', help='Build in release mode') - def build_geckolib(self, with_gecko=None, jobs=None, verbose=False, release=False): + def build_geckolib(self, jobs=None, verbose=False, release=False): self.set_use_stable_rust() self.ensure_bootstrapped() self.ensure_clobbered() @@ -429,9 +426,6 @@ class MachCommands(CommandBase): ret = None opts = [] features = [] - if with_gecko is not None: - features += ["bindgen"] - env["MOZ_DIST"] = path.abspath(path.expanduser(with_gecko)) if jobs is not None: opts += ["-j", jobs] if verbose: diff --git a/python/tidy/servo_tidy/tidy.py b/python/tidy/servo_tidy/tidy.py index ccca233db9c..51e7e69f8d4 100644 --- a/python/tidy/servo_tidy/tidy.py +++ b/python/tidy/servo_tidy/tidy.py @@ -67,6 +67,7 @@ WEBIDL_STANDARDS = [ "//dom.spec.whatwg.org", "//domparsing.spec.whatwg.org", "//drafts.csswg.org", + "//drafts.css-houdini.org", "//drafts.fxtf.org", "//encoding.spec.whatwg.org", "//fetch.spec.whatwg.org", @@ -446,6 +447,7 @@ def check_rust(file_name, lines): prev_use = None prev_open_brace = False + multi_line_string = False current_indent = 0 prev_crate = {} prev_mod = {} @@ -463,6 +465,15 @@ def check_rust(file_name, lines): prev_indent = indent indent = len(original_line) - len(line) + # Hack for components/selectors/build.rs + if multi_line_string: + if line.startswith('"#'): + multi_line_string = False + else: + continue + if line.endswith('r#"'): + multi_line_string = True + is_attribute = re.search(r"#\[.*\]", line) is_comment = re.search(r"^//|^/\*|^\*", line) diff --git a/resources/presentational-hints.css b/resources/presentational-hints.css index 84157dcb4bd..63f42491dd3 100644 --- a/resources/presentational-hints.css +++ b/resources/presentational-hints.css @@ -18,11 +18,15 @@ br[clear=right i] { clear: right; } br[clear=all i], br[clear=both i] { clear: both; } -ol[type=1], li[type=1] { list-style-type: decimal; } -ol[type=a], li[type=a] { list-style-type: lower-alpha; } -ol[type=A], li[type=A] { list-style-type: upper-alpha; } -ol[type=i], li[type=i] { list-style-type: lower-roman; } -ol[type=I], li[type=I] { list-style-type: upper-roman; } +ol[type="1"], li[type="1"] { list-style-type: decimal; } +ol:-servo-case-sensitive-type-attr(a), +li:-servo-case-sensitive-type-attr(a) { list-style-type: lower-alpha; } +ol:-servo-case-sensitive-type-attr(A), +li:-servo-case-sensitive-type-attr(A) { list-style-type: upper-alpha; } +ol:-servo-case-sensitive-type-attr(i), +li:-servo-case-sensitive-type-attr(i) { list-style-type: lower-roman; } +ol:-servo-case-sensitive-type-attr(I), +li:-servo-case-sensitive-type-attr(I) { list-style-type: upper-roman; } ul[type=none i], li[type=none i] { list-style-type: none; } ul[type=disc i], li[type=disc i] { list-style-type: disc; } ul[type=circle i], li[type=circle i] { list-style-type: circle; } diff --git a/servo-tidy.toml b/servo-tidy.toml index 1b07e242ff9..55357dfe1ce 100644 --- a/servo-tidy.toml +++ b/servo-tidy.toml @@ -56,9 +56,6 @@ files = [ "./tests/wpt/mozilla/tests/css/fonts", "./tests/wpt/mozilla/tests/css/pre_with_tab.html", "./tests/wpt/mozilla/tests/mozilla/textarea_placeholder.html", - # Tidy complains about taking &String instead of &str, but they aren't - # equivalent given the way the traits are set up. - "./components/selectors/tree.rs", ] # Directories that are ignored for the non-WPT tidy check. directories = [ diff --git a/tests/unit/script/size_of.rs b/tests/unit/script/size_of.rs index a328bcbf6f5..3368271d8e1 100644 --- a/tests/unit/script/size_of.rs +++ b/tests/unit/script/size_of.rs @@ -30,13 +30,13 @@ macro_rules! sizeof_checker ( // Update the sizes here sizeof_checker!(size_event_target, EventTarget, 40); -sizeof_checker!(size_node, Node, 152); -sizeof_checker!(size_element, Element, 320); -sizeof_checker!(size_htmlelement, HTMLElement, 336); -sizeof_checker!(size_div, HTMLDivElement, 336); -sizeof_checker!(size_span, HTMLSpanElement, 336); -sizeof_checker!(size_text, Text, 184); -sizeof_checker!(size_characterdata, CharacterData, 184); +sizeof_checker!(size_node, Node, 184); +sizeof_checker!(size_element, Element, 352); +sizeof_checker!(size_htmlelement, HTMLElement, 368); +sizeof_checker!(size_div, HTMLDivElement, 368); +sizeof_checker!(size_span, HTMLSpanElement, 368); +sizeof_checker!(size_text, Text, 216); +sizeof_checker!(size_characterdata, CharacterData, 216); sizeof_checker!(size_servothreadsafelayoutnode, ServoThreadSafeLayoutNode, 16); // We use these types in the parallel traversal. They should stay pointer-sized. diff --git a/tests/unit/style/attr.rs b/tests/unit/style/attr.rs index 10f1bd5e585..25fe085a637 100644 --- a/tests/unit/style/attr.rs +++ b/tests/unit/style/attr.rs @@ -5,17 +5,18 @@ use app_units::Au; use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_length}; use style::values::computed::CalcLengthOrPercentage; +use style_traits::values::specified::AllowedLengthType; #[test] fn test_length_calc() { - let calc = CalcLengthOrPercentage { length: Au(10), percentage: Some(0.2) }; - assert_eq!(calc.to_computed(Some(Au(10))), Some(Au(12))); - assert_eq!(calc.to_computed(Some(Au(0))), Some(Au(10))); - assert_eq!(calc.to_computed(None), None); + let calc = CalcLengthOrPercentage::new(Au(10), Some(0.2)); + assert_eq!(calc.to_used_value(Some(Au(10))), Some(Au(12))); + assert_eq!(calc.to_used_value(Some(Au(0))), Some(Au(10))); + assert_eq!(calc.to_used_value(None), None); - let calc = CalcLengthOrPercentage { length: Au(10), percentage: None }; - assert_eq!(calc.to_computed(Some(Au(0))), Some(Au(10))); - assert_eq!(calc.to_computed(None), Some(Au(10))); + let calc = CalcLengthOrPercentage::new(Au(10), None); + assert_eq!(calc.to_used_value(Some(Au(0))), Some(Au(10))); + assert_eq!(calc.to_used_value(None), Some(Au(10))); } #[test] diff --git a/tests/unit/style/parsing/mod.rs b/tests/unit/style/parsing/mod.rs index e15d2dca21b..5f48483affa 100644 --- a/tests/unit/style/parsing/mod.rs +++ b/tests/unit/style/parsing/mod.rs @@ -5,10 +5,16 @@ //! Tests for parsing and serialization of values/properties use cssparser::Parser; +use euclid::size::TypedSize2D; use media_queries::CSSErrorReporterTest; use style::context::QuirksMode; +use style::font_metrics::ServoMetricsProvider; +use style::media_queries::{Device, MediaType}; use style::parser::{PARSING_MODE_DEFAULT, ParserContext}; +use style::properties::{ComputedValues, StyleBuilder}; use style::stylesheets::{CssRuleType, Origin}; +use style::values::computed::{Context, ToComputedValue}; +use style_traits::ToCss; fn parse<T, F: Fn(&ParserContext, &mut Parser) -> Result<T, ()>>(f: F, s: &str) -> Result<T, ()> { let url = ::servo_url::ServoUrl::parse("http://localhost").unwrap(); @@ -24,6 +30,32 @@ fn parse_entirely<T, F: Fn(&ParserContext, &mut Parser) -> Result<T, ()>>(f: F, parse(|context, parser| parser.parse_entirely(|p| f(context, p)), s) } +fn assert_computed_serialization<C, F, T>(f: F, input: &str, output: &str) + where F: Fn(&ParserContext, &mut Parser) -> Result<T, ()>, + T: ToComputedValue<ComputedValue=C>, C: ToCss +{ + let viewport_size = TypedSize2D::new(0., 0.); + let initial_style = ComputedValues::initial_values(); + let device = Device::new(MediaType::Screen, viewport_size); + + let context = Context { + is_root_element: true, + device: &device, + inherited_style: initial_style, + layout_parent_style: initial_style, + style: StyleBuilder::for_derived_style(&initial_style), + cached_system_font: None, + font_metrics_provider: &ServoMetricsProvider, + in_media_query: false, + quirks_mode: QuirksMode::NoQuirks, + }; + + let parsed = parse(f, input).unwrap(); + let computed = parsed.to_computed_value(&context); + let serialized = ToCss::to_css_string(&computed); + assert_eq!(serialized, output); +} + // This is a macro so that the file/line information // is preserved in the panic macro_rules! assert_roundtrip_with_context { diff --git a/tests/unit/style/parsing/position.rs b/tests/unit/style/parsing/position.rs index 7a3bfe1bae3..c1527c2f233 100644 --- a/tests/unit/style/parsing/position.rs +++ b/tests/unit/style/parsing/position.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use parsing::{parse, parse_entirely}; +use parsing::{assert_computed_serialization, parse, parse_entirely}; use style::parser::Parse; use style::values::specified::position::*; use style_traits::ToCss; @@ -168,3 +168,68 @@ fn test_grid_auto_flow() { assert!(parse(grid_auto_flow::parse, "column 'dense'").is_err()); assert!(parse(grid_auto_flow::parse, "column 2px dense").is_err()); } + +#[test] +fn test_grid_auto_rows_columns() { + use style::properties::longhands::grid_auto_rows; + + // the grammar is <track-size>+ but gecko supports only a single value, so we've clamped ourselves + assert_roundtrip_with_context!(grid_auto_rows::parse, "55%"); + assert_roundtrip_with_context!(grid_auto_rows::parse, "0.5fr"); + assert_roundtrip_with_context!(grid_auto_rows::parse, "fit-content(11%)"); + // only <inflexible-breadth> is allowed in first arg of minmax + assert!(parse(grid_auto_rows::parse, "minmax(1fr, max-content)").is_err()); +} + +#[test] +fn test_grid_template_rows_columns() { + use style::properties::longhands::grid_template_rows; + + assert_roundtrip_with_context!(grid_template_rows::parse, "none"); // none keyword + // <track-size>{2} with `<track-breadth> minmax(<inflexible-breadth>, <track-breadth>)` + assert_roundtrip_with_context!(grid_template_rows::parse, "1fr minmax(min-content, 1fr)"); + // <track-size> with <track-breadth> as <length-percentage> + assert_roundtrip_with_context!(grid_template_rows::parse, "calc(4em + 5px)"); + // <track-size> with <length> followed by <track-repeat> with `<track-size>{3}` (<flex>, auto, minmax) + assert_roundtrip_with_context!(grid_template_rows::parse, "10px repeat(2, 1fr auto minmax(200px, 1fr))"); + // <track-repeat> with `<track-size> <line-names>` followed by <track-size> + assert_roundtrip_with_context!(grid_template_rows::parse, "repeat(4, 10px [col-start] 250px [col-end]) 10px"); + // mixture of <track-size>, <track-repeat> and <line-names> + assert_roundtrip_with_context!(grid_template_rows::parse, + "[a] auto [b] minmax(min-content, 1fr) [b c d] repeat(2, [e] 40px) repeat(5, [f g] auto [h]) [i]"); + + // no span allowed in <line-names> + assert!(parse(grid_template_rows::parse, "[a span] 10px").is_err()); + // <track-list> needs at least one <track-size> | <track-repeat> + assert!(parse(grid_template_rows::parse, "[a b c]").is_err()); + // at least one argument of <fixed-size> should be a <fixed-breadth> (i.e., <length-percentage>) + assert!(parse(grid_template_rows::parse, "[a b] repeat(auto-fill, 50px) minmax(auto, 1fr)").is_err()); + // fit-content is not a <fixed-size> + assert!(parse(grid_template_rows::parse, "[a b] repeat(auto-fill, fit-content(20%))").is_err()); + // <auto-track-list> only allows <fixed-size> | <fixed-repeat> + assert!(parse(grid_template_rows::parse, "[a] repeat(2, auto) repeat(auto-fill, 10px)").is_err()); + // only <inflexible-breadth> allowed in <auto-track-repeat> + assert!(parse(grid_template_rows::parse, "[a] repeat(auto-fill, 1fr)").is_err()); + // <auto-track-repeat> is allowed only once + assert!(parse(grid_template_rows::parse, "[a] repeat(auto-fit, [b] 8px) [c] repeat(auto-fill, [c] 8px)").is_err()); +} + +#[test] +fn test_computed_grid_template_rows_colums() { + use style::properties::longhands::grid_template_rows; + + assert_computed_serialization(grid_template_rows::parse, + "[a] repeat(calc(1 + 1), [b] auto)", "[a b] auto [b] auto"); + + assert_computed_serialization(grid_template_rows::parse, + "[a] repeat(2, [b c] auto [e] auto [d])", + "[a b c] auto [e] auto [d b c] auto [e] auto [d]"); + + assert_computed_serialization(grid_template_rows::parse, + "[a] 50px [b] 10% [b c d] repeat(2, [e] 40px [f]) [g] repeat(auto-fill, [h i] 20px [j]) [k] 10px [l]", + "[a] 50px [b] 10% [b c d e] 40px [f e] 40px [f g] repeat(auto-fill, [h i] 20px [j]) [k] 10px [l]"); + + assert_computed_serialization(grid_template_rows::parse, + "10px repeat(2, 1fr auto minmax(200px, 1fr))", + "10px minmax(auto, 1fr) auto minmax(200px, 1fr) minmax(auto, 1fr) auto minmax(200px, 1fr)"); +} diff --git a/tests/unit/style/stylesheets.rs b/tests/unit/style/stylesheets.rs index 1cef0f377be..44c02f6c1f2 100644 --- a/tests/unit/style/stylesheets.rs +++ b/tests/unit/style/stylesheets.rs @@ -6,6 +6,7 @@ use cssparser::{self, Parser as CssParser, SourcePosition, SourceLocation}; use html5ever::{Namespace as NsAtom}; use media_queries::CSSErrorReporterTest; use parking_lot::RwLock; +use selectors::attr::*; use selectors::parser::*; use servo_atoms::Atom; use servo_url::ServoUrl; @@ -90,28 +91,24 @@ fn test_parse_stylesheet() { }))), CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule { selectors: SelectorList(vec![ - Selector { - inner: SelectorInner::from_vec(vec![ - Component::Namespace(Namespace { - prefix: None, - url: NsAtom::from("http://www.w3.org/1999/xhtml") - }), + Selector::new_for_unit_testing( + SelectorInner::from_vec(vec![ + Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")), Component::LocalName(LocalName { name: local_name!("input"), lower_name: local_name!("input"), }), - Component::AttrEqual(AttrSelector { - name: local_name!("type"), - lower_name: local_name!("type"), - namespace: NamespaceConstraint::Specific(Namespace { - prefix: None, - url: ns!() - }), - }, "hidden".to_owned(), CaseSensitivity::CaseInsensitive) + Component::AttributeInNoNamespace { + local_name: local_name!("type"), + local_name_lower: local_name!("type"), + operator: AttrSelectorOperator::Equal, + value: "hidden".to_owned(), + case_sensitivity: ParsedCaseSensitivity::AsciiCaseInsensitive, + never_matches: false, + } ]), - pseudo_element: None, - specificity: (0 << 20) + (1 << 10) + (1 << 0), - }, + (0 << 20) + (1 << 10) + (1 << 0) + ), ]), block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![ (PropertyDeclaration::Display(longhands::display::SpecifiedValue::none), @@ -127,34 +124,26 @@ fn test_parse_stylesheet() { }))), CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule { selectors: SelectorList(vec![ - Selector { - inner: SelectorInner::from_vec(vec![ - Component::Namespace(Namespace { - prefix: None, - url: NsAtom::from("http://www.w3.org/1999/xhtml") - }), + Selector::new_for_unit_testing( + SelectorInner::from_vec(vec![ + Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")), Component::LocalName(LocalName { name: local_name!("html"), lower_name: local_name!("html"), }), ]), - pseudo_element: None, - specificity: (0 << 20) + (0 << 10) + (1 << 0), - }, - Selector { - inner: SelectorInner::from_vec(vec![ - Component::Namespace(Namespace { - prefix: None, - url: NsAtom::from("http://www.w3.org/1999/xhtml") - }), + (0 << 20) + (0 << 10) + (1 << 0) + ), + Selector::new_for_unit_testing( + SelectorInner::from_vec(vec![ + Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")), Component::LocalName(LocalName { name: local_name!("body"), lower_name: local_name!("body"), }), ]), - pseudo_element: None, - specificity: (0 << 20) + (0 << 10) + (1 << 0), - }, + (0 << 20) + (0 << 10) + (1 << 0) + ), ]), block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![ (PropertyDeclaration::Display(longhands::display::SpecifiedValue::block), @@ -167,23 +156,16 @@ fn test_parse_stylesheet() { }))), CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule { selectors: SelectorList(vec![ - Selector { - inner: SelectorInner::from_vec(vec![ - Component::Namespace(Namespace { - prefix: None, - url: NsAtom::from("http://www.w3.org/1999/xhtml") - }), + Selector::new_for_unit_testing( + SelectorInner::from_vec(vec![ + Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")), Component::ID(Atom::from("d1")), Component::Combinator(Combinator::Child), - Component::Namespace(Namespace { - prefix: None, - url: NsAtom::from("http://www.w3.org/1999/xhtml") - }), + Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")), Component::Class(Atom::from("ok")), ]), - pseudo_element: None, - specificity: (1 << 20) + (1 << 10) + (0 << 0), - }, + (1 << 20) + (1 << 10) + (0 << 0) + ), ]), block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![ (PropertyDeclaration::BackgroundColor( diff --git a/tests/unit/style/stylist.rs b/tests/unit/style/stylist.rs index 073c003b0bb..3c136edcef3 100644 --- a/tests/unit/style/stylist.rs +++ b/tests/unit/style/stylist.rs @@ -212,9 +212,9 @@ fn test_insert() { #[test] fn test_get_universal_rules() { thread_state::initialize(thread_state::LAYOUT); - let (map, shared_lock) = get_mock_map(&["*|*", "#foo > *|*", ".klass", "#id"]); + let (map, shared_lock) = get_mock_map(&["*|*", "#foo > *|*", "*|* > *|*", ".klass", "#id"]); let decls = map.get_universal_rules(CascadeLevel::UserNormal); - assert_eq!(decls.len(), 1); + assert_eq!(decls.len(), 1, "{:?}", decls); } diff --git a/tests/unit/stylo/Cargo.toml b/tests/unit/stylo/Cargo.toml index 8c6478be76e..46d9c6df281 100644 --- a/tests/unit/stylo/Cargo.toml +++ b/tests/unit/stylo/Cargo.toml @@ -21,7 +21,6 @@ env_logger = "0.4" euclid = "0.11" libc = "0.2" log = {version = "0.3.5", features = ["release_max_level_info"]} -parking_lot = "0.3" selectors = {path = "../../../components/selectors", features = ["gecko_like_types"]} style_traits = {path = "../../../components/style_traits"} geckoservo = {path = "../../../ports/geckolib"} diff --git a/tests/unit/stylo/lib.rs b/tests/unit/stylo/lib.rs index 76b54a45b7d..fedbba703b9 100644 --- a/tests/unit/stylo/lib.rs +++ b/tests/unit/stylo/lib.rs @@ -7,7 +7,6 @@ extern crate cssparser; extern crate env_logger; extern crate geckoservo; #[macro_use] extern crate log; -extern crate parking_lot; extern crate selectors; #[macro_use] extern crate style; extern crate style_traits; diff --git a/tests/unit/stylo/size_of.rs b/tests/unit/stylo/size_of.rs index 4ae541c8de0..f8770a8842e 100644 --- a/tests/unit/stylo/size_of.rs +++ b/tests/unit/stylo/size_of.rs @@ -12,8 +12,8 @@ fn size_of_selectors_dummy_types() { assert_eq!(size_of::<dummies::PseudoClass>(), size_of::<real::NonTSPseudoClass>()); assert_eq!(align_of::<dummies::PseudoClass>(), align_of::<real::NonTSPseudoClass>()); - assert_eq!(size_of::<dummies::PseudoElementSelector>(), size_of::<real::PseudoElementSelector>()); - assert_eq!(align_of::<dummies::PseudoElementSelector>(), align_of::<real::PseudoElementSelector>()); + assert_eq!(size_of::<dummies::PseudoElement>(), size_of::<real::PseudoElement>()); + assert_eq!(align_of::<dummies::PseudoElement>(), align_of::<real::PseudoElement>()); assert_eq!(size_of::<dummies::Atom>(), size_of::<style::Atom>()); assert_eq!(align_of::<dummies::Atom>(), align_of::<style::Atom>()); diff --git a/tests/wpt/metadata-css/css-transforms-1_dev/html/css-transform-3d-transform-style.htm.ini b/tests/wpt/metadata-css/css-transforms-1_dev/html/css-transform-3d-transform-style.htm.ini deleted file mode 100644 index 5057dc804a7..00000000000 --- a/tests/wpt/metadata-css/css-transforms-1_dev/html/css-transform-3d-transform-style.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[css-transform-3d-transform-style.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css-transforms-1_dev/html/transform3d-sorting-004.htm.ini b/tests/wpt/metadata-css/css-transforms-1_dev/html/transform3d-sorting-004.htm.ini new file mode 100644 index 00000000000..680f6a37a89 --- /dev/null +++ b/tests/wpt/metadata-css/css-transforms-1_dev/html/transform3d-sorting-004.htm.ini @@ -0,0 +1,4 @@ +[transform3d-sorting-004.htm] + type: reftest + expected: FAIL + bug: https://github.com/w3c/web-platform-tests/issues/5931 diff --git a/tests/wpt/metadata-css/css-transforms-1_dev/html/transform3d-sorting-005.htm.ini b/tests/wpt/metadata-css/css-transforms-1_dev/html/transform3d-sorting-005.htm.ini deleted file mode 100644 index d51b70bab1b..00000000000 --- a/tests/wpt/metadata-css/css-transforms-1_dev/html/transform3d-sorting-005.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[transform3d-sorting-005.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css-transforms-1_dev/html/transform3d-sorting-006.htm.ini b/tests/wpt/metadata-css/css-transforms-1_dev/html/transform3d-sorting-006.htm.ini index e275c5457a6..e3fa053475b 100644 --- a/tests/wpt/metadata-css/css-transforms-1_dev/html/transform3d-sorting-006.htm.ini +++ b/tests/wpt/metadata-css/css-transforms-1_dev/html/transform3d-sorting-006.htm.ini @@ -1,3 +1,4 @@ [transform3d-sorting-006.htm] type: reftest expected: FAIL + bug: https://github.com/w3c/web-platform-tests/pull/5922 diff --git a/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-024.htm.ini b/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-024.htm.ini deleted file mode 100644 index 8d25a735ed8..00000000000 --- a/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-024.htm.ini +++ /dev/null @@ -1,5 +0,0 @@ -[css3-selectors-lang-024.htm] - type: testharness - [A lang|= value will match a lang attribute value regardless of case differences.] - expected: FAIL - diff --git a/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-035.htm.ini b/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-035.htm.ini deleted file mode 100644 index f1eb302573d..00000000000 --- a/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-035.htm.ini +++ /dev/null @@ -1,5 +0,0 @@ -[css3-selectors-lang-035.htm] - type: testharness - [A lang|= value will match a lang attribute value regardless of case differences in the script tag.] - expected: FAIL - diff --git a/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-044.htm.ini b/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-044.htm.ini deleted file mode 100644 index 0a343c73563..00000000000 --- a/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-044.htm.ini +++ /dev/null @@ -1,5 +0,0 @@ -[css3-selectors-lang-044.htm] - type: testharness - [A lang= value will match a lang attribute value regardless of case differences.] - expected: FAIL - diff --git a/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-055.htm.ini b/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-055.htm.ini deleted file mode 100644 index f8ff6b5f1f9..00000000000 --- a/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-055.htm.ini +++ /dev/null @@ -1,5 +0,0 @@ -[css3-selectors-lang-055.htm] - type: testharness - [A lang= value will match a lang attribute value regardless of case differences in the script tag.] - expected: FAIL - diff --git a/tests/wpt/metadata/dom/nodes/MutationObserver-attributes.html.ini b/tests/wpt/metadata/dom/nodes/MutationObserver-attributes.html.ini index 66e97c704e6..52a6a920412 100644 --- a/tests/wpt/metadata/dom/nodes/MutationObserver-attributes.html.ini +++ b/tests/wpt/metadata/dom/nodes/MutationObserver-attributes.html.ini @@ -1,114 +1,27 @@ [MutationObserver-attributes.html] type: testharness - [attributes Element.id: update, no oldValue, mutation] - expected: FAIL - - [attributes Element.id: update mutation] - expected: FAIL - - [attributes Element.id: empty string update mutation] - expected: FAIL - [attributes Element.id: same value mutation] expected: FAIL - [attributes Element.unknown: IDL attribute no mutation] - expected: FAIL - - [attributes HTMLInputElement.type: type update mutation] - expected: FAIL - [attributes Element.className: new value mutation] expected: FAIL - [attributes Element.className: empty string update mutation] - expected: FAIL - - [attributes Element.className: same value mutation] - expected: FAIL - - [attributes Element.className: same multiple values mutation] - expected: FAIL - [attributes Element.classList.add: single token addition mutation] expected: FAIL [attributes Element.classList.add: multiple tokens addition mutation] expected: FAIL - [attributes Element.classList.add: syntax err/no mutation] - expected: FAIL - - [attributes Element.classList.add: invalid character/no mutation] - expected: FAIL - - [attributes Element.classList.add: same value mutation] - expected: FAIL - - [attributes Element.classList.remove: single token removal mutation] - expected: FAIL - - [attributes Element.classList.remove: multiple tokens removal mutation] - expected: FAIL - - [attributes Element.classList.remove: missing token removal mutation] - expected: FAIL - - [attributes Element.classList.toggle: token removal mutation] - expected: FAIL - - [attributes Element.classList.toggle: token addition mutation] - expected: FAIL - - [attributes Element.classList.toggle: forced token removal mutation] - expected: FAIL - - [attributes Element.classList.toggle: forced missing token removal no mutation] - expected: FAIL - - [attributes Element.classList.toggle: forced existing token addition no mutation] - expected: FAIL - - [attributes Element.classList.toggle: forced token addition mutation] - expected: FAIL - - [attributes Element.attributes.value: update mutation] - expected: FAIL - - [attributes Element.attributes.value: same id mutation] - expected: FAIL - - [attributes Element.setAttribute: id mutation] - expected: FAIL - - [attributes Element.setAttribute: same class mutation] - expected: FAIL - [attributes Element.setAttribute: classname mutation] expected: FAIL - [attributes Element.removeAttribute: removal mutation] - expected: FAIL - - [attributes Element.removeAttribute: removal no mutation] - expected: FAIL - - [childList HTMLInputElement.removeAttribute: type removal mutation] - expected: FAIL - [attributes Element.setAttributeNS: creation mutation] expected: FAIL [attributes Element.setAttributeNS: prefixed attribute creation mutation] expected: FAIL - [attributes Element.removeAttributeNS: removal mutation] - expected: FAIL - - [attributes Element.removeAttributeNS: removal no mutation] - expected: FAIL - - [attributes Element.removeAttributeNS: prefixed attribute removal no mutation] + [attributes Element.className: empty string update mutation] expected: FAIL [attributes/attributeFilter Element.id/Element.className: update mutation] @@ -117,12 +30,6 @@ [attributes/attributeFilter Element.id/Element.className: multiple filter update mutation] expected: FAIL - [attributeOldValue alone Element.id: update mutation] - expected: FAIL - [attributeFilter alone Element.id/Element.className: multiple filter update mutation] expected: FAIL - [childList false: no childList mutation] - expected: FAIL - diff --git a/tests/wpt/metadata/dom/nodes/MutationObserver-characterData.html.ini b/tests/wpt/metadata/dom/nodes/MutationObserver-characterData.html.ini index 37abce874dc..6e8512d76d9 100644 --- a/tests/wpt/metadata/dom/nodes/MutationObserver-characterData.html.ini +++ b/tests/wpt/metadata/dom/nodes/MutationObserver-characterData.html.ini @@ -1,59 +1,60 @@ [MutationObserver-characterData.html] type: testharness + expected: TIMEOUT [characterData Text.data: simple mutation without oldValue] - expected: FAIL + expected: TIMEOUT [characterData Text.data: simple mutation] - expected: FAIL + expected: TIMEOUT [characterData Text.appendData: simple mutation] - expected: FAIL + expected: TIMEOUT [characterData Text.appendData: empty string mutation] - expected: FAIL + expected: TIMEOUT [characterData Text.appendData: null string mutation] - expected: FAIL + expected: TIMEOUT [characterData Text.insertData: simple mutation] - expected: FAIL + expected: TIMEOUT [characterData Text.insertData: empty string mutation] - expected: FAIL + expected: TIMEOUT [characterData Text.insertData: null string mutation] - expected: FAIL + expected: TIMEOUT [characterData Text.deleteData: simple mutation] - expected: FAIL + expected: TIMEOUT [characterData Text.deleteData: empty mutation] - expected: FAIL + expected: TIMEOUT [characterData Text.replaceData: simple mutation] - expected: FAIL + expected: TIMEOUT [characterData Text.replaceData: empty mutation] - expected: FAIL + expected: TIMEOUT [characterData ProcessingInstruction: data mutations] - expected: FAIL + expected: TIMEOUT [characterData Comment: data mutations] - expected: FAIL + expected: TIMEOUT [characterData Range.deleteContents: child and data removal mutation] - expected: FAIL + expected: TIMEOUT [characterData Range.deleteContents: child and data removal mutation (2)] - expected: FAIL + expected: TIMEOUT [characterData Range.extractContents: child and data removal mutation] - expected: FAIL + expected: TIMEOUT [characterData Range.extractContents: child and data removal mutation (2)] - expected: FAIL + expected: TIMEOUT [characterData/characterDataOldValue alone Text.data: simple mutation] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/metadata/dom/nodes/MutationObserver-childList.html.ini b/tests/wpt/metadata/dom/nodes/MutationObserver-childList.html.ini index 7d5ce7fb070..91a99722ec5 100644 --- a/tests/wpt/metadata/dom/nodes/MutationObserver-childList.html.ini +++ b/tests/wpt/metadata/dom/nodes/MutationObserver-childList.html.ini @@ -1,95 +1,90 @@ [MutationObserver-childList.html] type: testharness - [childList Node.nodeValue: no mutation] - expected: FAIL - + expected: TIMEOUT [childList Node.textContent: replace content mutation] - expected: FAIL + expected: TIMEOUT [childList Node.textContent: no previous content mutation] - expected: FAIL - - [childList Node.textContent: textContent no mutation] - expected: FAIL + expected: TIMEOUT [childList Node.textContent: empty string mutation] - expected: FAIL + expected: TIMEOUT [childList Node.normalize mutation] - expected: FAIL + expected: TIMEOUT [childList Node.normalize mutations] - expected: FAIL + expected: TIMEOUT [childList Node.insertBefore: addition mutation] - expected: FAIL + expected: TIMEOUT [childList Node.insertBefore: removal mutation] - expected: FAIL + expected: TIMEOUT [childList Node.insertBefore: removal and addition mutations] - expected: FAIL + expected: TIMEOUT [childList Node.insertBefore: fragment addition mutations] - expected: FAIL + expected: TIMEOUT [childList Node.insertBefore: fragment removal mutations] - expected: FAIL + expected: TIMEOUT [childList Node.insertBefore: last child addition mutation] - expected: FAIL + expected: TIMEOUT [childList Node.appendChild: addition mutation] - expected: FAIL + expected: TIMEOUT [childList Node.appendChild: removal mutation] - expected: FAIL + expected: TIMEOUT [childList Node.appendChild: removal and addition mutations] - expected: FAIL + expected: TIMEOUT [childList Node.appendChild: fragment addition mutations] - expected: FAIL + expected: TIMEOUT [childList Node.appendChild: fragment removal mutations] - expected: FAIL + expected: TIMEOUT [childList Node.appendChild: addition outside document tree mutation] - expected: FAIL + expected: TIMEOUT [childList Node.replaceChild: replacement mutation] - expected: FAIL + expected: TIMEOUT [childList Node.replaceChild: removal mutation] - expected: FAIL + expected: TIMEOUT [childList Node.replaceChild: internal replacement mutation] - expected: FAIL + expected: TIMEOUT [childList Node.removeChild: removal mutation] - expected: FAIL + expected: TIMEOUT [childList Range.deleteContents: child removal mutation] - expected: FAIL + expected: TIMEOUT [childList Range.deleteContents: child and data removal mutation] - expected: FAIL + expected: TIMEOUT [childList Range.extractContents: child removal mutation] - expected: FAIL + expected: TIMEOUT [childList Range.extractContents: child and data removal mutation] - expected: FAIL + expected: TIMEOUT [childList Range.insertNode: child insertion mutation] - expected: FAIL + expected: TIMEOUT [childList Range.insertNode: children insertion mutation] - expected: FAIL + expected: TIMEOUT [childList Range.surroundContents: children removal and addition mutation] - expected: FAIL + expected: TIMEOUT [childList Node.replaceChild: self internal replacement mutation] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/metadata/dom/nodes/MutationObserver-inner-outer.html.ini b/tests/wpt/metadata/dom/nodes/MutationObserver-inner-outer.html.ini index cc46ef272e3..5c86ae99f38 100644 --- a/tests/wpt/metadata/dom/nodes/MutationObserver-inner-outer.html.ini +++ b/tests/wpt/metadata/dom/nodes/MutationObserver-inner-outer.html.ini @@ -1,11 +1,12 @@ [MutationObserver-inner-outer.html] type: testharness + expected: TIMEOUT [innerHTML mutation] expected: FAIL [innerHTML with 2 children mutation] - expected: FAIL + expected: TIMEOUT [outerHTML mutation] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/metadata/html/rendering/non-replaced-elements/lists/li-type-supported-xhtml.xhtml.ini b/tests/wpt/metadata/html/rendering/non-replaced-elements/lists/li-type-supported-xhtml.xhtml.ini deleted file mode 100644 index 3ac5a7343ca..00000000000 --- a/tests/wpt/metadata/html/rendering/non-replaced-elements/lists/li-type-supported-xhtml.xhtml.ini +++ /dev/null @@ -1,3 +0,0 @@ -[li-type-supported-xhtml.xhtml] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata/html/rendering/non-replaced-elements/lists/li-type-supported.html.ini b/tests/wpt/metadata/html/rendering/non-replaced-elements/lists/li-type-supported.html.ini deleted file mode 100644 index 1f631a1d1b8..00000000000 --- a/tests/wpt/metadata/html/rendering/non-replaced-elements/lists/li-type-supported.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[li-type-supported.html] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata/quirks-mode/supports.html.ini b/tests/wpt/metadata/quirks-mode/supports.html.ini index d3fb177270f..f1968d02eba 100644 --- a/tests/wpt/metadata/quirks-mode/supports.html.ini +++ b/tests/wpt/metadata/quirks-mode/supports.html.ini @@ -1,8 +1,5 @@ [supports.html] type: testharness - [@supports quirky color] - expected: FAIL - [Sanity check @supports length] expected: FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index f5cd627bb58..6e1e2c6fd99 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -3731,6 +3731,18 @@ {} ] ], + "css/min_width_percent_root_a.html": [ + [ + "/_mozilla/css/min_width_percent_root_a.html", + [ + [ + "/_mozilla/css/min_width_percent_root_b.html", + "==" + ] + ], + {} + ] + ], "css/min_width_simple_a.html": [ [ "/_mozilla/css/min_width_simple_a.html", @@ -5849,6 +5861,18 @@ {} ] ], + "css/transform_3d_from_outside_viewport.html": [ + [ + "/_mozilla/css/transform_3d_from_outside_viewport.html", + [ + [ + "/_mozilla/css/transform_3d_from_outside_viewport_ref.html", + "!=" + ] + ], + {} + ] + ], "css/transform_optimization.html": [ [ "/_mozilla/css/transform_optimization.html", @@ -8359,6 +8383,11 @@ {} ] ], + "css/min_width_percent_root_b.html": [ + [ + {} + ] + ], "css/min_width_simple_b.html": [ [ {} @@ -9259,6 +9288,11 @@ {} ] ], + "css/transform_3d_from_outside_viewport_ref.html": [ + [ + {} + ] + ], "css/transform_3d_ref.html": [ [ {} @@ -11168,6 +11202,21 @@ [ {} ] + ], + "mozilla/worklets/syntax_error.js": [ + [ + {} + ] + ], + "mozilla/worklets/test_worklet.js": [ + [ + {} + ] + ], + "mozilla/worklets/throw_exception.js": [ + [ + {} + ] ] }, "testharness": { @@ -20010,6 +20059,12 @@ "/_mozilla/mozilla/windowproxy.html", {} ] + ], + "mozilla/worklets/test_worklet.html": [ + [ + "/_mozilla/mozilla/worklets/test_worklet.html", + {} + ] ] } }, @@ -22622,6 +22677,14 @@ "e8ee634ab6350ad4c870cb1d0ce4f1a88b285581", "support" ], + "css/min_width_percent_root_a.html": [ + "e8c23bc78daed932d5b513b972582e21711ea06a", + "reftest" + ], + "css/min_width_percent_root_b.html": [ + "4c5b54f6b4e1bb77a15fdf7d99f9be3a47df7e8b", + "support" + ], "css/min_width_simple_a.html": [ "e709d1ab4096401ecb7b848d9297c00ef7786bf9", "reftest" @@ -24074,6 +24137,14 @@ "1450d169d4c5506fff240adca5c28b0cb6accd9f", "reftest" ], + "css/transform_3d_from_outside_viewport.html": [ + "b59ccc70dec5de8bf55440ef3d4dd35d2ec1493a", + "reftest" + ], + "css/transform_3d_from_outside_viewport_ref.html": [ + "87531bec4e8b60f17d885d6236ec0629f68f6c9a", + "support" + ], "css/transform_3d_ref.html": [ "e59866f74ae9dc10a2220c1bb240862371f3edc9", "support" @@ -25771,7 +25842,7 @@ "testharness" ], "mozilla/interfaces.html": [ - "21e18bafdbfe5f3aa0ee71766bdc3b6a7e334226", + "49dd9f6ef449813f2ce943d4c9fac351357e5c74", "testharness" ], "mozilla/interfaces.js": [ @@ -31689,6 +31760,22 @@ "mozilla/windowproxy.html": [ "128cd0aa5cf80f60078979039036d32b470b0616", "testharness" + ], + "mozilla/worklets/syntax_error.js": [ + "f3a9b8c78346507bc0b3190c8000ccf80cc133f6", + "support" + ], + "mozilla/worklets/test_worklet.html": [ + "fe9c93a5307c616f878b6623155e1b04c86dd994", + "testharness" + ], + "mozilla/worklets/test_worklet.js": [ + "9d5f8a07cd62a10f4f5ff93744672e5a6fdbc2b0", + "support" + ], + "mozilla/worklets/throw_exception.js": [ + "ebfdae19db68fed8e69142ef73842ac9921e4463", + "support" ] }, "url_base": "/_mozilla/", diff --git a/tests/wpt/mozilla/meta/mozilla/adopted_node_is_same_origin_domain.html.ini b/tests/wpt/mozilla/meta/mozilla/adopted_node_is_same_origin_domain.html.ini index 3c4d9edc50f..829514a8656 100644 --- a/tests/wpt/mozilla/meta/mozilla/adopted_node_is_same_origin_domain.html.ini +++ b/tests/wpt/mozilla/meta/mozilla/adopted_node_is_same_origin_domain.html.ini @@ -2,3 +2,4 @@ type: testharness [Adopting a node should make it same-origin-domain.] expected: FAIL + diff --git a/tests/wpt/mozilla/meta/mozilla/worklets/test_worklet.html.ini b/tests/wpt/mozilla/meta/mozilla/worklets/test_worklet.html.ini new file mode 100644 index 00000000000..37762a513dd --- /dev/null +++ b/tests/wpt/mozilla/meta/mozilla/worklets/test_worklet.html.ini @@ -0,0 +1,3 @@ +[test_worklet.html] + type: testharness + prefs: [dom.worklet.testing.enabled:true] diff --git a/tests/wpt/mozilla/tests/css/min_width_percent_root_a.html b/tests/wpt/mozilla/tests/css/min_width_percent_root_a.html new file mode 100644 index 00000000000..0def43e7e3c --- /dev/null +++ b/tests/wpt/mozilla/tests/css/min_width_percent_root_a.html @@ -0,0 +1,16 @@ +<!doctype html> +<html> + <head> + <meta charset="UTF-8"> + <link rel="match" href="min_width_percent_root_b.html"> + <title>absolute root element with percentage min-width</title> + <style> + html { + position: absolute; + min-width: 100%; + border: 2px solid red; + } + </style> + </head> + <body></body> +</html> diff --git a/tests/wpt/mozilla/tests/css/min_width_percent_root_b.html b/tests/wpt/mozilla/tests/css/min_width_percent_root_b.html new file mode 100644 index 00000000000..fad1df06f09 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/min_width_percent_root_b.html @@ -0,0 +1,14 @@ +<!doctype html> +<html> + <head> + <meta charset="UTF-8"> + <title>absolute root element with percentage min-width reference</title> + <style> + html { + min-width: 100%; + border: 2px solid red; + } + </style> + </head> + <body></body> +</html> diff --git a/tests/wpt/mozilla/tests/css/transform_3d_from_outside_viewport.html b/tests/wpt/mozilla/tests/css/transform_3d_from_outside_viewport.html new file mode 100644 index 00000000000..f400968bb85 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/transform_3d_from_outside_viewport.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>Ensure that content transformed from outside the viewport is displayed</title> + <link rel="match" href="transform_3d_from_outside_viewport_ref.html"> + <style> + #container { + left: 0; + top: 0; + position: absolute; + width: 100%; + height: 100%; + overflow: hidden; + perspective: 2000px; + perspective-origin: 20% 50%; + background: yellow; + } + + #transformed { + height: 200px; + transform: translate3d(0px, 0px, -1300px) rotateY(45deg); + background: green; + } + </style> +</head> + +<body> + <div id="container"> + <div id="transformed"></div> + </div> +</body> +</html> diff --git a/tests/wpt/mozilla/tests/css/transform_3d_from_outside_viewport_ref.html b/tests/wpt/mozilla/tests/css/transform_3d_from_outside_viewport_ref.html new file mode 100644 index 00000000000..1cd20131596 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/transform_3d_from_outside_viewport_ref.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>Ensure that content transformed from outside the viewport is displayed</title> + <style> + body { + background: yellow; + } + </style> +</head> + +<body> +</body> +</html> diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.html b/tests/wpt/mozilla/tests/mozilla/interfaces.html index a29f439a7c6..ee987ae9322 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.html @@ -200,6 +200,7 @@ test_interfaces([ "WebSocket", "Window", "Worker", + "Worklet", "XMLDocument", "XMLHttpRequest", "XMLHttpRequestEventTarget", diff --git a/tests/wpt/mozilla/tests/mozilla/worklets/syntax_error.js b/tests/wpt/mozilla/tests/mozilla/worklets/syntax_error.js new file mode 100644 index 00000000000..4adade8939c --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/worklets/syntax_error.js @@ -0,0 +1 @@ +{]; diff --git a/tests/wpt/mozilla/tests/mozilla/worklets/test_worklet.html b/tests/wpt/mozilla/tests/mozilla/worklets/test_worklet.html new file mode 100644 index 00000000000..d7a9efa04fe --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/worklets/test_worklet.html @@ -0,0 +1,35 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Test worklet loading</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script> +var testWorklet = new TestWorklet(); +var host_info = get_host_info(); + +promise_test(function() { + return testWorklet.addModule("test_worklet.js") + .then(function () { + assert_equals(testWorklet.lookup("hello"), "world"); + }); +}, "Loading a test worklet."); + +promise_test(function(t) { + var path = new URL("test_worklet.js", document.location).pathname; + var url = new URL(path, host_info.HTTP_REMOTE_ORIGIN); + return promise_rejects(t, "AbortError", testWorklet.addModule(url)); +}, "Loading a cross-origin test worklet."); + +promise_test(function(t) { + return promise_rejects(t, "AbortError", testWorklet.addModule("nonexistent_worklet.js")); +}, "Loading a nonexistent test worklet."); + +promise_test(function(t) { + return promise_rejects(t, "AbortError", testWorklet.addModule("syntax_error.js")); +}, "Loading a syntactically incorrect test worklet."); + +promise_test(function(t) { + return promise_rejects(t, "AbortError", testWorklet.addModule("throw_exception.js")); +}, "Loading an exception-throwing test worklet."); +</script> diff --git a/tests/wpt/mozilla/tests/mozilla/worklets/test_worklet.js b/tests/wpt/mozilla/tests/mozilla/worklets/test_worklet.js new file mode 100644 index 00000000000..9c0b392a6ab --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/worklets/test_worklet.js @@ -0,0 +1 @@ +registerKeyValue("hello", "world"); diff --git a/tests/wpt/mozilla/tests/mozilla/worklets/throw_exception.js b/tests/wpt/mozilla/tests/mozilla/worklets/throw_exception.js new file mode 100644 index 00000000000..6ca4f80fc27 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/worklets/throw_exception.js @@ -0,0 +1 @@ +throw new TypeError(); |