Merge branch 'master' into tidy

This commit is contained in:
Alex Touchet 2018-09-11 09:06:42 -07:00 committed by GitHub
commit 025b5550fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2805 changed files with 67337 additions and 36455 deletions

181
Cargo.lock generated
View file

@ -454,6 +454,14 @@ dependencies = [
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cmake"
version = "0.1.29"
@ -603,6 +611,18 @@ dependencies = [
"build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-channel"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-deque"
version = "0.2.0"
@ -626,6 +646,19 @@ dependencies = [
"scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-epoch"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-utils"
version = "0.2.2"
@ -634,6 +667,11 @@ dependencies = [
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-utils"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cssparser"
version = "0.24.0"
@ -899,7 +937,7 @@ version = "0.3.0"
source = "git+https://github.com/energymon/energymon-sys.git#f8d77ea2906b25f9c0fd358aa9d300a46dc3e97c"
dependencies = [
"cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -973,7 +1011,7 @@ version = "2.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1191,7 +1229,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1218,7 +1256,7 @@ dependencies = [
"wayland-client 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winit 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"x11-dl 2.18.1 (registry+https://github.com/rust-lang/crates.io-index)",
"x11-dl 2.18.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1238,7 +1276,7 @@ dependencies = [
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1284,7 +1322,7 @@ dependencies = [
"gstreamer-base-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1313,7 +1351,7 @@ dependencies = [
"gstreamer-base-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1340,7 +1378,7 @@ dependencies = [
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1369,7 +1407,7 @@ dependencies = [
"gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gstreamer-video-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1381,7 +1419,7 @@ dependencies = [
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1412,7 +1450,7 @@ dependencies = [
"gstreamer-base-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1432,7 +1470,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1459,7 +1497,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1623,12 +1661,29 @@ dependencies = [
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ipc-channel"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-channel 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "itertools"
version = "0.7.6"
@ -1844,7 +1899,7 @@ name = "libdbus-sys"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1921,7 +1976,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2135,7 +2190,7 @@ dependencies = [
[[package]]
name = "mio"
version = "0.6.12"
version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2144,10 +2199,10 @@ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2157,7 +2212,7 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2285,14 +2340,12 @@ dependencies = [
[[package]]
name = "net2"
version = "0.2.29"
version = "0.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -2491,7 +2544,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2566,7 +2619,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "petgraph"
version = "0.4.12"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2610,7 +2663,7 @@ dependencies = [
[[package]]
name = "pkg-config"
version = "0.3.12"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -2752,6 +2805,23 @@ dependencies = [
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "range"
version = "0.0.1"
@ -2780,7 +2850,7 @@ dependencies = [
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -3182,7 +3252,7 @@ version = "4.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"expat-sys 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"servo-freetype-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -3192,13 +3262,13 @@ version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "servo-media"
version = "0.1.0"
source = "git+https://github.com/servo/media#6ecac1c6259b3995e8d6a368e49777e5c2d398ae"
source = "git+https://github.com/servo/media#44ad355b020168e78ab32db2c6f5286e7db2ba77"
dependencies = [
"servo-media-audio 0.1.0 (git+https://github.com/servo/media)",
"servo-media-gstreamer 0.1.0 (git+https://github.com/servo/media)",
@ -3208,12 +3278,12 @@ dependencies = [
[[package]]
name = "servo-media-audio"
version = "0.1.0"
source = "git+https://github.com/servo/media#6ecac1c6259b3995e8d6a368e49777e5c2d398ae"
source = "git+https://github.com/servo/media#44ad355b020168e78ab32db2c6f5286e7db2ba77"
dependencies = [
"byte-slice-cast 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"petgraph 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_media_derive 0.1.0 (git+https://github.com/servo/media)",
"smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -3221,7 +3291,7 @@ dependencies = [
[[package]]
name = "servo-media-gstreamer"
version = "0.1.0"
source = "git+https://github.com/servo/media#6ecac1c6259b3995e8d6a368e49777e5c2d398ae"
source = "git+https://github.com/servo/media#44ad355b020168e78ab32db2c6f5286e7db2ba77"
dependencies = [
"byte-slice-cast 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3229,7 +3299,7 @@ dependencies = [
"gstreamer-app 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
"gstreamer-audio 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
"gstreamer-player 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ipc-channel 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ipc-channel 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"servo-media-audio 0.1.0 (git+https://github.com/servo/media)",
"servo-media-player 0.1.0 (git+https://github.com/servo/media)",
@ -3239,9 +3309,9 @@ dependencies = [
[[package]]
name = "servo-media-player"
version = "0.1.0"
source = "git+https://github.com/servo/media#6ecac1c6259b3995e8d6a368e49777e5c2d398ae"
source = "git+https://github.com/servo/media#44ad355b020168e78ab32db2c6f5286e7db2ba77"
dependencies = [
"ipc-channel 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ipc-channel 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -3325,7 +3395,7 @@ dependencies = [
[[package]]
name = "servo_media_derive"
version = "0.1.0"
source = "git+https://github.com/servo/media#6ecac1c6259b3995e8d6a368e49777e5c2d398ae"
source = "git+https://github.com/servo/media#44ad355b020168e78ab32db2c6f5286e7db2ba77"
dependencies = [
"quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3400,6 +3470,11 @@ name = "slab"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "slab"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "smallbitvec"
version = "2.1.1"
@ -4115,7 +4190,7 @@ dependencies = [
"smithay-client-toolkit 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"wayland-client 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"x11-dl 2.18.1 (registry+https://github.com/rust-lang/crates.io-index)",
"x11-dl 2.18.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -4135,7 +4210,7 @@ dependencies = [
"bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
"sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -4158,7 +4233,7 @@ version = "2.17.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -4172,12 +4247,12 @@ dependencies = [
[[package]]
name = "x11-dl"
version = "2.18.1"
version = "2.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -4276,6 +4351,7 @@ dependencies = [
"checksum clap 2.28.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dc34bf7d5d66268b466b9852bca925ec1d2650654dab4da081e63fd230145c2e"
"checksum clipboard 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b9b4623b47d8637fc9d47564583d4cc01eb8c8e34e26b2bf348bf4b036acb657"
"checksum clipboard-win 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "14cc3e6c075926b96490d5f90d4a5af7be8012a4d8a8698e619655085a7641a3"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "56d741ea7a69e577f6d06b36b7dff4738f680593dc27a701ffa8506b73ce28bb"
"checksum cocoa 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5cd1afb83b2de9c41e5dfedb2bcccb779d433b958404876009ae4b01746ff23"
"checksum color_quant 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a475fc4af42d83d28adf72968d9bcfaf035a1a9381642d8e85d8a04957767b0d"
@ -4286,9 +4362,12 @@ dependencies = [
"checksum core-graphics 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92801c908ea6301ae619ed842a72e01098085fc321b9c2f3f833dad555bba055"
"checksum core-text 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "157ff38a92496dc676ce36d9124554e9ac66f1c1039f952690ac64f71cfa5968"
"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
"checksum crossbeam-channel 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6c0a94250b0278d7fc5a894c3d276b11ea164edc8bf8feb10ca1ea517b44a649"
"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
"checksum crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30fecfcac6abfef8771151f8be4abc9e4edc112c2bcb233314cafde2680536e9"
"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
"checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015"
"checksum cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)" = "495beddc39b1987b8e9f029354eccbd5ef88eb5f1cd24badb764dce338acf2e0"
"checksum cssparser-macros 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f3a5383ae18dbfdeb569ed62019f5bddb2a95cd2d3833313c475a0d014777805"
"checksum darling 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a78af487e4eb8f4421a1770687b328af6bb4494ca93435210678c6eea875c11"
@ -4373,6 +4452,7 @@ dependencies = [
"checksum io-surface 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9a33981dff54baaff80f4decb487a65d148a3c00facc97820d0f09128f74dd"
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
"checksum ipc-channel 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db9daf099728ac5390c73f54e6e3708f0c514d2b51f24373830f568702eadfca"
"checksum ipc-channel 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd75debad4ffd295c00c6e3634d254df30050b0837a85e5cd039ac424365f24a"
"checksum itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b07332223953b5051bceb67e8c4700aa65291535568e1f12408c43c4a42c0394"
"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682"
"checksum jemalloc-sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "479294d130502fada93c7a957e8d059b632b03d6204aca37af557dee947f30a9"
@ -4406,7 +4486,7 @@ dependencies = [
"checksum miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "609ce024854aeb19a0ef7567d348aaa5a746b32fb72e336df7fcc16869d7e2b4"
"checksum miniz_oxide 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9ba430291c9d6cedae28bcd2d49d1c32fc57d60cd49086646c5dd5673a870eb5"
"checksum miniz_oxide_c_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5a5b8234d6103ebfba71e29786da4608540f862de5ce980a1c94f86a40ca0d51"
"checksum mio 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "75f72a93f046f1517e3cfddc0a096eb756a2ba727d36edc8227dee769a50a9b0"
"checksum mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)" = "4fcfcb32d63961fb6f367bfd5d21e4600b92cd310f71f9dca25acae196eb1560"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
"checksum mitochondria 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9de3eca27871df31c33b807f834b94ef7d000956f57aa25c5aed9c5f0aae8f6f"
"checksum mozangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "45a8a18a41cfab0fde25cc2f43ea89064d211a0fbb33225b8ff93ab20406e0e7"
@ -4416,7 +4496,7 @@ dependencies = [
"checksum mp4parse 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7316728464443fe5793a805dde3257864e9690cf46374daff3ce93de1df2f254"
"checksum msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aad9dfe950c057b1bfe9c1f2aa51583a8468ef2a5baba2ebbe06d775efeb7729"
"checksum muldiv 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cbef5aa2e8cd82a18cc20e26434cc9843e1ef46e55bfabe5bddb022236c5b3e"
"checksum net2 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "bc01404e7568680f1259aa5729539f221cb1e6d047a0d9053cab4be8a73b5d67"
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
"checksum new-ordered-float 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8ccbebba6fb53a6d2bdcfaf79cb339bc136dee3bfff54dc337a334bafe36476a"
"checksum new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0cdc457076c78ab54d5e0d6fa7c47981757f1e34dc39ff92787f217dede586c4"
"checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17"
@ -4446,12 +4526,12 @@ dependencies = [
"checksum parking_lot_core 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "538ef00b7317875071d5e00f603f24d16f0b474c1a5fc0ccb8b454ca72eafa79"
"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
"checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356"
"checksum petgraph 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "8b30dc85588cd02b9b76f5e386535db546d21dc68506cff2abebee0b6445e8e4"
"checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f"
"checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc"
"checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f"
"checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03"
"checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2"
"checksum pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "6a52e4dbc8354505ee07e484ab07127e06d87ca6fa7f0a516a2b294e5ad5ad16"
"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c"
"checksum plane-split 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ff3a4fc9e31d70eb6828e9a2d7a401a824d9f281686a39a8fc06f08796edb1bb"
"checksum png 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f54b9600d584d3b8a739e1662a595fab051329eff43f20e7d8cc22872962145b"
"checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd"
@ -4464,6 +4544,8 @@ dependencies = [
"checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035"
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
"checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2"
"checksum rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "485541959c8ecc49865526fe6c4de9653dd6e60d829d6edf0be228167b60372d"
"checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8"
"checksum redox_syscall 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "29dbdfd4b9df8ab31dec47c6087b7b13cbf4a776f335e4de8efba8288dda075b"
@ -4504,6 +4586,7 @@ dependencies = [
"checksum signpost 0.1.0 (git+https://github.com/pcwalton/signpost.git)" = "<none>"
"checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537"
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
"checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d"
"checksum smallbitvec 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c63726029f0069f88467873e47f392575f28f9f16b72ac65465263db4b3a13c"
"checksum smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "26df3bb03ca5eac2e64192b723d51f56c1b1e0860e7c766281f4598f181acdc8"
"checksum smithay-client-toolkit 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2051bffc6cbf271176e8ba1527f801b6444567daee15951ff5152aaaf7777b2f"
@ -4573,7 +4656,7 @@ dependencies = [
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
"checksum x11 2.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e5c4ac579b5d324dc4add02312b5d0e3e0218521e2d5779d526ac39ee4bb171"
"checksum x11-clipboard 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2e7374c7699210cca7084ca61d57e09640fc744d1391808cb9ae2fe4ca9bd1df"
"checksum x11-dl 2.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "966f78e9291e51d573bd3dd9287b285c0265daa8aa9fbe74c370467baa360c4e"
"checksum x11-dl 2.18.3 (registry+https://github.com/rust-lang/crates.io-index)" = "940586acb859ea05c53971ac231685799a7ec1dee66ac0bccc0e6ad96e06b4e3"
"checksum xcb 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5e917a3f24142e9ff8be2414e36c649d47d6cc2ba81f16201cdef96e533e02de"
"checksum xi-unicode 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "12ea8eda4b1eb72f02d148402e23832d56a33f55d8c1b2d5bcdde91d79d47cb1"
"checksum xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c1cb601d29fe2c2ac60a2b2e5e293994d87a1f6fa9687a31a15270f909be9c2"

View file

@ -101,9 +101,10 @@ If `virtualenv` does not exist, try `python-virtualenv`.
``` sh
sudo dnf install curl libtool gcc-c++ libXi-devel \
freetype-devel mesa-libGL-devel mesa-libEGL-devel glib2-devel libX11-devel libXrandr-devel gperf \
fontconfig-devel cabextract ttmkfdir python python-virtualenv python-pip expat-devel \
fontconfig-devel cabextract ttmkfdir python2 python2-virtualenv python2-pip expat-devel \
rpm-build openssl-devel cmake bzip2-devel libXcursor-devel libXmu-devel mesa-libOSMesa-devel \
dbus-devel ncurses-devel harfbuzz-devel ccache mesa-libGLU-devel clang clang-libs gstreamer autoconf213
dbus-devel ncurses-devel harfbuzz-devel ccache mesa-libGLU-devel clang clang-libs gstreamer1-devel \
gstreamer1-plugins-base-devel gstreamer1-plugins-bad-free-devel autoconf213
```
#### On CentOS
@ -132,13 +133,13 @@ export LIBCLANG_PATH=/opt/rh/llvm-toolset-7/root/usr/lib64
sudo zypper install libX11-devel libexpat-devel libbz2-devel Mesa-libEGL-devel Mesa-libGL-devel cabextract cmake \
dbus-1-devel fontconfig-devel freetype-devel gcc-c++ git glib2-devel gperf \
harfbuzz-devel libOSMesa-devel libXcursor-devel libXi-devel libXmu-devel libXrandr-devel libopenssl-devel \
python-pip python-virtualenv rpm-build glu-devel ccache llvm-clang libclang
python-pip python-virtualenv rpm-build glu-devel ccache llvm-clang libclang
```
#### On Arch Linux
``` sh
sudo pacman -S --needed base-devel git python2 python2-virtualenv python2-pip mesa cmake bzip2 libxmu glu \
pkg-config ttf-fira-sans harfbuzz ccache clang
pkg-config ttf-fira-sans harfbuzz ccache clang autoconf2.13
```
#### On Gentoo Linux

View file

@ -44,7 +44,7 @@ cache:
- .ccache
install:
- choco install pkgconfiglite
- appveyor-retry choco install pkgconfiglite
- appveyor-retry appveyor DownloadFile https://gstreamer.freedesktop.org/data/pkg/windows/1.14.1/gstreamer-1.0-devel-x86_64-1.14.1.msi
- appveyor-retry appveyor DownloadFile https://gstreamer.freedesktop.org/data/pkg/windows/1.14.1/gstreamer-1.0-x86_64-1.14.1.msi
- msiexec /i gstreamer-1.0-devel-x86_64-1.14.1.msi /quiet /qn /norestart /log install-devel.log
@ -54,8 +54,6 @@ install:
- rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain none
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- set PKG_CONFIG_PATH=%PKG_CONFIG_PATH%;C:\gstreamer\1.0\x86_64\lib\pkgconfig
- set LIB=%LIB%;C:\gstreamer\1.0\x86_64\lib
- set LIBPATH=C:\gstreamer\1.0\x86_64\lib;%LIBPATH%
- rustup -V
- mach rustc --version
- mach cargo --version

View file

@ -36,6 +36,7 @@ invalid
keydown
keypress
left
ltr
load
loadeddata
loadedmetadata
@ -65,6 +66,7 @@ readystatechange
reftest-wait
reset
right
rtl
sans-serif
scan
screen

View file

@ -22,23 +22,38 @@ pub struct WebGLThreads(WebGLSender<WebGLMsg>);
impl WebGLThreads {
/// Creates a new WebGLThreads object
pub fn new(gl_factory: GLContextFactory,
webrender_gl: Rc<gl::Gl>,
webrender_api_sender: webrender_api::RenderApiSender,
webvr_compositor: Option<Box<WebVRRenderHandler>>)
-> (WebGLThreads, Box<webrender::ExternalImageHandler>, Option<Box<webrender::OutputImageHandler>>) {
pub fn new(
gl_factory: GLContextFactory,
webrender_gl: Rc<gl::Gl>,
webrender_api_sender: webrender_api::RenderApiSender,
webvr_compositor: Option<Box<WebVRRenderHandler>>,
) -> (
WebGLThreads,
Box<webrender::ExternalImageHandler>,
Option<Box<webrender::OutputImageHandler>>,
) {
// This implementation creates a single `WebGLThread` for all the pipelines.
let channel = WebGLThread::start(gl_factory,
webrender_api_sender,
webvr_compositor.map(|c| WebVRRenderWrapper(c)),
PhantomData);
let channel = WebGLThread::start(
gl_factory,
webrender_api_sender,
webvr_compositor.map(|c| WebVRRenderWrapper(c)),
PhantomData,
);
let output_handler = if PREFS.is_dom_to_texture_enabled() {
Some(Box::new(OutputHandler::new(webrender_gl.clone(), channel.clone())))
Some(Box::new(OutputHandler::new(
webrender_gl.clone(),
channel.clone(),
)))
} else {
None
};
let external = WebGLExternalImageHandler::new(WebGLExternalImages::new(webrender_gl, channel.clone()));
(WebGLThreads(channel), Box::new(external), output_handler.map(|b| b as Box<_>))
let external =
WebGLExternalImageHandler::new(WebGLExternalImages::new(webrender_gl, channel.clone()));
(
WebGLThreads(channel),
Box::new(external),
output_handler.map(|b| b as Box<_>),
)
}
/// Gets the WebGLThread handle for each script pipeline.
@ -49,7 +64,9 @@ impl WebGLThreads {
/// Sends a exit message to close the WebGLThreads and release all WebGLContexts.
pub fn exit(&self) -> Result<(), &'static str> {
self.0.send(WebGLMsg::Exit).map_err(|_| "Failed to send Exit message")
self.0
.send(WebGLMsg::Exit)
.map_err(|_| "Failed to send Exit message")
}
}
@ -58,7 +75,10 @@ struct WebGLExternalImages {
webrender_gl: Rc<gl::Gl>,
webgl_channel: WebGLSender<WebGLMsg>,
// Used to avoid creating a new channel on each received WebRender request.
lock_channel: (WebGLSender<(u32, Size2D<i32>, usize)>, WebGLReceiver<(u32, Size2D<i32>, usize)>),
lock_channel: (
WebGLSender<(u32, Size2D<i32>, usize)>,
WebGLReceiver<(u32, Size2D<i32>, usize)>,
),
}
impl WebGLExternalImages {
@ -75,12 +95,15 @@ impl WebGLExternalImageApi for WebGLExternalImages {
fn lock(&mut self, ctx_id: WebGLContextId) -> (u32, Size2D<i32>) {
// WebGL Thread has it's own GL command queue that we need to synchronize with the WR GL command queue.
// The WebGLMsg::Lock message inserts a fence in the WebGL command queue.
self.webgl_channel.send(WebGLMsg::Lock(ctx_id, self.lock_channel.0.clone())).unwrap();
self.webgl_channel
.send(WebGLMsg::Lock(ctx_id, self.lock_channel.0.clone()))
.unwrap();
let (image_id, size, gl_sync) = self.lock_channel.1.recv().unwrap();
// The next glWaitSync call is run on the WR thread and it's used to synchronize the two
// flows of OpenGL commands in order to avoid WR using a semi-ready WebGL texture.
// glWaitSync doesn't block WR thread, it affects only internal OpenGL subsystem.
self.webrender_gl.wait_sync(gl_sync as gl::GLsync, 0, gl::TIMEOUT_IGNORED);
self.webrender_gl
.wait_sync(gl_sync as gl::GLsync, 0, gl::TIMEOUT_IGNORED);
(image_id, size)
}
@ -92,11 +115,17 @@ impl WebGLExternalImageApi for WebGLExternalImages {
/// Custom observer used in a `WebGLThread`.
impl WebGLThreadObserver for PhantomData<()> {
fn on_context_create(&mut self, ctx_id: WebGLContextId, texture_id: u32, size: Size2D<i32>) {
debug!("WebGLContext created (ctx_id: {:?} texture_id: {:?} size: {:?}", ctx_id, texture_id, size);
debug!(
"WebGLContext created (ctx_id: {:?} texture_id: {:?} size: {:?}",
ctx_id, texture_id, size
);
}
fn on_context_resize(&mut self, ctx_id: WebGLContextId, texture_id: u32, size: Size2D<i32>) {
debug!("WebGLContext resized (ctx_id: {:?} texture_id: {:?} size: {:?}", ctx_id, texture_id, size);
debug!(
"WebGLContext resized (ctx_id: {:?} texture_id: {:?} size: {:?}",
ctx_id, texture_id, size
);
}
fn on_context_delete(&mut self, ctx_id: WebGLContextId) {
@ -104,7 +133,6 @@ impl WebGLThreadObserver for PhantomData<()> {
}
}
/// Wrapper to send WebVR commands used in `WebGLThread`.
struct WebVRRenderWrapper(Box<WebVRRenderHandler>);
@ -120,7 +148,10 @@ struct OutputHandler {
webrender_gl: Rc<gl::Gl>,
webgl_channel: WebGLSender<WebGLMsg>,
// Used to avoid creating a new channel on each received WebRender request.
lock_channel: (WebGLSender<OutputHandlerData>, WebGLReceiver<OutputHandlerData>),
lock_channel: (
WebGLSender<OutputHandlerData>,
WebGLReceiver<OutputHandlerData>,
),
sync_objects: FnvHashMap<webrender_api::PipelineId, gl::GLsync>,
}
@ -137,14 +168,24 @@ impl OutputHandler {
/// Bridge between the WR frame outputs and WebGL to implement DOMToTexture synchronization.
impl webrender::OutputImageHandler for OutputHandler {
fn lock(&mut self, id: webrender_api::PipelineId) -> Option<(u32, webrender_api::DeviceIntSize)> {
fn lock(
&mut self,
id: webrender_api::PipelineId,
) -> Option<(u32, webrender_api::DeviceIntSize)> {
// Insert a fence in the WR command queue
let gl_sync = self.webrender_gl.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0);
let gl_sync = self
.webrender_gl
.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0);
// The lock command adds a WaitSync call on the WebGL command flow.
let command = DOMToTextureCommand::Lock(id, gl_sync as usize, self.lock_channel.0.clone());
self.webgl_channel.send(WebGLMsg::DOMToTextureCommand(command)).unwrap();
self.webgl_channel
.send(WebGLMsg::DOMToTextureCommand(command))
.unwrap();
self.lock_channel.1.recv().unwrap().map(|(tex_id, size)| {
(tex_id, webrender_api::DeviceIntSize::new(size.width, size.height))
(
tex_id,
webrender_api::DeviceIntSize::new(size.width, size.height),
)
})
}

View file

@ -7,8 +7,8 @@ use canvas_traits::webgl::*;
use euclid::Size2D;
use fnv::FnvHashMap;
use gleam::gl;
use ipc_channel::ipc::IpcBytesSender;
use offscreen_gl_context::{GLContext, GLContextAttributes, GLLimits, NativeGLContextMethods};
use serde_bytes::ByteBuf;
use std::thread;
use super::gl_context::{GLContextFactory, GLContextWrapper};
use webrender;
@ -659,10 +659,12 @@ impl WebGLImpl {
ctx.gl().blend_func(src, dest),
WebGLCommand::BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha) =>
ctx.gl().blend_func_separate(src_rgb, dest_rgb, src_alpha, dest_alpha),
WebGLCommand::BufferData(buffer_type, ref data, usage) =>
gl::buffer_data(ctx.gl(), buffer_type, data, usage),
WebGLCommand::BufferSubData(buffer_type, offset, ref data) =>
gl::buffer_sub_data(ctx.gl(), buffer_type, offset, data),
WebGLCommand::BufferData(buffer_type, ref receiver, usage) => {
gl::buffer_data(ctx.gl(), buffer_type, &receiver.recv().unwrap(), usage)
},
WebGLCommand::BufferSubData(buffer_type, offset, ref receiver) => {
gl::buffer_sub_data(ctx.gl(), buffer_type, offset, &receiver.recv().unwrap())
},
WebGLCommand::Clear(mask) =>
ctx.gl().clear(mask),
WebGLCommand::ClearColor(r, g, b, a) =>
@ -711,8 +713,9 @@ impl WebGLImpl {
ctx.gl().pixel_store_i(name, val),
WebGLCommand::PolygonOffset(factor, units) =>
ctx.gl().polygon_offset(factor, units),
WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, ref chan) =>
Self::read_pixels(ctx.gl(), x, y, width, height, format, pixel_type, chan),
WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, ref chan) => {
Self::read_pixels(ctx.gl(), x, y, width, height, format, pixel_type, chan)
}
WebGLCommand::RenderbufferStorage(target, format, width, height) =>
ctx.gl().renderbuffer_storage(target, format, width, height),
WebGLCommand::SampleCoverage(value, invert) =>
@ -831,11 +834,32 @@ impl WebGLImpl {
WebGLCommand::SetViewport(x, y, width, height) => {
ctx.gl().viewport(x, y, width, height);
}
WebGLCommand::TexImage2D(target, level, internal, width, height, format, data_type, ref data) =>
ctx.gl().tex_image_2d(target, level, internal, width, height,
/*border*/0, format, data_type, Some(data)),
WebGLCommand::TexSubImage2D(target, level, xoffset, yoffset, x, y, width, height, ref data) =>
ctx.gl().tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height, data),
WebGLCommand::TexImage2D(target, level, internal, width, height, format, data_type, ref chan) => {
ctx.gl().tex_image_2d(
target,
level,
internal,
width,
height,
0,
format,
data_type,
Some(&chan.recv().unwrap()),
)
}
WebGLCommand::TexSubImage2D(target, level, xoffset, yoffset, x, y, width, height, ref chan) => {
ctx.gl().tex_sub_image_2d(
target,
level,
xoffset,
yoffset,
x,
y,
width,
height,
&chan.recv().unwrap(),
)
}
WebGLCommand::DrawingBufferWidth(ref sender) =>
sender.send(ctx.borrow_draw_buffer().unwrap().size().width).unwrap(),
WebGLCommand::DrawingBufferHeight(ref sender) =>
@ -1163,10 +1187,10 @@ impl WebGLImpl {
height: i32,
format: u32,
pixel_type: u32,
chan: &WebGLSender<ByteBuf>,
chan: &IpcBytesSender,
) {
let result = gl.read_pixels(x, y, width, height, format, pixel_type);
chan.send(result.into()).unwrap()
chan.send(&result).unwrap()
}
fn finish(gl: &gl::Gl, chan: &WebGLSender<()>) {

View file

@ -4,6 +4,7 @@
use euclid::Size2D;
use gleam::gl;
use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender};
use offscreen_gl_context::{GLContextAttributes, GLLimits};
use serde_bytes::ByteBuf;
use std::borrow::Cow;
@ -24,7 +25,7 @@ pub use ::webgl_channel::WebGLPipeline;
pub use ::webgl_channel::WebGLChan;
/// WebGL Message API
#[derive(Clone, Deserialize, Serialize)]
#[derive(Deserialize, Serialize)]
pub enum WebGLMsg {
/// Creates a new WebGLContext.
CreateContext(WebGLVersion, Size2D<i32>, GLContextAttributes,
@ -155,7 +156,7 @@ impl WebGLMsgSender {
}
/// WebGL Commands for a specific WebGLContext
#[derive(Clone, Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize)]
pub enum WebGLCommand {
GetContextAttributes(WebGLSender<GLContextAttributes>),
ActiveTexture(u32),
@ -167,8 +168,8 @@ pub enum WebGLCommand {
AttachShader(WebGLProgramId, WebGLShaderId),
DetachShader(WebGLProgramId, WebGLShaderId),
BindAttribLocation(WebGLProgramId, u32, String),
BufferData(u32, ByteBuf, u32),
BufferSubData(u32, isize, ByteBuf),
BufferData(u32, IpcBytesReceiver, u32),
BufferSubData(u32, isize, IpcBytesReceiver),
Clear(u32),
ClearColor(f32, f32, f32, f32),
ClearDepth(f32),
@ -213,7 +214,7 @@ pub enum WebGLCommand {
GetRenderbufferParameter(u32, u32, WebGLSender<i32>),
PolygonOffset(f32, f32),
RenderbufferStorage(u32, u32, i32, i32),
ReadPixels(i32, i32, i32, i32, u32, u32, WebGLSender<ByteBuf>),
ReadPixels(i32, i32, i32, i32, u32, u32, IpcBytesSender),
SampleCoverage(f32, bool),
Scissor(i32, i32, i32, i32),
StencilFunc(u32, i32, u32),
@ -251,8 +252,8 @@ pub enum WebGLCommand {
VertexAttribPointer(u32, i32, u32, bool, i32, u32),
VertexAttribPointer2f(u32, i32, bool, i32, u32),
SetViewport(i32, i32, i32, i32),
TexImage2D(u32, i32, i32, i32, i32, u32, u32, ByteBuf),
TexSubImage2D(u32, i32, i32, i32, i32, i32, u32, u32, ByteBuf),
TexImage2D(u32, i32, i32, i32, i32, u32, u32, IpcBytesReceiver),
TexSubImage2D(u32, i32, i32, i32, i32, i32, u32, u32, IpcBytesReceiver),
DrawingBufferWidth(WebGLSender<i32>),
DrawingBufferHeight(WebGLSender<i32>),
Finish(WebGLSender<()>),

View file

@ -9,7 +9,7 @@ use std::io;
pub type WebGLSender<T> = ipc_channel::ipc::IpcSender<T>;
pub type WebGLReceiver<T> = ipc_channel::ipc::IpcReceiver<T>;
pub fn webgl_channel<T: Serialize + for<'de> Deserialize<'de>>()
-> Result<(WebGLSender<T>, WebGLReceiver<T>), io::Error> {
pub fn webgl_channel<T: Serialize + for<'de> Deserialize<'de>>(
) -> Result<(WebGLSender<T>, WebGLReceiver<T>), io::Error> {
ipc_channel::ipc::channel()
}

View file

@ -13,17 +13,27 @@ use servo_config::opts;
use std::fmt;
lazy_static! {
static ref IS_MULTIPROCESS: bool = {
opts::multiprocess()
};
static ref IS_MULTIPROCESS: bool = { opts::multiprocess() };
}
#[derive(Clone, Deserialize, Serialize)]
#[derive(Deserialize, Serialize)]
pub enum WebGLSender<T: Serialize> {
Ipc(ipc::WebGLSender<T>),
Mpsc(mpsc::WebGLSender<T>),
}
impl<T> Clone for WebGLSender<T>
where
T: Serialize,
{
fn clone(&self) -> Self {
match *self {
WebGLSender::Ipc(ref chan) => WebGLSender::Ipc(chan.clone()),
WebGLSender::Mpsc(ref chan) => WebGLSender::Mpsc(chan.clone()),
}
}
}
impl<T: Serialize> fmt::Debug for WebGLSender<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "WebGLSender(..)")
@ -34,41 +44,42 @@ impl<T: Serialize> WebGLSender<T> {
#[inline]
pub fn send(&self, msg: T) -> WebGLSendResult {
match *self {
WebGLSender::Ipc(ref sender) => {
sender.send(msg).map_err(|_| ())
},
WebGLSender::Mpsc(ref sender) => {
sender.send(msg).map_err(|_| ())
}
WebGLSender::Ipc(ref sender) => sender.send(msg).map_err(|_| ()),
WebGLSender::Mpsc(ref sender) => sender.send(msg).map_err(|_| ()),
}
}
}
pub type WebGLSendResult = Result<(), ()>;
pub enum WebGLReceiver<T> where T: for<'de> Deserialize<'de> + Serialize {
pub enum WebGLReceiver<T>
where
T: for<'de> Deserialize<'de> + Serialize,
{
Ipc(ipc::WebGLReceiver<T>),
Mpsc(mpsc::WebGLReceiver<T>),
}
impl<T> WebGLReceiver<T> where T: for<'de> Deserialize<'de> + Serialize {
impl<T> WebGLReceiver<T>
where
T: for<'de> Deserialize<'de> + Serialize,
{
pub fn recv(&self) -> Result<T, ()> {
match *self {
WebGLReceiver::Ipc(ref receiver) => {
receiver.recv().map_err(|_| ())
},
WebGLReceiver::Mpsc(ref receiver) => {
receiver.recv().map_err(|_| ())
}
WebGLReceiver::Ipc(ref receiver) => receiver.recv().map_err(|_| ()),
WebGLReceiver::Mpsc(ref receiver) => receiver.recv().map_err(|_| ()),
}
}
}
pub fn webgl_channel<T>() -> Result<(WebGLSender<T>, WebGLReceiver<T>), ()>
where T: for<'de> Deserialize<'de> + Serialize {
where
T: for<'de> Deserialize<'de> + Serialize,
{
if *IS_MULTIPROCESS {
ipc::webgl_channel().map(|(tx, rx)| (WebGLSender::Ipc(tx), WebGLReceiver::Ipc(rx)))
.map_err(|_| ())
ipc::webgl_channel()
.map(|(tx, rx)| (WebGLSender::Ipc(tx), WebGLReceiver::Ipc(rx)))
.map_err(|_| ())
} else {
mpsc::webgl_channel().map(|(tx, rx)| (WebGLSender::Mpsc(tx), WebGLReceiver::Mpsc(rx)))
}

View file

@ -17,17 +17,24 @@ macro_rules! unreachable_serializable {
impl<'a, T> Deserialize<'a> for $name<T> {
fn deserialize<D>(_: D) -> Result<$name<T>, D::Error>
where D: Deserializer<'a> {
where
D: Deserializer<'a>,
{
unreachable!();
}
}
};
}
#[derive(Clone)]
pub struct WebGLSender<T>(mpsc::Sender<T>);
pub struct WebGLReceiver<T>(mpsc::Receiver<T>);
impl<T> Clone for WebGLSender<T> {
fn clone(&self) -> Self {
WebGLSender(self.0.clone())
}
}
impl<T> WebGLSender<T> {
#[inline]
pub fn send(&self, data: T) -> Result<(), mpsc::SendError<T>> {

View file

@ -10,24 +10,33 @@ use std::io::{Read, Write};
use std::path::Path;
fn main() {
let lockfile_path = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()).join("..").join("..").join("Cargo.lock");
let revision_file_path = Path::new(&env::var_os("OUT_DIR").unwrap()).join("webrender_revision.rs");
let lockfile_path = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap())
.join("..")
.join("..")
.join("Cargo.lock");
let revision_file_path =
Path::new(&env::var_os("OUT_DIR").unwrap()).join("webrender_revision.rs");
let mut lockfile = String::new();
File::open(lockfile_path).expect("Cannot open lockfile")
File::open(lockfile_path)
.expect("Cannot open lockfile")
.read_to_string(&mut lockfile)
.expect("Failed to read lockfile");
match toml::from_str::<toml::value::Table>(&lockfile) {
Ok(result) => {
let packages = result.get("package").expect("Cargo lockfile should contain package list");
let packages = result
.get("package")
.expect("Cargo lockfile should contain package list");
match *packages {
toml::Value::Array(ref arr) => {
let source = arr
.iter()
.find(|pkg| pkg.get("name").and_then(|name| name.as_str()).unwrap_or("") == "webrender")
.and_then(|pkg| pkg.get("source").and_then(|source| source.as_str()))
.find(|pkg| {
pkg.get("name").and_then(|name| name.as_str()).unwrap_or("") ==
"webrender"
}).and_then(|pkg| pkg.get("source").and_then(|source| source.as_str()))
.unwrap_or("unknown");
let parsed: Vec<&str> = source.split("#").collect();
@ -36,9 +45,9 @@ fn main() {
let mut revision_module_file = File::create(&revision_file_path).unwrap();
write!(&mut revision_module_file, "{}", format!("\"{}\"", revision)).unwrap();
},
_ => panic!("Cannot find package definitions in lockfile")
_ => panic!("Cannot find package definitions in lockfile"),
}
},
Err(e) => panic!(e)
Err(e) => panic!(e),
}
}

View file

@ -43,7 +43,6 @@ use webrender_api::{self, DeviceIntPoint, DevicePoint, HitTestFlags, HitTestResu
use webrender_api::{LayoutVector2D, ScrollLocation};
use windowing::{self, EmbedderCoordinates, MouseWindowEvent, WebRenderDebugOption, WindowMethods};
#[derive(Debug, PartialEq)]
enum UnableToComposite {
WindowUnprepared,
@ -251,7 +250,7 @@ enum CompositeTarget {
WindowAndPng,
/// Compose to a PNG, write it to disk, and then exit the browser (used for reftests)
PngFile
PngFile,
}
#[derive(Clone)]
@ -273,7 +272,8 @@ impl webrender_api::RenderNotifier for RenderNotifier {
}
fn wake_up(&self) {
self.compositor_proxy.recomposite(CompositingReason::NewWebRenderFrame);
self.compositor_proxy
.recomposite(CompositingReason::NewWebRenderFrame);
}
fn new_frame_ready(
@ -284,7 +284,8 @@ impl webrender_api::RenderNotifier for RenderNotifier {
_render_time_ns: Option<u64>,
) {
if scrolled {
self.compositor_proxy.send(Msg::NewScrollFrameReady(composite_needed));
self.compositor_proxy
.send(Msg::NewScrollFrameReady(composite_needed));
} else {
self.wake_up();
}
@ -295,7 +296,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
fn new(window: Rc<Window>, state: InitialCompositorState) -> Self {
let composite_target = match opts::get().output_file {
Some(_) => CompositeTarget::PngFile,
None => CompositeTarget::Window
None => CompositeTarget::Window,
};
IOCompositor {
@ -372,7 +373,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
// Tell the profiler, memory profiler, and scrolling timer to shut down.
if let Ok((sender, receiver)) = ipc::channel() {
self.time_profiler_chan.send(time::ProfilerMsg::Exit(sender));
self.time_profiler_chan
.send(time::ProfilerMsg::Exit(sender));
let _ = receiver.recv();
}
@ -383,33 +385,33 @@ impl<Window: WindowMethods> IOCompositor<Window> {
match (msg, self.shutdown_state) {
(_, ShutdownState::FinishedShuttingDown) => {
error!("compositor shouldn't be handling messages after shutting down");
return false
}
return false;
},
(Msg::ShutdownComplete, _) => {
self.finish_shutting_down();
return false;
}
},
(Msg::ChangeRunningAnimationsState(pipeline_id, animation_state),
ShutdownState::NotShuttingDown) => {
(
Msg::ChangeRunningAnimationsState(pipeline_id, animation_state),
ShutdownState::NotShuttingDown,
) => {
self.change_running_animations_state(pipeline_id, animation_state);
}
},
(Msg::SetFrameTree(frame_tree),
ShutdownState::NotShuttingDown) => {
(Msg::SetFrameTree(frame_tree), ShutdownState::NotShuttingDown) => {
self.set_frame_tree(&frame_tree);
self.send_viewport_rects();
}
},
(Msg::Recomposite(reason), ShutdownState::NotShuttingDown) => {
self.composition_request = CompositionRequest::CompositeNow(reason)
}
},
(Msg::TouchEventProcessed(result), ShutdownState::NotShuttingDown) => {
self.touch_handler.on_event_processed(result);
}
},
(Msg::CreatePng(reply), ShutdownState::NotShuttingDown) => {
let res = self.composite_specific_target(CompositeTarget::WindowAndPng);
@ -420,15 +422,20 @@ impl<Window: WindowMethods> IOCompositor<Window> {
if let Err(e) = reply.send(img) {
warn!("Sending reply to create png failed ({}).", e);
}
}
},
(Msg::ViewportConstrained(pipeline_id, constraints),
ShutdownState::NotShuttingDown) => {
(
Msg::ViewportConstrained(pipeline_id, constraints),
ShutdownState::NotShuttingDown,
) => {
self.constrain_viewport(pipeline_id, constraints);
}
},
(Msg::IsReadyToSaveImageReply(is_ready), ShutdownState::NotShuttingDown) => {
assert_eq!(self.ready_to_save_state, ReadyState::WaitingForConstellationReply);
assert_eq!(
self.ready_to_save_state,
ReadyState::WaitingForConstellationReply
);
if is_ready {
self.ready_to_save_state = ReadyState::ReadyToSaveImage;
if opts::get().is_running_problem_test {
@ -441,34 +448,38 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
}
self.composite_if_necessary(CompositingReason::Headless);
}
},
(Msg::PipelineVisibilityChanged(pipeline_id, visible), ShutdownState::NotShuttingDown) => {
(
Msg::PipelineVisibilityChanged(pipeline_id, visible),
ShutdownState::NotShuttingDown,
) => {
self.pipeline_details(pipeline_id).visible = visible;
if visible {
self.process_animations();
}
}
},
(Msg::PipelineExited(pipeline_id, sender), _) => {
debug!("Compositor got pipeline exited: {:?}", pipeline_id);
self.remove_pipeline_root_layer(pipeline_id);
let _ = sender.send(());
}
},
(Msg::NewScrollFrameReady(recomposite_needed), ShutdownState::NotShuttingDown) => {
self.waiting_for_results_of_scroll = false;
if recomposite_needed {
self.composition_request = CompositionRequest::CompositeNow(
CompositingReason::NewWebRenderScrollFrame);
CompositingReason::NewWebRenderScrollFrame,
);
}
}
},
(Msg::Dispatch(func), ShutdownState::NotShuttingDown) => {
// The functions sent here right now are really dumb, so they can't panic.
// But if we start running more complex code here, we should really catch panic here.
func();
}
},
(Msg::LoadComplete(_), ShutdownState::NotShuttingDown) => {
// If we're painting in headless mode, schedule a recomposite.
@ -479,30 +490,30 @@ impl<Window: WindowMethods> IOCompositor<Window> {
(Msg::PendingPaintMetric(pipeline_id, epoch), _) => {
self.pending_paint_metrics.insert(pipeline_id, epoch);
}
},
(Msg::GetClientWindow(req), ShutdownState::NotShuttingDown) => {
if let Err(e) = req.send(self.embedder_coordinates.window) {
warn!("Sending response to get client window failed ({}).", e);
}
}
},
(Msg::GetScreenSize(req), ShutdownState::NotShuttingDown) => {
if let Err(e) = req.send(self.embedder_coordinates.screen) {
warn!("Sending response to get screen size failed ({}).", e);
}
}
},
(Msg::GetScreenAvailSize(req), ShutdownState::NotShuttingDown) => {
if let Err(e) = req.send(self.embedder_coordinates.screen_avail) {
warn!("Sending response to get screen avail size failed ({}).", e);
}
}
},
// When we are shutting_down, we need to avoid performing operations
// such as Paint that may crash because we have begun tearing down
// the rest of our resources.
(_, ShutdownState::ShuttingDown) => {}
(_, ShutdownState::ShuttingDown) => {},
}
true
@ -522,42 +533,53 @@ impl<Window: WindowMethods> IOCompositor<Window> {
if visible {
self.composite_if_necessary(CompositingReason::Animation);
}
}
},
AnimationState::AnimationCallbacksPresent => {
let visible = self.pipeline_details(pipeline_id).visible;
self.pipeline_details(pipeline_id).animation_callbacks_running = true;
self.pipeline_details(pipeline_id)
.animation_callbacks_running = true;
if visible {
self.tick_animations_for_pipeline(pipeline_id);
}
}
},
AnimationState::NoAnimationsPresent => {
self.pipeline_details(pipeline_id).animations_running = false;
}
},
AnimationState::NoAnimationCallbacksPresent => {
self.pipeline_details(pipeline_id).animation_callbacks_running = false;
}
self.pipeline_details(pipeline_id)
.animation_callbacks_running = false;
},
}
}
fn pipeline_details(&mut self, pipeline_id: PipelineId) -> &mut PipelineDetails {
if !self.pipeline_details.contains_key(&pipeline_id) {
self.pipeline_details.insert(pipeline_id, PipelineDetails::new());
self.pipeline_details
.insert(pipeline_id, PipelineDetails::new());
}
self.pipeline_details.get_mut(&pipeline_id).expect("Insert then get failed!")
self.pipeline_details
.get_mut(&pipeline_id)
.expect("Insert then get failed!")
}
pub fn pipeline(&self, pipeline_id: PipelineId) -> Option<&CompositionPipeline> {
match self.pipeline_details.get(&pipeline_id) {
Some(ref details) => details.pipeline.as_ref(),
None => {
warn!("Compositor layer has an unknown pipeline ({:?}).", pipeline_id);
warn!(
"Compositor layer has an unknown pipeline ({:?}).",
pipeline_id
);
None
}
},
}
}
fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree) {
debug!("Setting the frame tree for pipeline {}", frame_tree.pipeline.id);
debug!(
"Setting the frame tree for pipeline {}",
frame_tree.pipeline.id
);
self.root_pipeline = Some(frame_tree.pipeline.clone());
@ -565,7 +587,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
let mut txn = webrender_api::Transaction::new();
txn.set_root_pipeline(pipeline_id);
txn.generate_frame();
self.webrender_api.send_transaction(self.webrender_document, txn);
self.webrender_api
.send_transaction(self.webrender_document, txn);
self.create_pipeline_details_for_frame_tree(&frame_tree);
@ -589,10 +612,12 @@ impl<Window: WindowMethods> IOCompositor<Window> {
fn send_window_size(&self, size_type: WindowSizeType) {
let dppx = self.page_zoom * self.embedder_coordinates.hidpi_factor;
self.webrender_api.set_window_parameters(self.webrender_document,
self.embedder_coordinates.framebuffer,
self.embedder_coordinates.viewport,
self.embedder_coordinates.hidpi_factor.get());
self.webrender_api.set_window_parameters(
self.webrender_document,
self.embedder_coordinates.framebuffer,
self.embedder_coordinates.viewport,
self.embedder_coordinates.hidpi_factor.get(),
);
let initial_viewport = self.embedder_coordinates.viewport.size.to_f32() / dppx;
@ -601,9 +626,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
initial_viewport: initial_viewport,
};
let top_level_browsing_context_id = self.root_pipeline.as_ref().map(|pipeline| {
pipeline.top_level_browsing_context_id
});
let top_level_browsing_context_id = self
.root_pipeline
.as_ref()
.map(|pipeline| pipeline.top_level_browsing_context_id);
let msg = ConstellationMsg::WindowSize(top_level_browsing_context_id, data, size_type);
@ -624,7 +650,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
if self.embedder_coordinates.viewport == old_coords.viewport &&
self.embedder_coordinates.framebuffer == old_coords.framebuffer {
self.embedder_coordinates.framebuffer == old_coords.framebuffer
{
return;
}
@ -634,11 +661,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
pub fn on_mouse_window_event_class(&mut self, mouse_window_event: MouseWindowEvent) {
if opts::get().convert_mouse_to_touch {
match mouse_window_event {
MouseWindowEvent::Click(_, _) => {}
MouseWindowEvent::Click(_, _) => {},
MouseWindowEvent::MouseDown(_, p) => self.on_touch_down(TouchId(0), p),
MouseWindowEvent::MouseUp(_, p) => self.on_touch_up(TouchId(0), p),
}
return
return;
}
self.dispatch_mouse_window_event_class(mouse_window_event);
@ -687,15 +714,14 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.webrender_document,
None,
world_cursor,
HitTestFlags::empty()
HitTestFlags::empty(),
)
}
pub fn on_mouse_window_move_event_class(&mut self, cursor: DevicePoint) {
if opts::get().convert_mouse_to_touch {
self.on_touch_move(TouchId(0), cursor);
return
return;
}
self.dispatch_mouse_window_move_event_class(cursor);
@ -720,7 +746,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
warn!("Sending event to constellation failed ({}).", e);
}
if let Some(cursor) = CursorKind::from_u8(item.tag.1 as _).ok() {
if let Some(cursor) = CursorKind::from_u8(item.tag.1 as _).ok() {
let msg = ConstellationMsg::SetCursor(cursor);
if let Err(e) = self.constellation_chan.send(msg) {
warn!("Sending event to constellation failed ({}).", e);
@ -733,8 +759,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
&self,
event_type: TouchEventType,
identifier: TouchId,
point: DevicePoint)
{
point: DevicePoint,
) {
let results = self.hit_test_at_point(point);
if let Some(item) = results.items.first() {
let event = TouchEvent(
@ -751,10 +777,12 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
}
pub fn on_touch_event(&mut self,
event_type: TouchEventType,
identifier: TouchId,
location: DevicePoint) {
pub fn on_touch_event(
&mut self,
event_type: TouchEventType,
identifier: TouchId,
location: DevicePoint,
) {
match event_type {
TouchEventType::Down => self.on_touch_down(identifier, location),
TouchEventType::Move => self.on_touch_move(identifier, location),
@ -770,28 +798,25 @@ impl<Window: WindowMethods> IOCompositor<Window> {
fn on_touch_move(&mut self, identifier: TouchId, point: DevicePoint) {
match self.touch_handler.on_touch_move(identifier, point) {
TouchAction::Scroll(delta) => {
self.on_scroll_window_event(
ScrollLocation::Delta(
LayoutVector2D::from_untyped(&delta.to_untyped())
),
point.cast()
)
}
TouchAction::Scroll(delta) => self.on_scroll_window_event(
ScrollLocation::Delta(LayoutVector2D::from_untyped(&delta.to_untyped())),
point.cast(),
),
TouchAction::Zoom(magnification, scroll_delta) => {
let cursor = TypedPoint2D::new(-1, -1); // Make sure this hits the base layer.
let cursor = TypedPoint2D::new(-1, -1); // Make sure this hits the base layer.
self.pending_scroll_zoom_events.push(ScrollZoomEvent {
magnification: magnification,
scroll_location: ScrollLocation::Delta(webrender_api::LayoutVector2D::from_untyped(
&scroll_delta.to_untyped())),
scroll_location: ScrollLocation::Delta(
webrender_api::LayoutVector2D::from_untyped(&scroll_delta.to_untyped()),
),
cursor: cursor,
event_count: 1,
});
}
},
TouchAction::DispatchEvent => {
self.send_touch_event(TouchEventType::Move, identifier, point);
}
_ => {}
},
_ => {},
}
}
@ -818,24 +843,24 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.dispatch_mouse_window_event_class(MouseWindowEvent::Click(button, p));
}
pub fn on_scroll_event(&mut self,
delta: ScrollLocation,
cursor: DeviceIntPoint,
phase: TouchEventType) {
pub fn on_scroll_event(
&mut self,
delta: ScrollLocation,
cursor: DeviceIntPoint,
phase: TouchEventType,
) {
match phase {
TouchEventType::Move => self.on_scroll_window_event(delta, cursor),
TouchEventType::Up | TouchEventType::Cancel => {
self.on_scroll_end_window_event(delta, cursor);
}
},
TouchEventType::Down => {
self.on_scroll_start_window_event(delta, cursor);
}
},
}
}
fn on_scroll_window_event(&mut self,
scroll_location: ScrollLocation,
cursor: DeviceIntPoint) {
fn on_scroll_window_event(&mut self, scroll_location: ScrollLocation, cursor: DeviceIntPoint) {
self.in_scroll_transaction = Some(Instant::now());
self.pending_scroll_zoom_events.push(ScrollZoomEvent {
magnification: 1.0,
@ -845,9 +870,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
});
}
fn on_scroll_start_window_event(&mut self,
scroll_location: ScrollLocation,
cursor: DeviceIntPoint) {
fn on_scroll_start_window_event(
&mut self,
scroll_location: ScrollLocation,
cursor: DeviceIntPoint,
) {
self.scroll_in_progress = true;
self.pending_scroll_zoom_events.push(ScrollZoomEvent {
magnification: 1.0,
@ -857,9 +884,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
});
}
fn on_scroll_end_window_event(&mut self,
scroll_location: ScrollLocation,
cursor: DeviceIntPoint) {
fn on_scroll_end_window_event(
&mut self,
scroll_location: ScrollLocation,
cursor: DeviceIntPoint,
) {
self.scroll_in_progress = false;
self.pending_scroll_zoom_events.push(ScrollZoomEvent {
magnification: 1.0,
@ -884,19 +913,20 @@ impl<Window: WindowMethods> IOCompositor<Window> {
// disregard other pending events and exit the loop.
last_combined_event = Some(scroll_event);
break;
}
},
};
match &mut last_combined_event {
last_combined_event @ &mut None => {
*last_combined_event = Some(ScrollZoomEvent {
magnification: scroll_event.magnification,
scroll_location: ScrollLocation::Delta(webrender_api::LayoutVector2D::from_untyped(
&this_delta.to_untyped())),
scroll_location: ScrollLocation::Delta(
webrender_api::LayoutVector2D::from_untyped(&this_delta.to_untyped()),
),
cursor: this_cursor,
event_count: 1,
})
}
},
&mut Some(ref mut last_combined_event) => {
// Mac OS X sometimes delivers scroll events out of vsync during a
// fling. This causes events to get bunched up occasionally, causing
@ -909,21 +939,23 @@ impl<Window: WindowMethods> IOCompositor<Window> {
let new_event_count =
TypedScale::new(last_combined_event.event_count as f32);
last_combined_event.scroll_location = ScrollLocation::Delta(
(delta * old_event_count + this_delta) /
new_event_count);
(delta * old_event_count + this_delta) / new_event_count,
);
}
last_combined_event.magnification *= scroll_event.magnification;
}
},
}
}
if let Some(combined_event) = last_combined_event {
let scroll_location = match combined_event.scroll_location {
ScrollLocation::Delta(delta) => {
let scaled_delta = (TypedVector2D::from_untyped(&delta.to_untyped()) / self.scale)
.to_untyped();
let calculated_delta = webrender_api::LayoutVector2D::from_untyped(&scaled_delta);
ScrollLocation::Delta(calculated_delta)
let scaled_delta = (TypedVector2D::from_untyped(&delta.to_untyped()) /
self.scale)
.to_untyped();
let calculated_delta =
webrender_api::LayoutVector2D::from_untyped(&scaled_delta);
ScrollLocation::Delta(calculated_delta)
},
// Leave ScrollLocation unchanged if it is Start or End location.
sl @ ScrollLocation::Start | sl @ ScrollLocation::End => sl,
@ -938,7 +970,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
txn.set_pinch_zoom(webrender_api::ZoomFactor::new(self.pinch_zoom_level()));
}
txn.generate_frame();
self.webrender_api.send_transaction(self.webrender_document, txn);
self.webrender_api
.send_transaction(self.webrender_document, txn);
self.waiting_for_results_of_scroll = true
}
@ -951,10 +984,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
fn process_animations(&mut self) {
let mut pipeline_ids = vec![];
for (pipeline_id, pipeline_details) in &self.pipeline_details {
if (pipeline_details.animations_running ||
pipeline_details.animation_callbacks_running) &&
pipeline_details.visible {
pipeline_ids.push(*pipeline_id);
if (pipeline_details.animations_running || pipeline_details.animation_callbacks_running) &&
pipeline_details.visible
{
pipeline_ids.push(*pipeline_id);
}
}
let animation_state = if pipeline_ids.is_empty() {
@ -969,7 +1002,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
fn tick_animations_for_pipeline(&mut self, pipeline_id: PipelineId) {
let animation_callbacks_running = self.pipeline_details(pipeline_id).animation_callbacks_running;
let animation_callbacks_running = self
.pipeline_details(pipeline_id)
.animation_callbacks_running;
if animation_callbacks_running {
let msg = ConstellationMsg::TickAnimation(pipeline_id, AnimationTickType::Script);
if let Err(e) = self.constellation_chan.send(msg) {
@ -988,9 +1023,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
fn constrain_viewport(&mut self, pipeline_id: PipelineId, constraints: ViewportConstraints) {
let is_root = self.root_pipeline.as_ref().map_or(false, |root_pipeline| {
root_pipeline.id == pipeline_id
});
let is_root = self
.root_pipeline
.as_ref()
.map_or(false, |root_pipeline| root_pipeline.id == pipeline_id);
if is_root {
self.viewport_zoom = constraints.initial_zoom;
@ -1006,7 +1042,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
None => match opts::get().output_file {
Some(_) => TypedScale::new(1.0),
None => self.embedder_coordinates.hidpi_factor,
}
},
}
}
@ -1027,8 +1063,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
pub fn on_zoom_window_event(&mut self, magnification: f32) {
self.page_zoom = TypedScale::new((self.page_zoom.get() * magnification)
.max(MIN_ZOOM).min(MAX_ZOOM));
self.page_zoom = TypedScale::new(
(self.page_zoom.get() * magnification)
.max(MIN_ZOOM)
.min(MAX_ZOOM),
);
self.update_zoom_transform();
self.send_window_size(WindowSizeType::Resize);
self.update_page_zoom_for_webrender();
@ -1039,7 +1078,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
let mut txn = webrender_api::Transaction::new();
txn.set_page_zoom(page_zoom);
self.webrender_api.send_transaction(self.webrender_document, txn);
self.webrender_api
.send_transaction(self.webrender_document, txn);
}
/// Simulate a pinch zoom
@ -1047,14 +1087,17 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.pending_scroll_zoom_events.push(ScrollZoomEvent {
magnification: magnification,
scroll_location: ScrollLocation::Delta(TypedVector2D::zero()), // TODO: Scroll to keep the center in view?
cursor: TypedPoint2D::new(-1, -1), // Make sure this hits the base layer.
cursor: TypedPoint2D::new(-1, -1), // Make sure this hits the base layer.
event_count: 1,
});
}
fn send_viewport_rects(&self) {
let mut scroll_states_per_pipeline = HashMap::new();
for scroll_layer_state in self.webrender_api.get_scroll_node_state(self.webrender_document) {
for scroll_layer_state in self
.webrender_api
.get_scroll_node_state(self.webrender_document)
{
let scroll_state = ScrollState {
scroll_id: scroll_layer_state.id,
scroll_offset: scroll_layer_state.scroll_offset.to_untyped(),
@ -1105,8 +1148,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
let mut pipeline_epochs = HashMap::new();
for (id, _) in &self.pipeline_details {
let webrender_pipeline_id = id.to_webrender();
if let Some(webrender_api::Epoch(epoch)) = self.webrender
.current_epoch(webrender_pipeline_id) {
if let Some(webrender_api::Epoch(epoch)) =
self.webrender.current_epoch(webrender_pipeline_id)
{
let epoch = Epoch(epoch);
pipeline_epochs.insert(*id, epoch);
}
@ -1120,12 +1164,12 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
self.ready_to_save_state = ReadyState::WaitingForConstellationReply;
Err(NotReadyToPaint::JustNotifiedConstellation)
}
},
ReadyState::WaitingForConstellationReply => {
// If waiting on a reply from the constellation to the last
// query if the image is stable, then assume not ready yet.
Err(NotReadyToPaint::WaitingOnConstellation)
}
},
ReadyState::ReadyToSaveImage => {
// Constellation has replied at some point in the past
// that the current output image is stable and ready
@ -1137,7 +1181,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
self.ready_to_save_state = ReadyState::Unknown;
Ok(())
}
},
}
}
@ -1149,7 +1193,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.start_shutting_down();
},
Err(e) => if opts::get().is_running_problem_test {
if e != UnableToComposite::NotReadyToPaintImage(NotReadyToPaint::WaitingOnConstellation) {
if e != UnableToComposite::NotReadyToPaintImage(
NotReadyToPaint::WaitingOnConstellation,
) {
println!("not ready to composite: {:?}", e);
}
},
@ -1161,13 +1207,14 @@ impl<Window: WindowMethods> IOCompositor<Window> {
/// for some reason. If CompositeTarget is Window or Png no image data is returned;
/// in the latter case the image is written directly to a file. If CompositeTarget
/// is WindowAndPng Ok(Some(png::Image)) is returned.
fn composite_specific_target(&mut self,
target: CompositeTarget)
-> Result<Option<Image>, UnableToComposite> {
fn composite_specific_target(
&mut self,
target: CompositeTarget,
) -> Result<Option<Image>, UnableToComposite> {
let width = self.embedder_coordinates.framebuffer.width_typed();
let height = self.embedder_coordinates.framebuffer.height_typed();
if !self.window.prepare_for_composite(width, height) {
return Err(UnableToComposite::WindowUnprepared)
return Err(UnableToComposite::WindowUnprepared);
}
self.webrender.update();
@ -1183,34 +1230,40 @@ impl<Window: WindowMethods> IOCompositor<Window> {
// all active animations to complete.
if self.animations_active() {
self.process_animations();
return Err(UnableToComposite::NotReadyToPaintImage(NotReadyToPaint::AnimationsActive));
return Err(UnableToComposite::NotReadyToPaintImage(
NotReadyToPaint::AnimationsActive,
));
}
if let Err(result) = self.is_ready_to_paint_image_output() {
return Err(UnableToComposite::NotReadyToPaintImage(result))
return Err(UnableToComposite::NotReadyToPaintImage(result));
}
}
let rt_info = match target {
#[cfg(feature = "gleam")]
CompositeTarget::Window => {
gl::RenderTargetInfo::default()
}
CompositeTarget::Window => gl::RenderTargetInfo::default(),
#[cfg(feature = "gleam")]
CompositeTarget::WindowAndPng |
CompositeTarget::PngFile => {
CompositeTarget::WindowAndPng | CompositeTarget::PngFile => {
gl::initialize_png(&*self.window.gl(), width, height)
}
},
#[cfg(not(feature = "gleam"))]
_ => ()
_ => (),
};
profile(ProfilerCategory::Compositing, None, self.time_profiler_chan.clone(), || {
debug!("compositor: compositing");
profile(
ProfilerCategory::Compositing,
None,
self.time_profiler_chan.clone(),
|| {
debug!("compositor: compositing");
// Paint the scene.
// TODO(gw): Take notice of any errors the renderer returns!
self.webrender.render(self.embedder_coordinates.framebuffer).ok();
});
// Paint the scene.
// TODO(gw): Take notice of any errors the renderer returns!
self.webrender
.render(self.embedder_coordinates.framebuffer)
.ok();
},
);
// If there are pending paint metrics, we check if any of the painted epochs is
// one of the ones that the paint metrics recorder is expecting . In that case,
@ -1222,7 +1275,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
// For each pending paint metrics pipeline id
for (id, pending_epoch) in &self.pending_paint_metrics {
// we get the last painted frame id from webrender
if let Some(webrender_api::Epoch(epoch)) = self.webrender.current_epoch(id.to_webrender()) {
if let Some(webrender_api::Epoch(epoch)) =
self.webrender.current_epoch(id.to_webrender())
{
// and check if it is the one the layout thread is expecting,
let epoch = Epoch(epoch);
if *pending_epoch != epoch {
@ -1233,7 +1288,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
if let Some(pipeline) = self.pipeline(*id) {
// and inform the layout thread with the measured paint time.
let msg = LayoutControlMsg::PaintMetric(epoch, paint_time);
if let Err(e) = pipeline.layout_chan.send(msg) {
if let Err(e) = pipeline.layout_chan.send(msg) {
warn!("Sending PaintMetric message to layout failed ({}).", e);
}
}
@ -1256,27 +1311,31 @@ impl<Window: WindowMethods> IOCompositor<Window> {
bytes: ipc::IpcSharedMemory::from_bytes(&*img),
id: None,
})
}
},
#[cfg(feature = "gleam")]
CompositeTarget::PngFile => {
let gl = &*self.window.gl();
profile(ProfilerCategory::ImageSaving, None, self.time_profiler_chan.clone(), || {
match opts::get().output_file.as_ref() {
profile(
ProfilerCategory::ImageSaving,
None,
self.time_profiler_chan.clone(),
|| match opts::get().output_file.as_ref() {
Some(path) => match File::create(path) {
Ok(mut file) => {
let img = gl::draw_img(gl, rt_info, width, height);
let dynamic_image = DynamicImage::ImageRgb8(img);
if let Err(e) = dynamic_image.write_to(&mut file, ImageFormat::PNG) {
if let Err(e) = dynamic_image.write_to(&mut file, ImageFormat::PNG)
{
error!("Failed to save {} ({}).", path, e);
}
},
Err(e) => error!("Failed to create {} ({}).", path, e),
},
None => error!("No file specified."),
}
});
},
);
None
}
},
#[cfg(not(feature = "gleam"))]
_ => None,
};
@ -1301,7 +1360,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
self.composition_request = CompositionRequest::CompositeNow(reason)
} else if opts::get().is_running_problem_test {
println!("composition_request is already {:?}", self.composition_request);
println!(
"composition_request is already {:?}",
self.composition_request
);
}
}
@ -1315,17 +1377,17 @@ impl<Window: WindowMethods> IOCompositor<Window> {
let mut found_recomposite_msg = false;
while let Some(msg) = self.port.try_recv_compositor_msg() {
match msg {
Msg::Recomposite(_) if found_recomposite_msg => {}
Msg::Recomposite(_) if found_recomposite_msg => {},
Msg::Recomposite(_) => {
found_recomposite_msg = true;
compositor_messages.push(msg)
}
},
_ => compositor_messages.push(msg),
}
}
for msg in compositor_messages {
if !self.handle_browser_message(msg) {
return false
return false;
}
}
true
@ -1342,10 +1404,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
match self.composition_request {
CompositionRequest::NoCompositingNecessary => {}
CompositionRequest::CompositeNow(_) => {
self.composite()
}
CompositionRequest::NoCompositingNecessary => {},
CompositionRequest::CompositeNow(_) => self.composite(),
}
if !self.pending_scroll_zoom_events.is_empty() && !self.waiting_for_results_of_scroll {
@ -1368,10 +1428,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
let keep_going = self.handle_browser_message(msg);
if need_recomposite {
self.composite();
break
break;
}
if !keep_going {
break
break;
}
}
}
@ -1395,44 +1455,47 @@ impl<Window: WindowMethods> IOCompositor<Window> {
let flag = match option {
WebRenderDebugOption::Profiler => {
webrender::DebugFlags::PROFILER_DBG |
webrender::DebugFlags::GPU_TIME_QUERIES |
webrender::DebugFlags::GPU_SAMPLE_QUERIES
}
WebRenderDebugOption::TextureCacheDebug => {
webrender::DebugFlags::TEXTURE_CACHE_DBG
}
WebRenderDebugOption::RenderTargetDebug => {
webrender::DebugFlags::RENDER_TARGET_DBG
}
webrender::DebugFlags::GPU_TIME_QUERIES |
webrender::DebugFlags::GPU_SAMPLE_QUERIES
},
WebRenderDebugOption::TextureCacheDebug => webrender::DebugFlags::TEXTURE_CACHE_DBG,
WebRenderDebugOption::RenderTargetDebug => webrender::DebugFlags::RENDER_TARGET_DBG,
};
flags.toggle(flag);
self.webrender.set_debug_flags(flags);
let mut txn = webrender_api::Transaction::new();
txn.generate_frame();
self.webrender_api.send_transaction(self.webrender_document, txn);
self.webrender_api
.send_transaction(self.webrender_document, txn);
}
pub fn capture_webrender(&mut self) {
let capture_id = now().to_timespec().sec.to_string();
let available_path = [env::current_dir(), Ok(env::temp_dir())].iter()
.filter_map(|val| val.as_ref().map(|dir| dir.join("capture_webrender").join(&capture_id)).ok())
.find(|val| {
match create_dir_all(&val) {
Ok(_) => true,
Err(err) => {
eprintln!("Unable to create path '{:?}' for capture: {:?}", &val, err);
false
}
}
let available_path = [env::current_dir(), Ok(env::temp_dir())]
.iter()
.filter_map(|val| {
val.as_ref()
.map(|dir| dir.join("capture_webrender").join(&capture_id))
.ok()
}).find(|val| match create_dir_all(&val) {
Ok(_) => true,
Err(err) => {
eprintln!("Unable to create path '{:?}' for capture: {:?}", &val, err);
false
},
});
match available_path {
Some(capture_path) => {
let revision_file_path = capture_path.join("wr.txt");
debug!("Trying to save webrender capture under {:?}", &revision_file_path);
self.webrender_api.save_capture(capture_path, webrender_api::CaptureBits::all());
debug!(
"Trying to save webrender capture under {:?}",
&revision_file_path
);
self.webrender_api
.save_capture(capture_path, webrender_api::CaptureBits::all());
match File::create(revision_file_path) {
Ok(mut file) => {
@ -1440,11 +1503,14 @@ impl<Window: WindowMethods> IOCompositor<Window> {
if let Err(err) = write!(&mut file, "{}", revision) {
eprintln!("Unable to write webrender revision: {:?}", err)
}
}
Err(err) => eprintln!("Capture triggered, creating webrender revision info skipped: {:?}", err)
},
Err(err) => eprintln!(
"Capture triggered, creating webrender revision info skipped: {:?}",
err
),
}
},
None => eprintln!("Unable to locate path to save captures")
None => eprintln!("Unable to locate path to save captures"),
}
}
}

View file

@ -20,7 +20,6 @@ use style_traits::viewport::ViewportConstraints;
use webrender;
use webrender_api::{self, DeviceIntPoint, DeviceUintSize};
/// Sends messages to the compositor.
pub struct CompositorProxy {
pub sender: Sender<Msg>,
@ -48,7 +47,7 @@ impl Clone for CompositorProxy {
/// The port that the compositor receives messages on.
pub struct CompositorReceiver {
pub receiver: Receiver<Msg>
pub receiver: Receiver<Msg>,
}
impl CompositorReceiver {

View file

@ -2,12 +2,10 @@
* 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 gleam::gl;
use image::RgbImage;
use servo_geometry::DeviceUintLength;
#[derive(Default)]
pub struct RenderTargetInfo {
framebuffer_ids: Vec<gl::GLuint>,
@ -16,7 +14,9 @@ pub struct RenderTargetInfo {
}
pub fn initialize_png(
gl: &gl::Gl, width: DeviceUintLength, height: DeviceUintLength
gl: &gl::Gl,
width: DeviceUintLength,
height: DeviceUintLength,
) -> RenderTargetInfo {
let framebuffer_ids = gl.gen_framebuffers(1);
gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_ids[0]);
@ -24,27 +24,53 @@ pub fn initialize_png(
let texture_ids = gl.gen_textures(1);
gl.bind_texture(gl::TEXTURE_2D, texture_ids[0]);
gl.tex_image_2d(gl::TEXTURE_2D, 0, gl::RGB as gl::GLint, width.get() as gl::GLsizei,
height.get() as gl::GLsizei, 0, gl::RGB, gl::UNSIGNED_BYTE, None);
gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as gl::GLint);
gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as gl::GLint);
gl.tex_image_2d(
gl::TEXTURE_2D,
0,
gl::RGB as gl::GLint,
width.get() as gl::GLsizei,
height.get() as gl::GLsizei,
0,
gl::RGB,
gl::UNSIGNED_BYTE,
None,
);
gl.tex_parameter_i(
gl::TEXTURE_2D,
gl::TEXTURE_MAG_FILTER,
gl::NEAREST as gl::GLint,
);
gl.tex_parameter_i(
gl::TEXTURE_2D,
gl::TEXTURE_MIN_FILTER,
gl::NEAREST as gl::GLint,
);
gl.framebuffer_texture_2d(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D,
texture_ids[0], 0);
gl.framebuffer_texture_2d(
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
texture_ids[0],
0,
);
gl.bind_texture(gl::TEXTURE_2D, 0);
let renderbuffer_ids = gl.gen_renderbuffers(1);
let depth_rb = renderbuffer_ids[0];
gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
gl.renderbuffer_storage(gl::RENDERBUFFER,
gl::DEPTH_COMPONENT24,
width.get() as gl::GLsizei,
height.get() as gl::GLsizei);
gl.framebuffer_renderbuffer(gl::FRAMEBUFFER,
gl::DEPTH_ATTACHMENT,
gl::RENDERBUFFER,
depth_rb);
gl.renderbuffer_storage(
gl::RENDERBUFFER,
gl::DEPTH_COMPONENT24,
width.get() as gl::GLsizei,
height.get() as gl::GLsizei,
);
gl.framebuffer_renderbuffer(
gl::FRAMEBUFFER,
gl::DEPTH_ATTACHMENT,
gl::RENDERBUFFER,
depth_rb,
);
RenderTargetInfo {
framebuffer_ids,
@ -70,10 +96,12 @@ pub fn draw_img(
gl.bind_vertex_array(0);
let mut pixels = gl.read_pixels(
0, 0,
0,
0,
width as gl::GLsizei,
height as gl::GLsizei,
gl::RGB, gl::UNSIGNED_BYTE,
gl::RGB,
gl::UNSIGNED_BYTE,
);
gl.bind_framebuffer(gl::FRAMEBUFFER, 0);
@ -88,10 +116,9 @@ pub fn draw_img(
for y in 0..height {
let dst_start = y * stride;
let src_start = (height - y - 1) * stride;
let src_slice = &orig_pixels[src_start .. src_start + stride];
(&mut pixels[dst_start .. dst_start + stride]).clone_from_slice(&src_slice[..stride]);
let src_slice = &orig_pixels[src_start..src_start + stride];
(&mut pixels[dst_start..dst_start + stride]).clone_from_slice(&src_slice[..stride]);
}
RgbImage::from_raw(width as u32, height as u32, pixels)
.expect("Flipping image failed!")
RgbImage::from_raw(width as u32, height as u32, pixels).expect("Flipping image failed!")
}

View file

@ -19,12 +19,15 @@ pub struct TouchHandler {
#[derive(Clone, Copy, Debug)]
pub struct TouchPoint {
pub id: TouchId,
pub point: TypedPoint2D<f32, DevicePixel>
pub point: TypedPoint2D<f32, DevicePixel>,
}
impl TouchPoint {
pub fn new(id: TouchId, point: TypedPoint2D<f32, DevicePixel>) -> Self {
TouchPoint { id: id, point: point }
TouchPoint {
id: id,
point: point,
}
}
}
@ -79,22 +82,25 @@ impl TouchHandler {
self.active_touch_points.push(point);
self.state = match self.state {
Nothing => WaitingForScript,
Touching | Panning => Pinching,
WaitingForScript => WaitingForScript,
DefaultPrevented => DefaultPrevented,
Nothing => WaitingForScript,
Touching | Panning => Pinching,
WaitingForScript => WaitingForScript,
DefaultPrevented => DefaultPrevented,
Pinching | MultiTouch => MultiTouch,
};
}
pub fn on_touch_move(&mut self, id: TouchId, point: TypedPoint2D<f32, DevicePixel>)
-> TouchAction {
pub fn on_touch_move(
&mut self,
id: TouchId,
point: TypedPoint2D<f32, DevicePixel>,
) -> TouchAction {
let idx = match self.active_touch_points.iter_mut().position(|t| t.id == id) {
Some(i) => i,
None => {
warn!("Got a touchmove event for a non-active touch point");
return TouchAction::NoAction;
}
},
};
let old_point = self.active_touch_points[idx].point;
@ -103,21 +109,19 @@ impl TouchHandler {
let delta = point - old_point;
if delta.x.abs() > TOUCH_PAN_MIN_SCREEN_PX ||
delta.y.abs() > TOUCH_PAN_MIN_SCREEN_PX
delta.y.abs() > TOUCH_PAN_MIN_SCREEN_PX
{
self.state = Panning;
TouchAction::Scroll(delta)
} else {
TouchAction::NoAction
}
}
},
Panning => {
let delta = point - old_point;
TouchAction::Scroll(delta)
}
DefaultPrevented => {
TouchAction::DispatchEvent
}
},
DefaultPrevented => TouchAction::DispatchEvent,
Pinching => {
let (d0, c0) = self.pinch_distance_and_center();
self.active_touch_points[idx].point = point;
@ -127,7 +131,7 @@ impl TouchHandler {
let scroll_delta = c1 - c0 * TypedScale::new(magnification);
TouchAction::Zoom(magnification, scroll_delta)
}
},
WaitingForScript => TouchAction::NoAction,
MultiTouch => TouchAction::NoAction,
Nothing => unreachable!(),
@ -141,15 +145,18 @@ impl TouchHandler {
action
}
pub fn on_touch_up(&mut self, id: TouchId, _point: TypedPoint2D<f32, DevicePixel>)
-> TouchAction {
pub fn on_touch_up(
&mut self,
id: TouchId,
_point: TypedPoint2D<f32, DevicePixel>,
) -> TouchAction {
match self.active_touch_points.iter().position(|t| t.id == id) {
Some(i) => {
self.active_touch_points.swap_remove(i);
}
},
None => {
warn!("Got a touch up event for a non-active touch point");
}
},
}
match self.state {
Touching => {
@ -157,21 +164,21 @@ impl TouchHandler {
// FIXME: Don't send a click if preventDefault is called on the touchend event.
self.state = Nothing;
TouchAction::Click
}
},
Nothing | Panning => {
self.state = Nothing;
TouchAction::NoAction
}
},
Pinching => {
self.state = Panning;
TouchAction::NoAction
}
},
WaitingForScript | DefaultPrevented | MultiTouch => {
if self.active_touch_points.is_empty() {
self.state = Nothing;
}
TouchAction::NoAction
}
},
}
}
@ -179,25 +186,25 @@ impl TouchHandler {
match self.active_touch_points.iter().position(|t| t.id == id) {
Some(i) => {
self.active_touch_points.swap_remove(i);
}
},
None => {
warn!("Got a touchcancel event for a non-active touch point");
return;
}
},
}
match self.state {
Nothing => {}
Nothing => {},
Touching | Panning => {
self.state = Nothing;
}
},
Pinching => {
self.state = Panning;
}
},
WaitingForScript | DefaultPrevented | MultiTouch => {
if self.active_touch_points.is_empty() {
self.state = Nothing;
}
}
},
}
}
@ -209,7 +216,7 @@ impl TouchHandler {
1 => Touching,
2 => Pinching,
_ => MultiTouch,
}
},
}
}
}

View file

@ -8,7 +8,12 @@
use std::path::PathBuf;
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))]
#[cfg(all(
unix,
not(target_os = "macos"),
not(target_os = "ios"),
not(target_os = "android")
))]
pub fn default_config_dir() -> Option<PathBuf> {
let mut config_dir = ::dirs::config_dir().unwrap();
config_dir.push("servo");

View file

@ -9,17 +9,21 @@ extern crate dirs;
extern crate embedder_traits;
extern crate euclid;
extern crate getopts;
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate log;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate num_cpus;
extern crate rustc_serialize;
#[macro_use] extern crate serde;
#[macro_use]
extern crate serde;
extern crate servo_geometry;
extern crate servo_url;
extern crate url;
pub mod basedir;
#[allow(unsafe_code)] pub mod opts;
#[allow(unsafe_code)]
pub mod opts;
pub mod prefs;
pub fn servo_version() -> String {

View file

@ -22,7 +22,6 @@ use std::process;
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
use url::{self, Url};
/// Global flags for Servo, currently set on the command line.
#[derive(Clone, Deserialize, Serialize)]
pub struct Opts {
@ -230,11 +229,13 @@ pub struct Opts {
}
fn print_usage(app: &str, opts: &Options) {
let message = format!("Usage: {} [ options ... ] [URL]\n\twhere options include", app);
let message = format!(
"Usage: {} [ options ... ] [URL]\n\twhere options include",
app
);
println!("{}", opts.usage(&message));
}
/// Debug options for Servo, currently set on the command line with -Z
#[derive(Default)]
pub struct DebugOptions {
@ -333,7 +334,6 @@ pub struct DebugOptions {
pub signpost: bool,
}
impl DebugOptions {
pub fn extend(&mut self, debug_string: String) -> Result<(), String> {
for option in debug_string.split(',') {
@ -371,50 +371,103 @@ impl DebugOptions {
"" => {},
_ => return Err(String::from(option)),
};
};
}
Ok(())
}
}
fn print_debug_usage(app: &str) -> ! {
fn print_option(name: &str, description: &str) {
println!("\t{:<35} {}", name, description);
}
println!("Usage: {} debug option,[options,...]\n\twhere options include\n\nOptions:", app);
println!(
"Usage: {} debug option,[options,...]\n\twhere options include\n\nOptions:",
app
);
print_option("bubble-widths", "Bubble intrinsic widths separately like other engines.");
print_option(
"bubble-widths",
"Bubble intrinsic widths separately like other engines.",
);
print_option("disable-text-aa", "Disable antialiasing of rendered text.");
print_option("disable-canvas-aa", "Disable antialiasing on the HTML canvas element.");
print_option("dump-style-tree", "Print the DOM with computed styles after each restyle.");
print_option(
"disable-canvas-aa",
"Disable antialiasing on the HTML canvas element.",
);
print_option(
"dump-style-tree",
"Print the DOM with computed styles after each restyle.",
);
print_option("dump-flow-tree", "Print the flow tree after each layout.");
print_option("dump-display-list", "Print the display list after each layout.");
print_option("dump-display-list-json", "Print the display list in JSON form.");
print_option("relayout-event", "Print notifications when there is a relayout.");
print_option("profile-script-events", "Enable profiling of script-related events.");
print_option("profile-heartbeats", "Enable heartbeats for all thread categories.");
print_option("show-fragment-borders", "Paint borders along fragment boundaries.");
print_option("show-parallel-layout", "Mark which thread laid each flow out with colors.");
print_option("trace-layout", "Write layout trace to an external file for debugging.");
print_option("disable-share-style-cache",
"Disable the style sharing cache.");
print_option("parallel-display-list-building", "Build display lists in parallel.");
print_option("convert-mouse-to-touch", "Send touch events instead of mouse events");
print_option("replace-surrogates", "Replace unpaires surrogates in DOM strings with U+FFFD. \
See https://github.com/servo/servo/issues/6564");
print_option(
"dump-display-list",
"Print the display list after each layout.",
);
print_option(
"dump-display-list-json",
"Print the display list in JSON form.",
);
print_option(
"relayout-event",
"Print notifications when there is a relayout.",
);
print_option(
"profile-script-events",
"Enable profiling of script-related events.",
);
print_option(
"profile-heartbeats",
"Enable heartbeats for all thread categories.",
);
print_option(
"show-fragment-borders",
"Paint borders along fragment boundaries.",
);
print_option(
"show-parallel-layout",
"Mark which thread laid each flow out with colors.",
);
print_option(
"trace-layout",
"Write layout trace to an external file for debugging.",
);
print_option(
"disable-share-style-cache",
"Disable the style sharing cache.",
);
print_option(
"parallel-display-list-building",
"Build display lists in parallel.",
);
print_option(
"convert-mouse-to-touch",
"Send touch events instead of mouse events",
);
print_option(
"replace-surrogates",
"Replace unpaires surrogates in DOM strings with U+FFFD. \
See https://github.com/servo/servo/issues/6564",
);
print_option("gc-profile", "Log GC passes and their durations.");
print_option("load-webfonts-synchronously",
"Load web fonts synchronously to avoid non-deterministic network-driven reflows");
print_option("disable-vsync",
"Disable vsync mode in the compositor to allow profiling at more than monitor refresh rate");
print_option(
"load-webfonts-synchronously",
"Load web fonts synchronously to avoid non-deterministic network-driven reflows",
);
print_option(
"disable-vsync",
"Disable vsync mode in the compositor to allow profiling at more than monitor refresh rate",
);
print_option("wr-stats", "Show WebRender profiler on screen.");
print_option("msaa", "Use multisample antialiasing in WebRender.");
print_option("full-backtraces", "Print full backtraces for all errors");
print_option("wr-debug", "Display webrender tile borders.");
print_option("wr-no-batch", "Disable webrender instanced batching.");
print_option("precache-shaders", "Compile all shaders during init.");
print_option("signpost", "Emit native OS signposts for profile events (currently macOS only)");
print_option(
"signpost",
"Emit native OS signposts for profile events (currently macOS only)",
);
println!("");
@ -445,7 +498,7 @@ enum UserAgent {
Desktop,
Android,
#[allow(non_camel_case_types)]
iOS
iOS,
}
fn default_user_agent_string(agent: UserAgent) -> &'static str {
@ -468,17 +521,12 @@ fn default_user_agent_string(agent: UserAgent) -> &'static str {
const DESKTOP_UA_STRING: &'static str =
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:55.0) Servo/1.0 Firefox/55.0";
match agent {
UserAgent::Desktop => {
DESKTOP_UA_STRING
}
UserAgent::Android => {
"Mozilla/5.0 (Android; Mobile; rv:55.0) Servo/1.0 Firefox/55.0"
}
UserAgent::Desktop => DESKTOP_UA_STRING,
UserAgent::Android => "Mozilla/5.0 (Android; Mobile; rv:55.0) Servo/1.0 Firefox/55.0",
UserAgent::iOS => {
"Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X; rv:55.0) Servo/1.0 Firefox/55.0"
}
},
}
}
@ -564,54 +612,146 @@ pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult {
opts.optopt("o", "output", "Output file", "output.png");
opts.optopt("s", "size", "Size of tiles", "512");
opts.optopt("", "device-pixel-ratio", "Device pixels per px", "");
opts.optflagopt("p", "profile", "Time profiler flag and either a TSV output filename \
OR an interval for output to Stdout (blank for Stdout with interval of 5s)", "10 \
OR time.tsv");
opts.optflagopt("", "profiler-trace-path",
"Path to dump a self-contained HTML timeline of profiler traces",
"");
opts.optflagopt("m", "memory-profile", "Memory profiler flag and output interval", "10");
opts.optflagopt(
"p",
"profile",
"Time profiler flag and either a TSV output filename \
OR an interval for output to Stdout (blank for Stdout with interval of 5s)",
"10 \
OR time.tsv",
);
opts.optflagopt(
"",
"profiler-trace-path",
"Path to dump a self-contained HTML timeline of profiler traces",
"",
);
opts.optflagopt(
"m",
"memory-profile",
"Memory profiler flag and output interval",
"10",
);
opts.optflag("x", "exit", "Exit after load flag");
opts.optopt("y", "layout-threads", "Number of threads to use for layout", "1");
opts.optflag("i", "nonincremental-layout", "Enable to turn off incremental layout.");
opts.optflagopt("", "userscripts",
"Uses userscripts in resources/user-agent-js, or a specified full path", "");
opts.optmulti("", "user-stylesheet",
"A user stylesheet to be added to every document", "file.css");
opts.optopt("", "shaders",
"Shaders will be loaded from the specified directory instead of using the builtin ones.", "");
opts.optopt(
"y",
"layout-threads",
"Number of threads to use for layout",
"1",
);
opts.optflag(
"i",
"nonincremental-layout",
"Enable to turn off incremental layout.",
);
opts.optflagopt(
"",
"userscripts",
"Uses userscripts in resources/user-agent-js, or a specified full path",
"",
);
opts.optmulti(
"",
"user-stylesheet",
"A user stylesheet to be added to every document",
"file.css",
);
opts.optopt(
"",
"shaders",
"Shaders will be loaded from the specified directory instead of using the builtin ones.",
"",
);
opts.optflag("z", "headless", "Headless mode");
opts.optflag("f", "hard-fail", "Exit on thread failure instead of displaying about:failure");
opts.optflag("F", "soft-fail", "Display about:failure on thread failure instead of exiting");
opts.optflagopt("", "remote-debugging-port", "Start remote debugger server on port", "2794");
opts.optflagopt("", "devtools", "Start remote devtools server on port", "6000");
opts.optflagopt("", "webdriver", "Start remote WebDriver server on port", "7000");
opts.optflag(
"f",
"hard-fail",
"Exit on thread failure instead of displaying about:failure",
);
opts.optflag(
"F",
"soft-fail",
"Display about:failure on thread failure instead of exiting",
);
opts.optflagopt(
"",
"remote-debugging-port",
"Start remote debugger server on port",
"2794",
);
opts.optflagopt(
"",
"devtools",
"Start remote devtools server on port",
"6000",
);
opts.optflagopt(
"",
"webdriver",
"Start remote WebDriver server on port",
"7000",
);
opts.optopt("", "resolution", "Set window resolution.", "1024x740");
opts.optopt("u",
"user-agent",
"Set custom user agent string (or ios / android / desktop for platform default)",
"NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m)");
opts.optopt(
"u",
"user-agent",
"Set custom user agent string (or ios / android / desktop for platform default)",
"NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m)",
);
opts.optflag("M", "multiprocess", "Run in multiprocess mode");
opts.optflag("S", "sandbox", "Run in a sandbox if multiprocess");
opts.optopt("",
"random-pipeline-closure-probability",
"Probability of randomly closing a pipeline (for testing constellation hardening).",
"0.0");
opts.optopt("", "random-pipeline-closure-seed", "A fixed seed for repeatbility of random pipeline closure.", "");
opts.optmulti("Z", "debug",
"A comma-separated string of debug options. Pass help to show available options.", "");
opts.optopt(
"",
"random-pipeline-closure-probability",
"Probability of randomly closing a pipeline (for testing constellation hardening).",
"0.0",
);
opts.optopt(
"",
"random-pipeline-closure-seed",
"A fixed seed for repeatbility of random pipeline closure.",
"",
);
opts.optmulti(
"Z",
"debug",
"A comma-separated string of debug options. Pass help to show available options.",
"",
);
opts.optflag("h", "help", "Print this message");
opts.optopt("", "resources-path", "Path to find static resources", "/home/servo/resources");
opts.optopt("", "certificate-path", "Path to find SSL certificates", "/home/servo/resources/certs");
opts.optopt("", "content-process" , "Run as a content process and connect to the given pipe",
"servo-ipc-channel.abcdefg");
opts.optmulti("", "pref",
"A preference to set to enable", "dom.bluetooth.enabled");
opts.optopt(
"",
"resources-path",
"Path to find static resources",
"/home/servo/resources",
);
opts.optopt(
"",
"certificate-path",
"Path to find SSL certificates",
"/home/servo/resources/certs",
);
opts.optopt(
"",
"content-process",
"Run as a content process and connect to the given pipe",
"servo-ipc-channel.abcdefg",
);
opts.optmulti(
"",
"pref",
"A preference to set to enable",
"dom.bluetooth.enabled",
);
opts.optflag("b", "no-native-titlebar", "Do not use native titlebar");
opts.optflag("w", "webrender", "Use webrender backend");
opts.optopt("G", "graphics", "Select graphics backend (gl or es2)", "gl");
opts.optopt("", "config-dir",
"config directory following xdg spec on linux platform", "");
opts.optopt(
"",
"config-dir",
"config directory following xdg spec on linux platform",
"",
);
opts.optflag("v", "version", "Display servo version information");
opts.optflag("", "unminify-js", "Unminify Javascript");
opts.optopt("", "profiler-db-user", "Profiler database user", "");
@ -654,44 +794,50 @@ pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult {
} else {
None
};
let is_running_problem_test =
url_opt
.as_ref()
.map_or(false, |url|
url.starts_with("http://web-platform.test:8000/2dcontext/drawing-images-to-the-canvas/") ||
url.starts_with("http://web-platform.test:8000/_mozilla/mozilla/canvas/") ||
url.starts_with("http://web-platform.test:8000/_mozilla/css/canvas_over_area.html"));
let is_running_problem_test = url_opt.as_ref().map_or(false, |url| {
url.starts_with("http://web-platform.test:8000/2dcontext/drawing-images-to-the-canvas/") ||
url.starts_with("http://web-platform.test:8000/_mozilla/mozilla/canvas/") ||
url.starts_with("http://web-platform.test:8000/_mozilla/css/canvas_over_area.html")
});
let url_opt = url_opt.and_then(|url_string| parse_url_or_filename(&cwd, url_string)
.or_else(|error| {
warn!("URL parsing failed ({:?}).", error);
Err(error)
}).ok());
let url_opt = url_opt.and_then(|url_string| {
parse_url_or_filename(&cwd, url_string)
.or_else(|error| {
warn!("URL parsing failed ({:?}).", error);
Err(error)
}).ok()
});
let tile_size: usize = match opt_match.opt_str("s") {
Some(tile_size_str) => tile_size_str.parse()
Some(tile_size_str) => tile_size_str
.parse()
.unwrap_or_else(|err| args_fail(&format!("Error parsing option: -s ({})", err))),
None => 512,
};
let device_pixels_per_px = opt_match.opt_str("device-pixel-ratio").map(|dppx_str|
dppx_str.parse()
.unwrap_or_else(|err| args_fail(&format!("Error parsing option: --device-pixel-ratio ({})", err)))
);
let device_pixels_per_px = opt_match.opt_str("device-pixel-ratio").map(|dppx_str| {
dppx_str.parse().unwrap_or_else(|err| {
args_fail(&format!(
"Error parsing option: --device-pixel-ratio ({})",
err
))
})
});
// If only the flag is present, default to a 5 second period for both profilers
let time_profiling = if opt_match.opt_present("p") {
match opt_match.opt_str("p") {
Some(argument) => match argument.parse::<f64>() {
Ok(interval) => Some(OutputOptions::Stdout(interval)) ,
Err(_) => {
match ServoUrl::parse(&argument) {
Ok(url) => Some(OutputOptions::DB(url, opt_match.opt_str("profiler-db-name"),
opt_match.opt_str("profiler-db-user"),
opt_match.opt_str("profiler-db-pass"))),
Err(_) => Some(OutputOptions::FileName(argument)),
}
}
Ok(interval) => Some(OutputOptions::Stdout(interval)),
Err(_) => match ServoUrl::parse(&argument) {
Ok(url) => Some(OutputOptions::DB(
url,
opt_match.opt_str("profiler-db-name"),
opt_match.opt_str("profiler-db-user"),
opt_match.opt_str("profiler-db-pass"),
)),
Err(_) => Some(OutputOptions::FileName(argument)),
},
},
None => Some(OutputOptions::Stdout(5.0 as f64)),
}
@ -704,34 +850,50 @@ pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult {
let mut path = PathBuf::from(time_profiler_trace_path);
path.pop();
if let Err(why) = fs::create_dir_all(&path) {
error!("Couldn't create/open {:?}: {:?}",
Path::new(time_profiler_trace_path).to_string_lossy(), why);
error!(
"Couldn't create/open {:?}: {:?}",
Path::new(time_profiler_trace_path).to_string_lossy(),
why
);
}
}
let mem_profiler_period = opt_match.opt_default("m", "5").map(|period| {
period.parse().unwrap_or_else(|err| args_fail(&format!("Error parsing option: -m ({})", err)))
period
.parse()
.unwrap_or_else(|err| args_fail(&format!("Error parsing option: -m ({})", err)))
});
let mut layout_threads: Option<usize> = opt_match.opt_str("y")
.map(|layout_threads_str| {
layout_threads_str.parse()
.unwrap_or_else(|err| args_fail(&format!("Error parsing option: -y ({})", err)))
});
let mut layout_threads: Option<usize> = opt_match.opt_str("y").map(|layout_threads_str| {
layout_threads_str
.parse()
.unwrap_or_else(|err| args_fail(&format!("Error parsing option: -y ({})", err)))
});
let nonincremental_layout = opt_match.opt_present("i");
let random_pipeline_closure_probability = opt_match.opt_str("random-pipeline-closure-probability").map(|prob|
prob.parse().unwrap_or_else(|err| {
args_fail(&format!("Error parsing option: --random-pipeline-closure-probability ({})", err))
})
);
let random_pipeline_closure_probability = opt_match
.opt_str("random-pipeline-closure-probability")
.map(|prob| {
prob.parse().unwrap_or_else(|err| {
args_fail(&format!(
"Error parsing option: --random-pipeline-closure-probability ({})",
err
))
})
});
let random_pipeline_closure_seed = opt_match.opt_str("random-pipeline-closure-seed").map(|seed|
seed.parse().unwrap_or_else(|err| {
args_fail(&format!("Error parsing option: --random-pipeline-closure-seed ({})", err))
})
);
let random_pipeline_closure_seed =
opt_match
.opt_str("random-pipeline-closure-seed")
.map(|seed| {
seed.parse().unwrap_or_else(|err| {
args_fail(&format!(
"Error parsing option: --random-pipeline-closure-seed ({})",
err
))
})
});
let mut bubble_inline_sizes_separately = debug_options.bubble_widths;
if debug_options.trace_layout {
@ -739,29 +901,40 @@ pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult {
bubble_inline_sizes_separately = true;
}
let debugger_port = opt_match.opt_default("remote-debugging-port", "2794").map(|port| {
port.parse()
.unwrap_or_else(|err| args_fail(&format!("Error parsing option: --remote-debugging-port ({})", err)))
});
let debugger_port = opt_match
.opt_default("remote-debugging-port", "2794")
.map(|port| {
port.parse().unwrap_or_else(|err| {
args_fail(&format!(
"Error parsing option: --remote-debugging-port ({})",
err
))
})
});
let devtools_port = opt_match.opt_default("devtools", "6000").map(|port| {
port.parse().unwrap_or_else(|err| args_fail(&format!("Error parsing option: --devtools ({})", err)))
port.parse()
.unwrap_or_else(|err| args_fail(&format!("Error parsing option: --devtools ({})", err)))
});
let webdriver_port = opt_match.opt_default("webdriver", "7000").map(|port| {
port.parse().unwrap_or_else(|err| args_fail(&format!("Error parsing option: --webdriver ({})", err)))
port.parse().unwrap_or_else(|err| {
args_fail(&format!("Error parsing option: --webdriver ({})", err))
})
});
let initial_window_size = match opt_match.opt_str("resolution") {
Some(res_string) => {
let res: Vec<u32> = res_string.split('x').map(|r| {
r.parse().unwrap_or_else(|err| args_fail(&format!("Error parsing option: --resolution ({})", err)))
}).collect();
let res: Vec<u32> = res_string
.split('x')
.map(|r| {
r.parse().unwrap_or_else(|err| {
args_fail(&format!("Error parsing option: --resolution ({})", err))
})
}).collect();
TypedSize2D::new(res[0], res[1])
}
None => {
TypedSize2D::new(1024, 740)
}
},
None => TypedSize2D::new(1024, 740),
};
if opt_match.opt_present("M") {
@ -776,20 +949,24 @@ pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult {
None => default_user_agent_string(DEFAULT_USER_AGENT).into(),
};
let user_stylesheets = opt_match.opt_strs("user-stylesheet").iter().map(|filename| {
let path = cwd.join(filename);
let url = ServoUrl::from_url(Url::from_file_path(&path).unwrap());
let mut contents = Vec::new();
File::open(path)
.unwrap_or_else(|err| args_fail(&format!("Couldn't open {}: {}", filename, err)))
.read_to_end(&mut contents)
.unwrap_or_else(|err| args_fail(&format!("Couldn't read {}: {}", filename, err)));
(contents, url)
}).collect();
let user_stylesheets = opt_match
.opt_strs("user-stylesheet")
.iter()
.map(|filename| {
let path = cwd.join(filename);
let url = ServoUrl::from_url(Url::from_file_path(&path).unwrap());
let mut contents = Vec::new();
File::open(path)
.unwrap_or_else(|err| args_fail(&format!("Couldn't open {}: {}", filename, err)))
.read_to_end(&mut contents)
.unwrap_or_else(|err| args_fail(&format!("Couldn't read {}: {}", filename, err)));
(contents, url)
}).collect();
let do_not_use_native_titlebar =
opt_match.opt_present("b") ||
!PREFS.get("shell.native-titlebar.enabled").as_boolean().unwrap();
let do_not_use_native_titlebar = opt_match.opt_present("b") || !PREFS
.get("shell.native-titlebar.enabled")
.as_boolean()
.unwrap();
let is_printing_version = opt_match.opt_present("v") || opt_match.opt_present("version");
@ -870,7 +1047,10 @@ pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult {
if let Some(layout_threads) = layout_threads {
PREFS.set("layout.threads", PrefValue::Number(layout_threads as f64));
} else if let Some(layout_threads) = PREFS.get("layout.threads").as_string() {
PREFS.set("layout.threads", PrefValue::Number(layout_threads.parse::<f64>().unwrap()));
PREFS.set(
"layout.threads",
PrefValue::Number(layout_threads.parse::<f64>().unwrap()),
);
} else if *PREFS.get("layout.threads") == PrefValue::Missing {
let layout_threads = cmp::max(num_cpus::get() * 3 / 4, 1);
PREFS.set("layout.threads", PrefValue::Number(layout_threads as f64));
@ -926,8 +1106,8 @@ pub fn parse_pref_from_command_line(pref: &str) {
Some(&"true") | None => PREFS.set(pref_name, PrefValue::Boolean(true)),
Some(value) => match value.parse::<f64>() {
Ok(v) => PREFS.set(pref_name, PrefValue::Number(v)),
Err(_) => PREFS.set(pref_name, PrefValue::String(value.to_string()))
}
Err(_) => PREFS.set(pref_name, PrefValue::String(value.to_string())),
},
};
}
@ -941,7 +1121,7 @@ pub fn parse_url_or_filename(cwd: &Path, input: &str) -> Result<ServoUrl, ()> {
Ok(url) => Ok(url),
Err(url::ParseError::RelativeUrlWithoutBase) => {
Url::from_file_path(&*cwd.join(input)).map(ServoUrl::from_url)
}
},
Err(_) => Err(()),
}
}

View file

@ -30,7 +30,7 @@ pub enum PrefValue {
Boolean(bool),
String(String),
Number(f64),
Missing
Missing,
}
impl PrefValue {
@ -41,26 +41,22 @@ impl PrefValue {
Json::F64(x) => PrefValue::Number(x),
Json::I64(x) => PrefValue::Number(x as f64),
Json::U64(x) => PrefValue::Number(x as f64),
_ => return Err(())
_ => return Err(()),
};
Ok(value)
}
pub fn as_boolean(&self) -> Option<bool> {
match *self {
PrefValue::Boolean(value) => {
Some(value)
},
_ => None
PrefValue::Boolean(value) => Some(value),
_ => None,
}
}
pub fn as_string(&self) -> Option<&str> {
match *self {
PrefValue::String(ref value) => {
Some(&value)
},
_ => None
PrefValue::String(ref value) => Some(&value),
_ => None,
}
}
@ -82,16 +78,10 @@ impl PrefValue {
impl ToJson for PrefValue {
fn to_json(&self) -> Json {
match *self {
PrefValue::Boolean(x) => {
Json::Boolean(x)
},
PrefValue::String(ref x) => {
Json::String(x.clone())
},
PrefValue::Number(x) => {
Json::F64(x)
},
PrefValue::Missing => Json::Null
PrefValue::Boolean(x) => Json::Boolean(x),
PrefValue::String(ref x) => Json::String(x.clone()),
PrefValue::Number(x) => Json::F64(x),
PrefValue::Missing => Json::Null,
}
}
}
@ -99,10 +89,9 @@ impl ToJson for PrefValue {
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Pref {
NoDefault(Arc<PrefValue>),
WithDefault(Arc<PrefValue>, Option<Arc<PrefValue>>)
WithDefault(Arc<PrefValue>, Option<Arc<PrefValue>>),
}
impl Pref {
pub fn new(value: PrefValue) -> Pref {
Pref::NoDefault(Arc::new(value))
@ -120,12 +109,10 @@ impl Pref {
pub fn value(&self) -> &Arc<PrefValue> {
match *self {
Pref::NoDefault(ref x) => x,
Pref::WithDefault(ref default, ref override_value) => {
match *override_value {
Some(ref x) => x,
None => default
}
}
Pref::WithDefault(ref default, ref override_value) => match *override_value {
Some(ref x) => x,
None => default,
},
}
}
@ -133,12 +120,8 @@ impl Pref {
// TODO - this should error if we try to override a pref of one type
// with a value of a different type
match *self {
Pref::NoDefault(ref mut pref_value) => {
*pref_value = Arc::new(value)
},
Pref::WithDefault(_, ref mut override_value) => {
*override_value = Some(Arc::new(value))
}
Pref::NoDefault(ref mut pref_value) => *pref_value = Arc::new(value),
Pref::WithDefault(_, ref mut override_value) => *override_value = Some(Arc::new(value)),
}
}
}
@ -151,8 +134,10 @@ impl ToJson for Pref {
pub fn default_prefs() -> Preferences {
let prefs = Preferences(Arc::new(RwLock::new(HashMap::new())));
prefs.set("layout.threads", PrefValue::Number(
max(num_cpus::get() * 3 / 4, 1) as f64));
prefs.set(
"layout.threads",
PrefValue::Number(max(num_cpus::get() * 3 / 4, 1) as f64),
);
prefs
}
@ -169,7 +154,10 @@ pub fn read_prefs(txt: &str) -> Result<HashMap<String, Pref>, ()> {
Ok(x) => {
prefs.insert(name, x);
},
Err(_) => println!("Ignoring non-boolean/string/i64 preference value for {:?}", name),
Err(_) => println!(
"Ignoring non-boolean/string/i64 preference value for {:?}",
name
),
}
}
}
@ -181,14 +169,14 @@ pub fn add_user_prefs() {
Some(ref config_path) => {
let mut path = PathBuf::from(config_path);
init_user_prefs(&mut path);
}
},
None => {
if let Some(mut path) = default_config_dir() {
if path.join("prefs.json").exists() {
init_user_prefs(&mut path);
}
}
}
},
}
}
@ -201,8 +189,10 @@ fn init_user_prefs(path: &mut PathBuf) {
PREFS.extend(prefs);
}
} else {
writeln!(&mut stderr(), "Error opening prefs.json from config directory")
.expect("failed printing to stderr");
writeln!(
&mut stderr(),
"Error opening prefs.json from config directory"
).expect("failed printing to stderr");
}
}
@ -210,7 +200,11 @@ pub struct Preferences(Arc<RwLock<HashMap<String, Pref>>>);
impl Preferences {
pub fn get(&self, name: &str) -> Arc<PrefValue> {
self.0.read().unwrap().get(name).map_or(Arc::new(PrefValue::Missing), |x| x.value().clone())
self.0
.read()
.unwrap()
.get(name)
.map_or(Arc::new(PrefValue::Missing), |x| x.value().clone())
}
pub fn cloned(&self) -> HashMap<String, Pref> {
@ -244,7 +238,12 @@ impl Preferences {
pub fn reset_all(&self) {
let names = {
self.0.read().unwrap().keys().cloned().collect::<Vec<String>>()
self.0
.read()
.unwrap()
.keys()
.cloned()
.collect::<Vec<String>>()
};
for name in names.iter() {
self.reset(name);
@ -260,7 +259,9 @@ impl Preferences {
}
pub fn is_dom_to_texture_enabled(&self) -> bool {
self.get("dom.webgl.dom_to_texture.enabled").as_boolean().unwrap_or(false)
self.get("dom.webgl.dom_to_texture.enabled")
.as_boolean()
.unwrap_or(false)
}
pub fn is_webgl2_enabled(&self) -> bool {

View file

@ -24,7 +24,10 @@ fn test_argument_parsing() {
let url = parse_url_or_filename(fake_cwd, "file:///foo/bar.html").unwrap();
assert_eq!(url.scheme(), "file");
assert_eq!(url.path_segments().unwrap().collect::<Vec<_>>(), ["foo", "bar.html"]);
assert_eq!(
url.path_segments().unwrap().collect::<Vec<_>>(),
["foo", "bar.html"]
);
}
#[test]
@ -34,7 +37,10 @@ fn test_file_path_parsing() {
let url = parse_url_or_filename(fake_cwd, "bar.html").unwrap();
assert_eq!(url.scheme(), "file");
assert_eq!(url.path_segments().unwrap().collect::<Vec<_>>(), ["fake", "cwd", "bar.html"]);
assert_eq!(
url.path_segments().unwrap().collect::<Vec<_>>(),
["fake", "cwd", "bar.html"]
);
}
#[test]
@ -44,7 +50,10 @@ fn test_file_path_parsing() {
let url = parse_url_or_filename(fake_cwd, "bar.html").unwrap();
assert_eq!(url.scheme(), "file");
assert_eq!(url.path_segments().unwrap().collect::<Vec<_>>(), ["C:", "fake", "cwd", "bar.html"]);
assert_eq!(
url.path_segments().unwrap().collect::<Vec<_>>(),
["C:", "fake", "cwd", "bar.html"]
);
}
#[test]
@ -57,16 +66,24 @@ fn test_argument_parsing_special() {
let url = parse_url_or_filename(fake_cwd, "file:///foo/bar?baz#buzz.html").unwrap();
assert_eq!(&*url.to_file_path().unwrap(), Path::new("/foo/bar"));
assert_eq!(url.scheme(), "file");
assert_eq!(url.path_segments().unwrap().collect::<Vec<_>>(), ["foo", "bar"]);
assert_eq!(
url.path_segments().unwrap().collect::<Vec<_>>(),
["foo", "bar"]
);
assert_eq!(url.query(), Some("baz"));
assert_eq!(url.fragment(), Some("buzz.html"));
// but not in file names.
let url = parse_url_or_filename(fake_cwd, "./bar?baz#buzz.html").unwrap();
assert_eq!(&*url.to_file_path().unwrap(), Path::new("/fake/cwd/bar?baz#buzz.html"));
assert_eq!(
&*url.to_file_path().unwrap(),
Path::new("/fake/cwd/bar?baz#buzz.html")
);
assert_eq!(url.scheme(), "file");
assert_eq!(url.path_segments().unwrap().collect::<Vec<_>>(),
["fake", "cwd", "bar%3Fbaz%23buzz.html"]);
assert_eq!(
url.path_segments().unwrap().collect::<Vec<_>>(),
["fake", "cwd", "bar%3Fbaz%23buzz.html"]
);
assert_eq!(url.query(), None);
assert_eq!(url.fragment(), None);
}

View file

@ -12,10 +12,10 @@ use std::io::{Read, Write};
#[test]
fn test_create_pref() {
let json_str = "{\
\"layout.writing-mode.enabled\": true,\
\"network.mime.sniff\": false,\
\"shell.homepage\": \"https://servo.org\"\
}";
\"layout.writing-mode.enabled\": true,\
\"network.mime.sniff\": false,\
\"shell.homepage\": \"https://servo.org\"\
}";
let prefs = read_prefs(json_str);
assert!(prefs.is_ok());
@ -27,40 +27,52 @@ fn test_create_pref() {
#[test]
fn test_get_set_reset_extend() {
let json_str = "{\
\"layout.writing-mode.enabled\": true,\
\"extra.stuff\": false,\
\"shell.homepage\": \"https://google.com\"\
}";
\"layout.writing-mode.enabled\": true,\
\"extra.stuff\": false,\
\"shell.homepage\": \"https://google.com\"\
}";
assert_eq!(*PREFS.get("test"), PrefValue::Missing);
PREFS.set("test", PrefValue::String("hi".to_owned()));
assert_eq!(*PREFS.get("test"), PrefValue::String("hi".to_owned()));
assert_eq!(*PREFS.get("shell.homepage"), PrefValue::String("https://servo.org".to_owned()));
assert_eq!(
*PREFS.get("shell.homepage"),
PrefValue::String("https://servo.org".to_owned())
);
PREFS.set("shell.homepage", PrefValue::Boolean(true));
assert_eq!(*PREFS.get("shell.homepage"), PrefValue::Boolean(true));
PREFS.reset("shell.homepage");
assert_eq!(*PREFS.get("shell.homepage"), PrefValue::String("https://servo.org".to_owned()));
assert_eq!(
*PREFS.get("shell.homepage"),
PrefValue::String("https://servo.org".to_owned())
);
let extension = read_prefs(json_str).unwrap();
PREFS.extend(extension);
assert_eq!(*PREFS.get("shell.homepage"), PrefValue::String("https://google.com".to_owned()));
assert_eq!(*PREFS.get("layout.writing-mode.enabled"), PrefValue::Boolean(true));
assert_eq!(
*PREFS.get("shell.homepage"),
PrefValue::String("https://google.com".to_owned())
);
assert_eq!(
*PREFS.get("layout.writing-mode.enabled"),
PrefValue::Boolean(true)
);
assert_eq!(*PREFS.get("extra.stuff"), PrefValue::Boolean(false));
}
#[cfg(not(target_os = "android"))]
#[test]
fn test_default_config_dir_create_read_write() {
let json_str = "{\
\"layout.writing-mode.enabled\": true,\
\"extra.stuff\": false,\
\"shell.homepage\": \"https://google.com\"\
}";
let json_str = "{\
\"layout.writing-mode.enabled\": true,\
\"extra.stuff\": false,\
\"shell.homepage\": \"https://google.com\"\
}";
let mut expected_json = String::new();
let config_path = basedir::default_config_dir().unwrap();
if !config_path.exists() {
fs::create_dir_all(&config_path).unwrap();
fs::create_dir_all(&config_path).unwrap();
}
let json_path = config_path.join("test_config.json");

View file

@ -18,7 +18,7 @@ enum Message {
pub struct Sender(mpsc::Sender<Message>);
struct Connection {
sender: ws::Sender
sender: ws::Sender,
}
impl Handler for Connection {
@ -39,23 +39,27 @@ impl Handler for Connection {
pub fn start_server(port: u16) -> Sender {
debug!("Starting server.");
let (sender, receiver) = channel();
thread::Builder::new().name("debugger".to_owned()).spawn(move || {
let socket = Builder::new().build(|sender: ws::Sender| {
Connection { sender: sender }
}).unwrap();
let sender = socket.broadcaster();
thread::Builder::new().name("debugger-websocket".to_owned()).spawn(move || {
socket.listen(("127.0.0.1", port)).unwrap();
}).expect("Thread spawning failed");
while let Ok(message) = receiver.recv() {
match message {
Message::ShutdownServer => {
break;
thread::Builder::new()
.name("debugger".to_owned())
.spawn(move || {
let socket = Builder::new()
.build(|sender: ws::Sender| Connection { sender: sender })
.unwrap();
let sender = socket.broadcaster();
thread::Builder::new()
.name("debugger-websocket".to_owned())
.spawn(move || {
socket.listen(("127.0.0.1", port)).unwrap();
}).expect("Thread spawning failed");
while let Ok(message) = receiver.recv() {
match message {
Message::ShutdownServer => {
break;
},
}
}
}
sender.shutdown().unwrap();
}).expect("Thread spawning failed");
sender.shutdown().unwrap();
}).expect("Thread spawning failed");
Sender(sender)
}

View file

@ -14,8 +14,10 @@ decl_derive!([DenyPublicFields] => deny_public_fields_derive);
fn deny_public_fields_derive(s: synstructure::Structure) -> proc_macro::TokenStream {
s.each(|binding| {
if binding.ast().vis != syn::Visibility::Inherited {
panic!("Field `{}` should not be public",
binding.ast().ident.as_ref().unwrap_or(&binding.binding));
panic!(
"Field `{}` should not be public",
binding.ast().ident.as_ref().unwrap_or(&binding.binding)
);
}
"".to_owned()

View file

@ -3,7 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/// General actor system infrastructure.
use devtools_traits::PreciseTime;
use serde_json::{Map, Value};
use std::any::Any;
@ -23,11 +22,13 @@ pub enum ActorMessageStatus {
/// and the ability to process messages that are directed to particular actors.
/// TODO: ensure the name is immutable
pub trait Actor: Any + ActorAsAny {
fn handle_message(&self,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()>;
fn handle_message(
&self,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()>;
fn name(&self) -> String;
}
@ -37,8 +38,12 @@ pub trait ActorAsAny {
}
impl<T: Actor> ActorAsAny for T {
fn actor_as_any(&self) -> &Any { self }
fn actor_as_any_mut(&mut self) -> &mut Any { self }
fn actor_as_any(&self) -> &Any {
self
}
fn actor_as_any_mut(&mut self) -> &mut Any {
self
}
}
/// A list of known, owned actors.
@ -57,8 +62,8 @@ impl ActorRegistry {
pub fn new() -> ActorRegistry {
ActorRegistry {
actors: HashMap::new(),
new_actors: RefCell::new(vec!()),
old_actors: RefCell::new(vec!()),
new_actors: RefCell::new(vec![]),
old_actors: RefCell::new(vec![]),
script_actors: RefCell::new(HashMap::new()),
shareable: None,
next: Cell::new(0),
@ -149,29 +154,33 @@ impl ActorRegistry {
/// Attempt to process a message as directed by its `to` property. If the actor is not
/// found or does not indicate that it knew how to process the message, ignore the failure.
pub fn handle_message(&mut self,
msg: &Map<String, Value>,
stream: &mut TcpStream)
-> Result<(), ()> {
pub fn handle_message(
&mut self,
msg: &Map<String, Value>,
stream: &mut TcpStream,
) -> Result<(), ()> {
let to = msg.get("to").unwrap().as_str().unwrap();
match self.actors.get(to) {
None => debug!("message received for unknown actor \"{}\"", to),
Some(actor) => {
let msg_type = msg.get("type").unwrap().as_str().unwrap();
if actor.handle_message(self, msg_type, msg, stream)?
!= ActorMessageStatus::Processed {
debug!("unexpected message type \"{}\" found for actor \"{}\"",
msg_type, to);
if actor.handle_message(self, msg_type, msg, stream)? !=
ActorMessageStatus::Processed
{
debug!(
"unexpected message type \"{}\" found for actor \"{}\"",
msg_type, to
);
}
}
},
}
let new_actors = replace(&mut *self.new_actors.borrow_mut(), vec!());
let new_actors = replace(&mut *self.new_actors.borrow_mut(), vec![]);
for actor in new_actors.into_iter() {
self.actors.insert(actor.name().to_owned(), actor);
}
let old_actors = replace(&mut *self.old_actors.borrow_mut(), vec!());
let old_actors = replace(&mut *self.old_actors.borrow_mut(), vec![]);
for name in old_actors {
self.drop_actor(name);
}

View file

@ -94,32 +94,48 @@ impl Actor for ConsoleActor {
self.name.clone()
}
fn handle_message(&self,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(match msg_type {
"getCachedMessages" => {
let str_types = msg.get("messageTypes").unwrap().as_array().unwrap().into_iter().map(|json_type| {
json_type.as_str().unwrap()
});
let str_types = msg
.get("messageTypes")
.unwrap()
.as_array()
.unwrap()
.into_iter()
.map(|json_type| json_type.as_str().unwrap());
let mut message_types = CachedConsoleMessageTypes::empty();
for str_type in str_types {
match str_type {
"PageError" => message_types.insert(CachedConsoleMessageTypes::PAGE_ERROR),
"ConsoleAPI" => message_types.insert(CachedConsoleMessageTypes::CONSOLE_API),
"ConsoleAPI" => {
message_types.insert(CachedConsoleMessageTypes::CONSOLE_API)
},
s => debug!("unrecognized message type requested: \"{}\"", s),
};
};
}
let (chan, port) = ipc::channel().unwrap();
self.script_chan.send(DevtoolScriptControlMsg::GetCachedMessages(
self.pipeline, message_types, chan)).unwrap();
let messages = port.recv().map_err(|_| ())?.into_iter().map(|message| {
let json_string = message.encode().unwrap();
let json = serde_json::from_str::<Value>(&json_string).unwrap();
json.as_object().unwrap().to_owned()
}).collect();
self.script_chan
.send(DevtoolScriptControlMsg::GetCachedMessages(
self.pipeline,
message_types,
chan,
)).unwrap();
let messages = port
.recv()
.map_err(|_| ())?
.into_iter()
.map(|message| {
let json_string = message.encode().unwrap();
let json = serde_json::from_str::<Value>(&json_string).unwrap();
json.as_object().unwrap().to_owned()
}).collect();
let msg = GetCachedMessagesReply {
from: self.name(),
@ -127,56 +143,60 @@ impl Actor for ConsoleActor {
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"startListeners" => {
//TODO: actually implement listener filters that support starting/stopping
let msg = StartedListenersReply {
from: self.name(),
nativeConsoleAPI: true,
startedListeners:
vec!("PageError".to_owned(), "ConsoleAPI".to_owned()),
startedListeners: vec!["PageError".to_owned(), "ConsoleAPI".to_owned()],
traits: StartedListenersTraits {
customNetworkRequest: true,
}
},
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"stopListeners" => {
//TODO: actually implement listener filters that support starting/stopping
let msg = StopListenersReply {
from: self.name(),
stoppedListeners: msg.get("listeners")
.unwrap()
.as_array()
.unwrap_or(&vec!())
.iter()
.map(|listener| listener.as_str().unwrap().to_owned())
.collect(),
stoppedListeners: msg
.get("listeners")
.unwrap()
.as_array()
.unwrap_or(&vec![])
.iter()
.map(|listener| listener.as_str().unwrap().to_owned())
.collect(),
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
//TODO: implement autocompletion like onAutocomplete in
// http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/webconsole.js
"autocomplete" => {
let msg = AutocompleteReply {
from: self.name(),
matches: vec!(),
matches: vec![],
matchProp: "".to_owned(),
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"evaluateJS" => {
let input = msg.get("text").unwrap().as_str().unwrap().to_owned();
let (chan, port) = ipc::channel().unwrap();
self.script_chan.send(DevtoolScriptControlMsg::EvaluateJS(
self.pipeline, input.clone(), chan)).unwrap();
self.script_chan
.send(DevtoolScriptControlMsg::EvaluateJS(
self.pipeline,
input.clone(),
chan,
)).unwrap();
//TODO: extract conversion into protocol module or some other useful place
let result = match port.recv().map_err(|_| ())? {
@ -184,12 +204,12 @@ impl Actor for ConsoleActor {
let mut m = Map::new();
m.insert("type".to_owned(), Value::String("undefined".to_owned()));
Value::Object(m)
}
},
NullValue => {
let mut m = Map::new();
m.insert("type".to_owned(), Value::String("null".to_owned()));
Value::Object(m)
}
},
BooleanValue(val) => Value::Bool(val),
NumberValue(val) => {
if val.is_nan() {
@ -211,7 +231,7 @@ impl Actor for ConsoleActor {
} else {
Value::Number(Number::from_f64(val).unwrap())
}
}
},
StringValue(s) => Value::String(s),
ActorValue { class, uuid } => {
//TODO: make initial ActorValue message include these properties?
@ -225,7 +245,7 @@ impl Actor for ConsoleActor {
m.insert("frozen".to_owned(), Value::Bool(false));
m.insert("sealed".to_owned(), Value::Bool(false));
Value::Object(m)
}
},
};
//TODO: catch and return exception values from JS evaluation
@ -240,7 +260,7 @@ impl Actor for ConsoleActor {
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"setPreferences" => {
let msg = SetPreferencesReply {
@ -249,9 +269,9 @@ impl Actor for ConsoleActor {
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
_ => ActorMessageStatus::Ignored
_ => ActorMessageStatus::Ignored,
})
}
}

View file

@ -26,21 +26,24 @@ impl Actor for FramerateActor {
self.name.clone()
}
fn handle_message(&self,
_registry: &ActorRegistry,
_msg_type: &str,
_msg: &Map<String, Value>,
_stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
_registry: &ActorRegistry,
_msg_type: &str,
_msg: &Map<String, Value>,
_stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(ActorMessageStatus::Ignored)
}
}
impl FramerateActor {
/// return name of actor
pub fn create(registry: &ActorRegistry,
pipeline_id: PipelineId,
script_sender: IpcSender<DevtoolScriptControlMsg>) -> String {
pub fn create(
registry: &ActorRegistry,
pipeline_id: PipelineId,
script_sender: IpcSender<DevtoolScriptControlMsg>,
) -> String {
let actor_name = registry.new_name("framerate");
let mut actor = FramerateActor {
name: actor_name.clone(),
@ -60,8 +63,7 @@ impl FramerateActor {
self.ticks.push(HighResolutionStamp::wrap(tick));
if self.is_recording {
let msg = DevtoolScriptControlMsg::RequestAnimationFrame(self.pipeline,
self.name());
let msg = DevtoolScriptControlMsg::RequestAnimationFrame(self.pipeline, self.name());
self.script_sender.send(msg).unwrap();
}
}
@ -78,8 +80,7 @@ impl FramerateActor {
self.start_time = Some(precise_time_ns());
self.is_recording = true;
let msg = DevtoolScriptControlMsg::RequestAnimationFrame(self.pipeline,
self.name());
let msg = DevtoolScriptControlMsg::RequestAnimationFrame(self.pipeline, self.name());
self.script_sender.send(msg).unwrap();
}
@ -90,7 +91,6 @@ impl FramerateActor {
self.is_recording = false;
self.start_time = None;
}
}
impl Drop for FramerateActor {

View file

@ -61,27 +61,25 @@ impl Actor for HighlighterActor {
self.name.clone()
}
fn handle_message(&self,
_registry: &ActorRegistry,
msg_type: &str,
_msg: &Map<String, Value>,
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
_registry: &ActorRegistry,
msg_type: &str,
_msg: &Map<String, Value>,
stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(match msg_type {
"showBoxModel" => {
let msg = ShowBoxModelReply {
from: self.name(),
};
let msg = ShowBoxModelReply { from: self.name() };
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"hideBoxModel" => {
let msg = HideBoxModelReply {
from: self.name(),
};
let msg = HideBoxModelReply { from: self.name() };
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
_ => ActorMessageStatus::Ignored,
})
@ -98,29 +96,33 @@ impl Actor for NodeActor {
self.name.clone()
}
fn handle_message(&self,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(match msg_type {
"modifyAttributes" => {
let target = msg.get("to").unwrap().as_str().unwrap();
let mods = msg.get("modifications").unwrap().as_array().unwrap();
let modifications = mods.iter().map(|json_mod| {
serde_json::from_str(&serde_json::to_string(json_mod).unwrap()).unwrap()
}).collect();
let modifications = mods
.iter()
.map(|json_mod| {
serde_json::from_str(&serde_json::to_string(json_mod).unwrap()).unwrap()
}).collect();
self.script_chan.send(ModifyAttribute(self.pipeline,
registry.actor_to_script(target.to_owned()),
modifications))
.unwrap();
let reply = ModifyAttributeReply {
from: self.name(),
};
self.script_chan
.send(ModifyAttribute(
self.pipeline,
registry.actor_to_script(target.to_owned()),
modifications,
)).unwrap();
let reply = ModifyAttributeReply { from: self.name() };
stream.write_json_packet(&reply);
ActorMessageStatus::Processed
}
},
_ => ActorMessageStatus::Ignored,
})
@ -175,19 +177,23 @@ struct NodeActorMsg {
}
trait NodeInfoToProtocol {
fn encode(self,
actors: &ActorRegistry,
display: bool,
script_chan: IpcSender<DevtoolScriptControlMsg>,
pipeline: PipelineId) -> NodeActorMsg;
fn encode(
self,
actors: &ActorRegistry,
display: bool,
script_chan: IpcSender<DevtoolScriptControlMsg>,
pipeline: PipelineId,
) -> NodeActorMsg;
}
impl NodeInfoToProtocol for NodeInfo {
fn encode(self,
actors: &ActorRegistry,
display: bool,
script_chan: IpcSender<DevtoolScriptControlMsg>,
pipeline: PipelineId) -> NodeActorMsg {
fn encode(
self,
actors: &ActorRegistry,
display: bool,
script_chan: IpcSender<DevtoolScriptControlMsg>,
pipeline: PipelineId,
) -> NodeActorMsg {
let actor_name = if !actors.script_actor_registered(self.uniqueId.clone()) {
let name = actors.new_name("node");
let node_actor = NodeActor {
@ -215,15 +221,16 @@ impl NodeInfoToProtocol for NodeInfo {
publicId: self.publicId,
systemId: self.systemId,
attrs: self.attrs.into_iter().map(|attr| {
AttrMsg {
attrs: self
.attrs
.into_iter()
.map(|attr| AttrMsg {
namespace: attr.namespace,
name: attr.name,
value: attr.value,
}
}).collect(),
}).collect(),
pseudoClassLocks: vec!(), //TODO get this data from script
pseudoClassLocks: vec![], //TODO get this data from script
isDisplayed: display,
@ -272,25 +279,28 @@ impl Actor for WalkerActor {
self.name.clone()
}
fn handle_message(&self,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(match msg_type {
"querySelector" => {
let msg = QuerySelectorReply {
from: self.name(),
};
let msg = QuerySelectorReply { from: self.name() };
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"documentElement" => {
let (tx, rx) = ipc::channel().unwrap();
self.script_chan.send(GetDocumentElement(self.pipeline, tx)).unwrap();
self.script_chan
.send(GetDocumentElement(self.pipeline, tx))
.unwrap();
let doc_elem_info = rx.recv().unwrap().ok_or(())?;
let node = doc_elem_info.encode(registry, true, self.script_chan.clone(), self.pipeline);
let node =
doc_elem_info.encode(registry, true, self.script_chan.clone(), self.pipeline);
let msg = DocumentElementReply {
from: self.name(),
@ -298,36 +308,38 @@ impl Actor for WalkerActor {
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"clearPseudoClassLocks" => {
let msg = ClearPseudoclassesReply {
from: self.name(),
};
let msg = ClearPseudoclassesReply { from: self.name() };
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"children" => {
let target = msg.get("node").unwrap().as_str().unwrap();
let (tx, rx) = ipc::channel().unwrap();
self.script_chan.send(GetChildren(self.pipeline,
registry.actor_to_script(target.to_owned()),
tx))
.unwrap();
self.script_chan
.send(GetChildren(
self.pipeline,
registry.actor_to_script(target.to_owned()),
tx,
)).unwrap();
let children = rx.recv().unwrap().ok_or(())?;
let msg = ChildrenReply {
hasFirst: true,
hasLast: true,
nodes: children.into_iter().map(|child| {
child.encode(registry, true, self.script_chan.clone(), self.pipeline)
}).collect(),
nodes: children
.into_iter()
.map(|child| {
child.encode(registry, true, self.script_chan.clone(), self.pipeline)
}).collect(),
from: self.name(),
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
_ => ActorMessageStatus::Ignored,
})
@ -447,52 +459,72 @@ impl Actor for PageStyleActor {
self.name.clone()
}
fn handle_message(&self,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(match msg_type {
"getApplied" => {
//TODO: query script for relevant applied styles to node (msg.node)
let msg = GetAppliedReply {
entries: vec!(),
rules: vec!(),
sheets: vec!(),
entries: vec![],
rules: vec![],
sheets: vec![],
from: self.name(),
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"getComputed" => {
//TODO: query script for relevant computed styles on node (msg.node)
let msg = GetComputedReply {
computed: vec!(),
computed: vec![],
from: self.name(),
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
//TODO: query script for box layout properties of node (msg.node)
"getLayout" => {
let target = msg.get("node").unwrap().as_str().unwrap();
let (tx, rx) = ipc::channel().unwrap();
self.script_chan.send(GetLayout(self.pipeline,
registry.actor_to_script(target.to_owned()),
tx))
.unwrap();
self.script_chan
.send(GetLayout(
self.pipeline,
registry.actor_to_script(target.to_owned()),
tx,
)).unwrap();
let ComputedNodeLayout {
display, position, zIndex, boxSizing,
autoMargins, marginTop, marginRight, marginBottom, marginLeft,
borderTopWidth, borderRightWidth, borderBottomWidth, borderLeftWidth,
paddingTop, paddingRight, paddingBottom, paddingLeft,
width, height,
display,
position,
zIndex,
boxSizing,
autoMargins,
marginTop,
marginRight,
marginBottom,
marginLeft,
borderTopWidth,
borderRightWidth,
borderBottomWidth,
borderLeftWidth,
paddingTop,
paddingRight,
paddingBottom,
paddingLeft,
width,
height,
} = rx.recv().unwrap().ok_or(())?;
let auto_margins = msg.get("autoMargins")
.and_then(&Value::as_bool).unwrap_or(false);
let auto_margins = msg
.get("autoMargins")
.and_then(&Value::as_bool)
.unwrap_or(false);
// http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/styles.js
let msg = GetLayoutReply {
@ -504,10 +536,18 @@ impl Actor for PageStyleActor {
autoMargins: if auto_margins {
let mut m = Map::new();
let auto = serde_json::value::Value::String("auto".to_owned());
if autoMargins.top { m.insert("top".to_owned(), auto.clone()); }
if autoMargins.right { m.insert("right".to_owned(), auto.clone()); }
if autoMargins.bottom { m.insert("bottom".to_owned(), auto.clone()); }
if autoMargins.left { m.insert("left".to_owned(), auto.clone()); }
if autoMargins.top {
m.insert("top".to_owned(), auto.clone());
}
if autoMargins.right {
m.insert("right".to_owned(), auto.clone());
}
if autoMargins.bottom {
m.insert("bottom".to_owned(), auto.clone());
}
if autoMargins.left {
m.insert("left".to_owned(), auto.clone());
}
serde_json::value::Value::Object(m)
} else {
serde_json::value::Value::Null
@ -531,7 +571,7 @@ impl Actor for PageStyleActor {
let msg = serde_json::from_str::<Value>(&msg).unwrap();
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
_ => ActorMessageStatus::Ignored,
})
@ -543,11 +583,13 @@ impl Actor for InspectorActor {
self.name.clone()
}
fn handle_message(&self,
registry: &ActorRegistry,
msg_type: &str,
_msg: &Map<String, Value>,
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
registry: &ActorRegistry,
msg_type: &str,
_msg: &Map<String, Value>,
stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(match msg_type {
"getWalker" => {
if self.walker.borrow().is_none() {
@ -562,21 +604,24 @@ impl Actor for InspectorActor {
}
let (tx, rx) = ipc::channel().unwrap();
self.script_chan.send(GetRootNode(self.pipeline, tx)).unwrap();
self.script_chan
.send(GetRootNode(self.pipeline, tx))
.unwrap();
let root_info = rx.recv().unwrap().ok_or(())?;
let node = root_info.encode(registry, false, self.script_chan.clone(), self.pipeline);
let node =
root_info.encode(registry, false, self.script_chan.clone(), self.pipeline);
let msg = GetWalkerReply {
from: self.name(),
walker: WalkerMsg {
actor: self.walker.borrow().clone().unwrap(),
root: node,
}
},
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"getPageStyle" => {
if self.pageStyle.borrow().is_none() {
@ -598,7 +643,7 @@ impl Actor for InspectorActor {
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
//TODO: this is an old message; try adding highlightable to the root traits instead
// and support getHighlighter instead
@ -621,7 +666,7 @@ impl Actor for InspectorActor {
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
_ => ActorMessageStatus::Ignored,
})

View file

@ -28,11 +28,13 @@ impl Actor for MemoryActor {
self.name.clone()
}
fn handle_message(&self,
_registry: &ActorRegistry,
_msg_type: &str,
_msg: &Map<String, Value>,
_stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
_registry: &ActorRegistry,
_msg_type: &str,
_msg: &Map<String, Value>,
_stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(ActorMessageStatus::Ignored)
}
}
@ -42,7 +44,7 @@ impl MemoryActor {
pub fn create(registry: &ActorRegistry) -> String {
let actor_name = registry.new_name("memory");
let actor = MemoryActor {
name: actor_name.clone()
name: actor_name.clone(),
};
registry.register_later(Box::new(actor));

View file

@ -34,7 +34,7 @@ struct HttpRequest {
struct HttpResponse {
headers: Option<Headers>,
status: Option<RawStatus>,
body: Option<Vec<u8>>
body: Option<Vec<u8>>,
}
pub struct NetworkEventActor {
@ -52,7 +52,7 @@ pub struct EventActor {
pub startedDateTime: String,
pub timeStamp: i64,
pub isXHR: bool,
pub private: bool
pub private: bool,
}
#[derive(Serialize)]
@ -79,14 +79,12 @@ pub struct ResponseContentMsg {
pub discardResponseBody: bool,
}
#[derive(Serialize)]
pub struct ResponseHeadersMsg {
pub headers: usize,
pub headersSize: usize,
}
#[derive(Serialize)]
pub struct RequestCookiesMsg {
pub cookies: usize,
@ -103,7 +101,7 @@ struct GetRequestHeadersReply {
from: String,
headers: Vec<Header>,
headerSize: usize,
rawHeaders: String
rawHeaders: String,
}
#[derive(Serialize)]
@ -117,7 +115,7 @@ struct GetResponseHeadersReply {
from: String,
headers: Vec<Header>,
headerSize: usize,
rawHeaders: String
rawHeaders: String,
}
#[derive(Serialize)]
@ -131,19 +129,19 @@ struct GetResponseContentReply {
struct GetRequestPostDataReply {
from: String,
postData: Option<Vec<u8>>,
postDataDiscarded: bool
postDataDiscarded: bool,
}
#[derive(Serialize)]
struct GetRequestCookiesReply {
from: String,
cookies: Vec<u8>
cookies: Vec<u8>,
}
#[derive(Serialize)]
struct GetResponseCookiesReply {
from: String,
cookies: Vec<u8>
cookies: Vec<u8>,
}
#[derive(Serialize)]
@ -179,11 +177,13 @@ impl Actor for NetworkEventActor {
self.name.clone()
}
fn handle_message(&self,
_registry: &ActorRegistry,
msg_type: &str,
_msg: &Map<String, Value>,
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
_registry: &ActorRegistry,
msg_type: &str,
_msg: &Map<String, Value>,
stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(match msg_type {
"getRequestHeaders" => {
let mut headers = Vec::new();
@ -194,7 +194,10 @@ impl Actor for NetworkEventActor {
let value = item.value_string();
rawHeadersString = rawHeadersString + name + ":" + &value + "\r\n";
headersSize += name.len() + value.len();
headers.push(Header { name: name.to_owned(), value: value.to_owned() });
headers.push(Header {
name: name.to_owned(),
value: value.to_owned(),
});
}
let msg = GetRequestHeadersReply {
from: self.name(),
@ -204,7 +207,7 @@ impl Actor for NetworkEventActor {
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"getRequestCookies" => {
let mut cookies = Vec::new();
if let Some(req_cookies) = self.request.headers.get_raw("Cookie") {
@ -221,7 +224,7 @@ impl Actor for NetworkEventActor {
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"getRequestPostData" => {
let msg = GetRequestPostDataReply {
from: self.name(),
@ -230,7 +233,7 @@ impl Actor for NetworkEventActor {
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"getResponseHeaders" => {
if let Some(ref response_headers) = self.response.headers {
let mut headers = vec![];
@ -258,7 +261,7 @@ impl Actor for NetworkEventActor {
stream.write_json_packet(&msg);
}
ActorMessageStatus::Processed
}
},
"getResponseCookies" => {
let mut cookies = Vec::new();
if let Some(res_cookies) = self.request.headers.get_raw("set-cookie") {
@ -275,7 +278,7 @@ impl Actor for NetworkEventActor {
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"getResponseContent" => {
let msg = GetResponseContentReply {
from: self.name(),
@ -284,7 +287,7 @@ impl Actor for NetworkEventActor {
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"getEventTimings" => {
// TODO: This is a fake timings msg
let timingsObj = Timings {
@ -304,19 +307,19 @@ impl Actor for NetworkEventActor {
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"getSecurityInfo" => {
// TODO: Send the correct values for securityInfo.
let msg = GetSecurityInfoReply {
from: self.name(),
securityInfo: SecurityInfo {
state: "insecure".to_owned()
state: "insecure".to_owned(),
},
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
_ => ActorMessageStatus::Ignored
},
_ => ActorMessageStatus::Ignored,
})
}
}
@ -382,8 +385,13 @@ impl NetworkEventActor {
// TODO: Send the correct values for all these fields.
let hSizeOption = self.response.headers.as_ref().map(|headers| headers.len());
let hSize = hSizeOption.unwrap_or(0);
let (status_code, status_message) = self.response.status.as_ref().
map_or((0, "".to_owned()), |&RawStatus(ref code, ref text)| (*code, text.clone().into_owned()));
let (status_code, status_message) = self
.response
.status
.as_ref()
.map_or((0, "".to_owned()), |&RawStatus(ref code, ref text)| {
(*code, text.clone().into_owned())
});
// TODO: Send the correct values for remoteAddress and remotePort and http_version.
ResponseStartMsg {
httpVersion: "HTTP/1.1".to_owned(),
@ -392,7 +400,7 @@ impl NetworkEventActor {
status: status_code.to_string(),
statusText: status_message,
headersSize: hSize,
discardResponseBody: false
discardResponseBody: false,
}
}
@ -401,7 +409,7 @@ impl NetworkEventActor {
if let Some(ref headers) = self.response.headers {
mString = match headers.get() {
Some(&ContentType(ref mime)) => mime.to_string(),
None => "".to_owned()
None => "".to_owned(),
};
}
// TODO: Set correct values when response's body is sent to the devtools in http_loader.
@ -418,7 +426,7 @@ impl NetworkEventActor {
if let Some(ref headers) = self.response.headers {
cookies_size = match headers.get() {
Some(&Cookie(ref cookie)) => cookie.len(),
None => 0
None => 0,
};
}
ResponseCookiesMsg {
@ -431,10 +439,9 @@ impl NetworkEventActor {
let mut headers_byte_count = 0;
if let Some(ref headers) = self.response.headers {
headers_size = headers.len();
for item in headers.iter() {
for item in headers.iter() {
headers_byte_count += item.name().len() + item.value_string().len();
}
}
ResponseHeadersMsg {
headers: headers_size,
@ -443,10 +450,11 @@ impl NetworkEventActor {
}
pub fn request_headers(&self) -> RequestHeadersMsg {
let size = self.request
.headers
.iter()
.fold(0, |acc, h| acc + h.name().len() + h.value_string().len());
let size = self
.request
.headers
.iter()
.fold(0, |acc, h| acc + h.name().len() + h.value_string().len());
RequestHeadersMsg {
headers: self.request.headers.len(),
headersSize: size,
@ -456,7 +464,7 @@ impl NetworkEventActor {
pub fn request_cookies(&self) -> RequestCookiesMsg {
let cookies_size = match self.request.headers.get() {
Some(&Cookie(ref cookie)) => cookie.len(),
None => 0
None => 0,
};
RequestCookiesMsg {
cookies: cookies_size,

View file

@ -15,11 +15,13 @@ impl Actor for ObjectActor {
fn name(&self) -> String {
self.name.clone()
}
fn handle_message(&self,
_: &ActorRegistry,
_: &str,
_: &Map<String, Value>,
_: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
_: &ActorRegistry,
_: &str,
_: &Map<String, Value>,
_: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(ActorMessageStatus::Ignored)
}
}

View file

@ -51,11 +51,13 @@ impl Actor for PerformanceActor {
self.name.clone()
}
fn handle_message(&self,
_registry: &ActorRegistry,
msg_type: &str,
_msg: &Map<String, Value>,
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
_registry: &ActorRegistry,
msg_type: &str,
_msg: &Map<String, Value>,
stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(match msg_type {
"connect" => {
let msg = ConnectReply {
@ -79,11 +81,11 @@ impl Actor for PerformanceActor {
value: SuccessMsg {
success: true,
errors: vec![],
}
},
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
_ => ActorMessageStatus::Ignored,
})
}
@ -91,28 +93,34 @@ impl Actor for PerformanceActor {
impl PerformanceActor {
pub fn new(name: String) -> PerformanceActor {
PerformanceActor {
name: name,
}
PerformanceActor { name: name }
}
pub fn description() -> ActorDescription {
ActorDescription {
category: "actor",
typeName: "performance",
methods: vec![
Method {
name: "canCurrentlyRecord",
request: Value::Object(vec![
("type".to_owned(), Value::String("canCurrentlyRecord".to_owned())),
].into_iter().collect()),
response: Value::Object(vec![
("value".to_owned(), Value::Object(vec![
("_retval".to_owned(), Value::String("json".to_owned())),
].into_iter().collect())),
].into_iter().collect()),
},
],
methods: vec![Method {
name: "canCurrentlyRecord",
request: Value::Object(
vec![(
"type".to_owned(),
Value::String("canCurrentlyRecord".to_owned()),
)].into_iter()
.collect(),
),
response: Value::Object(
vec![(
"value".to_owned(),
Value::Object(
vec![("_retval".to_owned(), Value::String("json".to_owned()))]
.into_iter()
.collect(),
),
)].into_iter()
.collect(),
),
}],
}
}
}

View file

@ -15,19 +15,19 @@ impl Actor for ProfilerActor {
self.name.clone()
}
fn handle_message(&self,
_registry: &ActorRegistry,
_msg_type: &str,
_msg: &Map<String, Value>,
_stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
_registry: &ActorRegistry,
_msg_type: &str,
_msg: &Map<String, Value>,
_stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(ActorMessageStatus::Ignored)
}
}
impl ProfilerActor {
pub fn new(name: String) -> ProfilerActor {
ProfilerActor {
name: name,
}
ProfilerActor { name: name }
}
}

View file

@ -6,7 +6,6 @@
/// (http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/root.js).
/// Connection point for all new remote devtools interactions, providing lists of know actors
/// that perform more specific actions (tabs, addons, browser chrome, etc.)
use actor::{Actor, ActorMessageStatus, ActorRegistry};
use actors::performance::PerformanceActor;
use actors::tab::{TabActor, TabActorMsg};
@ -65,11 +64,13 @@ impl Actor for RootActor {
"root".to_owned()
}
fn handle_message(&self,
registry: &ActorRegistry,
msg_type: &str,
_msg: &Map<String, Value>,
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
registry: &ActorRegistry,
msg_type: &str,
_msg: &Map<String, Value>,
stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(match msg_type {
"listAddons" => {
let actor = ListAddonsReply {
@ -78,20 +79,22 @@ impl Actor for RootActor {
};
stream.write_json_packet(&actor);
ActorMessageStatus::Processed
}
},
//https://wiki.mozilla.org/Remote_Debugging_Protocol#Listing_Browser_Tabs
"listTabs" => {
let actor = ListTabsReply {
from: "root".to_owned(),
selected: 0,
tabs: self.tabs.iter().map(|tab| {
registry.find::<TabActor>(tab).encodable()
}).collect()
tabs: self
.tabs
.iter()
.map(|tab| registry.find::<TabActor>(tab).encodable())
.collect(),
};
stream.write_json_packet(&actor);
ActorMessageStatus::Processed
}
},
"protocolDescription" => {
let msg = ProtocolDescriptionReply {
@ -102,9 +105,9 @@ impl Actor for RootActor {
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
_ => ActorMessageStatus::Ignored
_ => ActorMessageStatus::Ignored,
})
}
}
@ -118,7 +121,7 @@ impl RootActor {
sources: true,
highlightable: true,
customHighlighters: true,
networkMonitor: true
networkMonitor: true,
},
}
}

View file

@ -37,7 +37,7 @@ struct TabDetachedReply {
#[derive(Serialize)]
struct ReconfigureReply {
from: String
from: String,
}
#[derive(Serialize)]
@ -84,25 +84,28 @@ impl Actor for TabActor {
self.name.clone()
}
fn handle_message(&self,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(match msg_type {
"reconfigure" => {
if let Some(options) = msg.get("options").and_then(|o| o.as_object()) {
if let Some(val) = options.get("performReload") {
if val.as_bool().unwrap_or(false) {
let console_actor = registry.find::<ConsoleActor>(&self.console);
let _ = console_actor.script_chan.send(
DevtoolScriptControlMsg::Reload(console_actor.pipeline));
let _ = console_actor
.script_chan
.send(DevtoolScriptControlMsg::Reload(console_actor.pipeline));
}
}
}
stream.write_json_packet(&ReconfigureReply { from: self.name() });
ActorMessageStatus::Processed
}
},
// https://wiki.mozilla.org/Remote_Debugging_Protocol#Listing_Browser_Tabs
// (see "To attach to a _tabActor_")
@ -116,12 +119,17 @@ impl Actor for TabActor {
traits: TabTraits,
};
let console_actor = registry.find::<ConsoleActor>(&self.console);
console_actor.streams.borrow_mut().push(stream.try_clone().unwrap());
console_actor
.streams
.borrow_mut()
.push(stream.try_clone().unwrap());
stream.write_json_packet(&msg);
console_actor.script_chan.send(
WantsLiveNotifications(console_actor.pipeline, true)).unwrap();
console_actor
.script_chan
.send(WantsLiveNotifications(console_actor.pipeline, true))
.unwrap();
ActorMessageStatus::Processed
}
},
//FIXME: The current implementation won't work for multiple connections. Need to ensure 105
// that the correct stream is removed.
@ -133,21 +141,23 @@ impl Actor for TabActor {
let console_actor = registry.find::<ConsoleActor>(&self.console);
console_actor.streams.borrow_mut().pop();
stream.write_json_packet(&msg);
console_actor.script_chan.send(
WantsLiveNotifications(console_actor.pipeline, false)).unwrap();
console_actor
.script_chan
.send(WantsLiveNotifications(console_actor.pipeline, false))
.unwrap();
ActorMessageStatus::Processed
}
},
"listFrames" => {
let msg = ListFramesReply {
from: self.name(),
frames: vec!(),
frames: vec![],
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
_ => ActorMessageStatus::Ignored
_ => ActorMessageStatus::Ignored,
})
}
}

View file

@ -35,7 +35,7 @@ struct ThreadResumedReply {
#[derive(Serialize)]
struct ReconfigureReply {
from: String
from: String,
}
#[derive(Serialize)]
@ -53,9 +53,7 @@ pub struct ThreadActor {
impl ThreadActor {
pub fn new(name: String) -> ThreadActor {
ThreadActor {
name: name,
}
ThreadActor { name: name }
}
}
@ -64,11 +62,13 @@ impl Actor for ThreadActor {
self.name.clone()
}
fn handle_message(&self,
registry: &ActorRegistry,
msg_type: &str,
_msg: &Map<String, Value>,
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
registry: &ActorRegistry,
msg_type: &str,
_msg: &Map<String, Value>,
stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(match msg_type {
"attach" => {
let msg = ThreadAttachedReply {
@ -76,7 +76,9 @@ impl Actor for ThreadActor {
type_: "paused".to_owned(),
actor: registry.new_name("pause"),
poppedFrames: vec![],
why: WhyMsg { type_: "attached".to_owned() },
why: WhyMsg {
type_: "attached".to_owned(),
},
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
@ -94,7 +96,7 @@ impl Actor for ThreadActor {
"reconfigure" => {
stream.write_json_packet(&ReconfigureReply { from: self.name() });
ActorMessageStatus::Processed
}
},
"sources" => {
let msg = SourcesReply {
@ -103,7 +105,7 @@ impl Actor for ThreadActor {
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
_ => ActorMessageStatus::Ignored,
})

View file

@ -44,7 +44,7 @@ struct Emitter {
#[derive(Serialize)]
struct IsRecordingReply {
from: String,
value: bool
value: bool,
}
#[derive(Serialize)]
@ -103,8 +103,10 @@ pub struct HighResolutionStamp(f64);
impl HighResolutionStamp {
pub fn new(start_stamp: PreciseTime, time: PreciseTime) -> HighResolutionStamp {
let duration = start_stamp.to(time).num_microseconds()
.expect("Too big duration in microseconds");
let duration = start_stamp
.to(time)
.num_microseconds()
.expect("Too big duration in microseconds");
HighResolutionStamp(duration as f64 / 1000 as f64)
}
@ -122,11 +124,12 @@ impl Serialize for HighResolutionStamp {
static DEFAULT_TIMELINE_DATA_PULL_TIMEOUT: u64 = 200; //ms
impl TimelineActor {
pub fn new(name: String,
pipeline: PipelineId,
script_sender: IpcSender<DevtoolScriptControlMsg>) -> TimelineActor {
let marker_types = vec!(TimelineMarkerType::Reflow,
TimelineMarkerType::DOMEvent);
pub fn new(
name: String,
pipeline: PipelineId,
script_sender: IpcSender<DevtoolScriptControlMsg>,
) -> TimelineActor {
let marker_types = vec![TimelineMarkerType::Reflow, TimelineMarkerType::DOMEvent];
TimelineActor {
name: name,
@ -141,15 +144,20 @@ impl TimelineActor {
}
}
fn pull_timeline_data(&self, receiver: IpcReceiver<Option<TimelineMarker>>, mut emitter: Emitter) {
fn pull_timeline_data(
&self,
receiver: IpcReceiver<Option<TimelineMarker>>,
mut emitter: Emitter,
) {
let is_recording = self.is_recording.clone();
if !*is_recording.lock().unwrap() {
return;
}
thread::Builder::new().name("PullTimelineMarkers".to_owned()).spawn(move || {
loop {
thread::Builder::new()
.name("PullTimelineMarkers".to_owned())
.spawn(move || loop {
if !*is_recording.lock().unwrap() {
break;
}
@ -161,8 +169,7 @@ impl TimelineActor {
emitter.send(markers);
thread::sleep(Duration::from_millis(DEFAULT_TIMELINE_DATA_PULL_TIMEOUT));
}
}).expect("Thread spawning failed");
}).expect("Thread spawning failed");
}
}
@ -171,19 +178,24 @@ impl Actor for TimelineActor {
self.name.clone()
}
fn handle_message(&self,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(match msg_type {
"start" => {
**self.is_recording.lock().as_mut().unwrap() = true;
let (tx, rx) = ipc::channel::<Option<TimelineMarker>>().unwrap();
self.script_sender.send(SetTimelineMarkers(self.pipeline,
self.marker_types.clone(),
tx)).unwrap();
self.script_sender
.send(SetTimelineMarkers(
self.pipeline,
self.marker_types.clone(),
tx,
)).unwrap();
*self.stream.borrow_mut() = stream.try_clone().ok();
@ -198,18 +210,22 @@ impl Actor for TimelineActor {
if let Some(with_ticks) = msg.get("withTicks") {
if let Some(true) = with_ticks.as_bool() {
let framerate_actor = Some(FramerateActor::create(
registry,
self.pipeline.clone(),
self.script_sender.clone()));
registry,
self.pipeline.clone(),
self.script_sender.clone(),
));
*self.framerate_actor.borrow_mut() = framerate_actor;
}
}
let emitter = Emitter::new(self.name(), registry.shareable(),
registry.start_stamp(),
stream.try_clone().unwrap(),
self.memory_actor.borrow().clone(),
self.framerate_actor.borrow().clone());
let emitter = Emitter::new(
self.name(),
registry.shareable(),
registry.start_stamp(),
stream.try_clone().unwrap(),
self.memory_actor.borrow().clone(),
self.framerate_actor.borrow().clone(),
);
self.pull_timeline_data(rx, emitter);
@ -219,7 +235,7 @@ impl Actor for TimelineActor {
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
"stop" => {
let msg = StopReply {
@ -228,7 +244,11 @@ impl Actor for TimelineActor {
};
stream.write_json_packet(&msg);
self.script_sender.send(DropTimelineMarkers(self.pipeline, self.marker_types.clone())).unwrap();
self.script_sender
.send(DropTimelineMarkers(
self.pipeline,
self.marker_types.clone(),
)).unwrap();
if let Some(ref actor_name) = *self.framerate_actor.borrow() {
registry.drop_actor_later(actor_name.clone());
@ -241,32 +261,32 @@ impl Actor for TimelineActor {
**self.is_recording.lock().as_mut().unwrap() = false;
self.stream.borrow_mut().take();
ActorMessageStatus::Processed
}
},
"isRecording" => {
let msg = IsRecordingReply {
from: self.name(),
value: self.is_recording.lock().unwrap().clone()
value: self.is_recording.lock().unwrap().clone(),
};
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
},
_ => {
ActorMessageStatus::Ignored
}
_ => ActorMessageStatus::Ignored,
})
}
}
impl Emitter {
pub fn new(name: String,
registry: Arc<Mutex<ActorRegistry>>,
start_stamp: PreciseTime,
stream: TcpStream,
memory_actor_name: Option<String>,
framerate_actor_name: Option<String>) -> Emitter {
pub fn new(
name: String,
registry: Arc<Mutex<ActorRegistry>>,
start_stamp: PreciseTime,
stream: TcpStream,
memory_actor_name: Option<String>,
framerate_actor_name: Option<String>,
) -> Emitter {
Emitter {
from: name,
stream: stream,

View file

@ -17,11 +17,13 @@ impl Actor for WorkerActor {
fn name(&self) -> String {
self.name.clone()
}
fn handle_message(&self,
_: &ActorRegistry,
_: &str,
_: &Map<String, Value>,
_: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
fn handle_message(
&self,
_: &ActorRegistry,
_: &str,
_: &Map<String, Value>,
_: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(ActorMessageStatus::Processed)
}
}

View file

@ -9,7 +9,6 @@
#![crate_name = "devtools"]
#![crate_type = "rlib"]
#![allow(non_snake_case)]
#![deny(unsafe_code)]
@ -19,7 +18,8 @@ extern crate ipc_channel;
#[macro_use]
extern crate log;
extern crate msg;
#[macro_use] extern crate serde;
#[macro_use]
extern crate serde;
extern crate serde_json;
extern crate time;
@ -128,23 +128,24 @@ pub fn start_server(port: u16) -> Sender<DevtoolsControlMsg> {
let (sender, receiver) = channel();
{
let sender = sender.clone();
thread::Builder::new().name("Devtools".to_owned()).spawn(move || {
run_server(sender, receiver, port)
}).expect("Thread spawning failed");
thread::Builder::new()
.name("Devtools".to_owned())
.spawn(move || run_server(sender, receiver, port))
.expect("Thread spawning failed");
}
sender
}
fn run_server(sender: Sender<DevtoolsControlMsg>,
receiver: Receiver<DevtoolsControlMsg>,
port: u16) {
fn run_server(
sender: Sender<DevtoolsControlMsg>,
receiver: Receiver<DevtoolsControlMsg>,
port: u16,
) {
let listener = TcpListener::bind(&("127.0.0.1", port)).unwrap();
let mut registry = ActorRegistry::new();
let root = Box::new(RootActor {
tabs: vec!(),
});
let root = Box::new(RootActor { tabs: vec![] });
registry.register(root);
registry.find::<RootActor>("root");
@ -158,7 +159,6 @@ fn run_server(sender: Sender<DevtoolsControlMsg>,
let mut actor_workers: HashMap<(PipelineId, WorkerId), String> = HashMap::new();
/// Process the input from a single devtools client until EOF.
fn handle_client(actors: Arc<Mutex<ActorRegistry>>, mut stream: TcpStream) {
debug!("connection established to {}", stream.peer_addr().unwrap());
@ -171,21 +171,24 @@ fn run_server(sender: Sender<DevtoolsControlMsg>,
'outer: loop {
match stream.read_json_packet() {
Ok(Some(json_packet)) => {
if let Err(()) = actors.lock().unwrap().handle_message(json_packet.as_object().unwrap(),
&mut stream) {
if let Err(()) = actors
.lock()
.unwrap()
.handle_message(json_packet.as_object().unwrap(), &mut stream)
{
debug!("error: devtools actor stopped responding");
let _ = stream.shutdown(Shutdown::Both);
break 'outer
break 'outer;
}
}
},
Ok(None) => {
debug!("error: EOF");
break 'outer
}
break 'outer;
},
Err(err_msg) => {
debug!("error: {}", err_msg);
break 'outer
}
break 'outer;
},
}
}
}
@ -199,12 +202,14 @@ fn run_server(sender: Sender<DevtoolsControlMsg>,
// We need separate actor representations for each script global that exists;
// clients can theoretically connect to multiple globals simultaneously.
// TODO: move this into the root or tab modules?
fn handle_new_global(actors: Arc<Mutex<ActorRegistry>>,
ids: (PipelineId, Option<WorkerId>),
script_sender: IpcSender<DevtoolScriptControlMsg>,
actor_pipelines: &mut HashMap<PipelineId, String>,
actor_workers: &mut HashMap<(PipelineId, WorkerId), String>,
page_info: DevtoolsPageInfo) {
fn handle_new_global(
actors: Arc<Mutex<ActorRegistry>>,
ids: (PipelineId, Option<WorkerId>),
script_sender: IpcSender<DevtoolScriptControlMsg>,
actor_pipelines: &mut HashMap<PipelineId, String>,
actor_workers: &mut HashMap<(PipelineId, WorkerId), String>,
page_info: DevtoolsPageInfo,
) {
let mut actors = actors.lock().unwrap();
let (pipeline, worker_id) = ids;
@ -226,9 +231,7 @@ fn run_server(sender: Sender<DevtoolsControlMsg>,
pipeline: pipeline,
};
let timeline = TimelineActor::new(actors.new_name("timeline"),
pipeline,
script_sender);
let timeline = TimelineActor::new(actors.new_name("timeline"), pipeline, script_sender);
let profiler = ProfilerActor::new(actors.new_name("profiler"));
let performance = PerformanceActor::new(actors.new_name("performance"));
@ -251,7 +254,15 @@ fn run_server(sender: Sender<DevtoolsControlMsg>,
let root = actors.find_mut::<RootActor>("root");
root.tabs.push(tab.name.clone());
(tab, console, inspector, timeline, profiler, performance, thread)
(
tab,
console,
inspector,
timeline,
profiler,
performance,
thread,
)
};
if let Some(id) = worker_id {
@ -274,14 +285,21 @@ fn run_server(sender: Sender<DevtoolsControlMsg>,
actors.register(Box::new(thread));
}
fn handle_console_message(actors: Arc<Mutex<ActorRegistry>>,
id: PipelineId,
worker_id: Option<WorkerId>,
console_message: ConsoleMessage,
actor_pipelines: &HashMap<PipelineId, String>,
actor_workers: &HashMap<(PipelineId, WorkerId), String>) {
let console_actor_name = match find_console_actor(actors.clone(), id, worker_id, actor_workers,
actor_pipelines) {
fn handle_console_message(
actors: Arc<Mutex<ActorRegistry>>,
id: PipelineId,
worker_id: Option<WorkerId>,
console_message: ConsoleMessage,
actor_pipelines: &HashMap<PipelineId, String>,
actor_workers: &HashMap<(PipelineId, WorkerId), String>,
) {
let console_actor_name = match find_console_actor(
actors.clone(),
id,
worker_id,
actor_workers,
actor_pipelines,
) {
Some(name) => name,
None => return,
};
@ -296,10 +314,10 @@ fn run_server(sender: Sender<DevtoolsControlMsg>,
LogLevel::Info => "info",
LogLevel::Warn => "warn",
LogLevel::Error => "error",
_ => "log"
_ => "log",
}.to_owned(),
timeStamp: precise_time_ns(),
arguments: vec!(console_message.message),
arguments: vec![console_message.message],
filename: console_message.filename,
lineNumber: console_message.lineNumber,
columnNumber: console_message.columnNumber,
@ -310,11 +328,13 @@ fn run_server(sender: Sender<DevtoolsControlMsg>,
}
}
fn find_console_actor(actors: Arc<Mutex<ActorRegistry>>,
id: PipelineId,
worker_id: Option<WorkerId>,
actor_workers: &HashMap<(PipelineId, WorkerId), String>,
actor_pipelines: &HashMap<PipelineId, String>) -> Option<String> {
fn find_console_actor(
actors: Arc<Mutex<ActorRegistry>>,
id: PipelineId,
worker_id: Option<WorkerId>,
actor_workers: &HashMap<(PipelineId, WorkerId), String>,
actor_pipelines: &HashMap<PipelineId, String>,
) -> Option<String> {
let actors = actors.lock().unwrap();
if let Some(worker_id) = worker_id {
let actor_name = (*actor_workers).get(&(id, worker_id))?;
@ -325,20 +345,28 @@ fn run_server(sender: Sender<DevtoolsControlMsg>,
}
}
fn handle_network_event(actors: Arc<Mutex<ActorRegistry>>,
mut connections: Vec<TcpStream>,
actor_pipelines: &HashMap<PipelineId, String>,
actor_requests: &mut HashMap<String, String>,
actor_workers: &HashMap<(PipelineId, WorkerId), String>,
pipeline_id: PipelineId,
request_id: String,
network_event: NetworkEvent) {
let console_actor_name = match find_console_actor(actors.clone(), pipeline_id, None,
actor_workers, actor_pipelines) {
fn handle_network_event(
actors: Arc<Mutex<ActorRegistry>>,
mut connections: Vec<TcpStream>,
actor_pipelines: &HashMap<PipelineId, String>,
actor_requests: &mut HashMap<String, String>,
actor_workers: &HashMap<(PipelineId, WorkerId), String>,
pipeline_id: PipelineId,
request_id: String,
network_event: NetworkEvent,
) {
let console_actor_name = match find_console_actor(
actors.clone(),
pipeline_id,
None,
actor_workers,
actor_pipelines,
) {
Some(name) => name,
None => return,
};
let netevent_actor_name = find_network_event_actor(actors.clone(), actor_requests, request_id.clone());
let netevent_actor_name =
find_network_event_actor(actors.clone(), actor_requests, request_id.clone());
let mut actors = actors.lock().unwrap();
let actor = actors.find_mut::<NetworkEventActor>(&netevent_actor_name);
@ -356,8 +384,7 @@ fn run_server(sender: Sender<DevtoolsControlMsg>,
for stream in &mut connections {
stream.write_json_packet(&msg);
}
}
},
NetworkEvent::HttpResponse(httpresponse) => {
//Store the response information in the actor
actor.add_response(httpresponse);
@ -385,7 +412,7 @@ fn run_server(sender: Sender<DevtoolsControlMsg>,
from: netevent_actor_name.clone(),
type_: "networkEventUpdate".to_owned(),
updateType: "responseStart".to_owned(),
response: actor.response_start()
response: actor.response_start(),
};
for stream in &mut connections {
@ -441,78 +468,109 @@ fn run_server(sender: Sender<DevtoolsControlMsg>,
for stream in &mut connections {
stream.write_merged_json_packet(&msg, &actor.response_headers());
}
}
},
}
}
// Find the name of NetworkEventActor corresponding to request_id
// Create a new one if it does not exist, add it to the actor_requests hashmap
fn find_network_event_actor(actors: Arc<Mutex<ActorRegistry>>,
actor_requests: &mut HashMap<String, String>,
request_id: String) -> String {
fn find_network_event_actor(
actors: Arc<Mutex<ActorRegistry>>,
actor_requests: &mut HashMap<String, String>,
request_id: String,
) -> String {
let mut actors = actors.lock().unwrap();
match (*actor_requests).entry(request_id) {
Occupied(name) => {
//TODO: Delete from map like Firefox does?
name.into_mut().clone()
}
},
Vacant(entry) => {
let actor_name = actors.new_name("netevent");
let actor = NetworkEventActor::new(actor_name.clone());
entry.insert(actor_name.clone());
actors.register(Box::new(actor));
actor_name
}
},
}
}
let sender_clone = sender.clone();
thread::Builder::new().name("DevtoolsClientAcceptor".to_owned()).spawn(move || {
// accept connections and process them, spawning a new thread for each one
for stream in listener.incoming() {
// connection succeeded
sender_clone.send(DevtoolsControlMsg::FromChrome(
ChromeToDevtoolsControlMsg::AddClient(stream.unwrap()))).unwrap();
}
}).expect("Thread spawning failed");
thread::Builder::new()
.name("DevtoolsClientAcceptor".to_owned())
.spawn(move || {
// accept connections and process them, spawning a new thread for each one
for stream in listener.incoming() {
// connection succeeded
sender_clone
.send(DevtoolsControlMsg::FromChrome(
ChromeToDevtoolsControlMsg::AddClient(stream.unwrap()),
)).unwrap();
}
}).expect("Thread spawning failed");
while let Ok(msg) = receiver.recv() {
match msg {
DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::AddClient(stream)) => {
let actors = actors.clone();
accepted_connections.push(stream.try_clone().unwrap());
thread::Builder::new().name("DevtoolsClientHandler".to_owned()).spawn(move || {
handle_client(actors, stream.try_clone().unwrap())
}).expect("Thread spawning failed");
}
thread::Builder::new()
.name("DevtoolsClientHandler".to_owned())
.spawn(move || handle_client(actors, stream.try_clone().unwrap()))
.expect("Thread spawning failed");
},
DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::FramerateTick(
actor_name, tick)) =>
handle_framerate_tick(actors.clone(), actor_name, tick),
actor_name,
tick,
)) => handle_framerate_tick(actors.clone(), actor_name, tick),
DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::NewGlobal(
ids, script_sender, pageinfo)) =>
handle_new_global(actors.clone(), ids, script_sender, &mut actor_pipelines,
&mut actor_workers, pageinfo),
ids,
script_sender,
pageinfo,
)) => handle_new_global(
actors.clone(),
ids,
script_sender,
&mut actor_pipelines,
&mut actor_workers,
pageinfo,
),
DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ConsoleAPI(
id,
console_message,
worker_id)) =>
handle_console_message(actors.clone(), id, worker_id, console_message,
&actor_pipelines, &actor_workers),
id,
console_message,
worker_id,
)) => handle_console_message(
actors.clone(),
id,
worker_id,
console_message,
&actor_pipelines,
&actor_workers,
),
DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportCSSError(
id,
css_error)) => {
let console_message = ConsoleMessage {
id,
css_error,
)) => {
let console_message = ConsoleMessage {
message: css_error.msg,
logLevel: LogLevel::Warn,
filename: css_error.filename,
lineNumber: css_error.line as usize,
columnNumber: css_error.column as usize,
};
handle_console_message(actors.clone(), id, None, console_message,
&actor_pipelines, &actor_workers)
handle_console_message(
actors.clone(),
id,
None,
console_message,
&actor_pipelines,
&actor_workers,
)
},
DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::NetworkEvent(
request_id, network_event)) => {
request_id,
network_event,
)) => {
// copy the accepted_connections vector
let mut connections = Vec::<TcpStream>::new();
for stream in &accepted_connections {
@ -523,10 +581,18 @@ fn run_server(sender: Sender<DevtoolsControlMsg>,
NetworkEvent::HttpResponse(ref response) => response.pipeline_id,
NetworkEvent::HttpRequest(ref request) => request.pipeline_id,
};
handle_network_event(actors.clone(), connections, &actor_pipelines, &mut actor_requests,
&actor_workers, pipeline_id, request_id, network_event);
handle_network_event(
actors.clone(),
connections,
&actor_pipelines,
&mut actor_requests,
&actor_workers,
pipeline_id,
request_id,
network_event,
);
},
DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::ServerExitMsg) => break
DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::ServerExitMsg) => break,
}
}
for connection in &mut accepted_connections {

View file

@ -55,11 +55,11 @@ impl JsonPacketStream for TcpStream {
fn read_json_packet(&mut self) -> Result<Option<Value>, String> {
// https://wiki.mozilla.org/Remote_Debugging_Protocol_Stream_Transport
// In short, each JSON packet is [ascii length]:[JSON data of given length]
let mut buffer = vec!();
let mut buffer = vec![];
loop {
let mut buf = [0];
let byte = match self.read(&mut buf) {
Ok(0) => return Ok(None), // EOF
Ok(0) => return Ok(None), // EOF
Ok(1) => buf[0],
Ok(_) => unreachable!(),
Err(e) => return Err(e.description().to_owned()),

View file

@ -8,7 +8,6 @@
#![crate_name = "devtools_traits"]
#![crate_type = "rlib"]
#![allow(non_snake_case)]
#![deny(unsafe_code)]
@ -17,9 +16,11 @@ extern crate bitflags;
extern crate hyper;
extern crate ipc_channel;
extern crate malloc_size_of;
#[macro_use] extern crate malloc_size_of_derive;
#[macro_use]
extern crate malloc_size_of_derive;
extern crate msg;
#[macro_use] extern crate serde;
#[macro_use]
extern crate serde;
extern crate servo_url;
extern crate time;
@ -45,7 +46,7 @@ pub struct CSSError {
pub filename: String,
pub line: u32,
pub column: u32,
pub msg: String
pub msg: String,
}
/// Messages to instruct the devtools server to update its known actors/state
@ -75,9 +76,11 @@ pub enum ChromeToDevtoolsControlMsg {
pub enum ScriptToDevtoolsControlMsg {
/// A new global object was created, associated with a particular pipeline.
/// The means of communicating directly with it are provided.
NewGlobal((PipelineId, Option<WorkerId>),
IpcSender<DevtoolScriptControlMsg>,
DevtoolsPageInfo),
NewGlobal(
(PipelineId, Option<WorkerId>),
IpcSender<DevtoolScriptControlMsg>,
DevtoolsPageInfo,
),
/// A particular page has invoked the console API.
ConsoleAPI(PipelineId, ConsoleMessage, Option<WorkerId>),
/// An animation frame with the given timestamp was processed in a script thread.
@ -201,13 +204,21 @@ pub enum DevtoolScriptControlMsg {
/// Retrieve the computed layout properties of the given node in the given pipeline.
GetLayout(PipelineId, String, IpcSender<Option<ComputedNodeLayout>>),
/// Retrieve all stored console messages for the given pipeline.
GetCachedMessages(PipelineId, CachedConsoleMessageTypes, IpcSender<Vec<CachedConsoleMessage>>),
GetCachedMessages(
PipelineId,
CachedConsoleMessageTypes,
IpcSender<Vec<CachedConsoleMessage>>,
),
/// Update a given node's attributes with a list of modifications.
ModifyAttribute(PipelineId, String, Vec<Modification>),
/// Request live console messages for a given pipeline (true if desired, false otherwise).
WantsLiveNotifications(PipelineId, bool),
/// Request live notifications for a given set of timeline events for a given pipeline.
SetTimelineMarkers(PipelineId, Vec<TimelineMarkerType>, IpcSender<Option<TimelineMarker>>),
SetTimelineMarkers(
PipelineId,
Vec<TimelineMarkerType>,
IpcSender<Option<TimelineMarker>>,
),
/// Withdraw request for live timeline notifications for a given pipeline.
DropTimelineMarkers(PipelineId, Vec<TimelineMarkerType>),
/// Request a callback directed at the given actor name from the next animation frame

View file

@ -25,7 +25,6 @@ pub fn dom_struct(args: TokenStream, input: TokenStream) -> TokenStream {
// Work around https://github.com/rust-lang/rust/issues/46489
let attributes: TokenStream = attributes.to_string().parse().unwrap();
let output: TokenStream = attributes.into_iter().chain(input.into_iter()).collect();
let item: Item = syn::parse(output).unwrap();
@ -36,7 +35,11 @@ pub fn dom_struct(args: TokenStream, input: TokenStream) -> TokenStream {
return quote!(#s2).into();
}
if let Fields::Named(ref f) = s.fields {
let f = f.named.first().expect("Must have at least one field").into_value();
let f = f
.named
.first()
.expect("Must have at least one field")
.into_value();
let ident = f.ident.as_ref().expect("Must have named fields");
let name = &s.ident;
let ty = &f.ty;

View file

@ -5,8 +5,10 @@
#![recursion_limit = "128"]
extern crate proc_macro;
#[macro_use] extern crate quote;
#[macro_use] extern crate syn;
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
#[proc_macro_derive(DomObject)]
pub fn expand_token_stream(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
@ -74,7 +76,9 @@ fn expand_dom_object(input: syn::DeriveInput) -> quote::Tokens {
}));
let mut generics = input.generics.clone();
generics.params.push(parse_quote!(__T: ::dom::bindings::reflector::DomObject));
generics
.params
.push(parse_quote!(__T: ::dom::bindings::reflector::DomObject));
let (impl_generics, _, where_clause) = generics.split_for_impl();

View file

@ -24,9 +24,8 @@ use std::sync::mpsc::{Receiver, Sender};
use style_traits::cursor::CursorKind;
use webrender_api::{DeviceIntPoint, DeviceUintSize};
/// Used to wake up the event loop, provided by the servo port/embedder.
pub trait EventLoopWaker : 'static + Send {
pub trait EventLoopWaker: 'static + Send {
fn clone(&self) -> Box<EventLoopWaker + Send>;
fn wake(&self);
}
@ -58,11 +57,13 @@ impl Clone for EmbedderProxy {
/// The port that the embedder receives messages on.
pub struct EmbedderReceiver {
pub receiver: Receiver<(Option<TopLevelBrowsingContextId>, EmbedderMsg)>
pub receiver: Receiver<(Option<TopLevelBrowsingContextId>, EmbedderMsg)>,
}
impl EmbedderReceiver {
pub fn try_recv_embedder_msg(&mut self) -> Option<(Option<TopLevelBrowsingContextId>, EmbedderMsg)> {
pub fn try_recv_embedder_msg(
&mut self,
) -> Option<(Option<TopLevelBrowsingContextId>, EmbedderMsg)> {
self.receiver.try_recv().ok()
}
pub fn recv_embedder_msg(&mut self) -> (Option<TopLevelBrowsingContextId>, EmbedderMsg) {
@ -148,7 +149,7 @@ impl Debug for EmbedderMsg {
EmbedderMsg::HideIME => write!(f, "HideIME"),
EmbedderMsg::Shutdown => write!(f, "Shutdown"),
EmbedderMsg::AllowOpeningBrowser(..) => write!(f, "AllowOpeningBrowser"),
EmbedderMsg::BrowserCreated(..) => write!(f, "BrowserCreated")
EmbedderMsg::BrowserCreated(..) => write!(f, "BrowserCreated"),
}
}
}

View file

@ -7,10 +7,12 @@ use std::sync::RwLock;
lazy_static! {
static ref RES: RwLock<Option<Box<ResourceReaderMethods + Sync + Send>>> = RwLock::new({
#[cfg(not(feature = "tests"))] {
#[cfg(not(feature = "tests"))]
{
None
}
#[cfg(feature = "tests")] {
#[cfg(feature = "tests")]
{
Some(resources_for_tests())
}
});
@ -21,7 +23,11 @@ pub fn set(reader: Box<ResourceReaderMethods + Sync + Send>) {
}
pub fn read_bytes(res: Resource) -> Vec<u8> {
RES.read().unwrap().as_ref().expect("Resource reader not set.").read(res)
RES.read()
.unwrap()
.as_ref()
.expect("Resource reader not set.")
.read(res)
}
pub fn read_string(res: Resource) -> String {
@ -29,11 +35,19 @@ pub fn read_string(res: Resource) -> String {
}
pub fn sandbox_access_files() -> Vec<PathBuf> {
RES.read().unwrap().as_ref().expect("Resource reader not set.").sandbox_access_files()
RES.read()
.unwrap()
.as_ref()
.expect("Resource reader not set.")
.sandbox_access_files()
}
pub fn sandbox_access_files_dirs() -> Vec<PathBuf> {
RES.read().unwrap().as_ref().expect("Resource reader not set.").sandbox_access_files_dirs()
RES.read()
.unwrap()
.as_ref()
.expect("Resource reader not set.")
.sandbox_access_files_dirs()
}
pub enum Resource {
@ -64,8 +78,12 @@ fn resources_for_tests() -> Box<ResourceReaderMethods + Sync + Send> {
use std::io::Read;
struct ResourceReader;
impl ResourceReaderMethods for ResourceReader {
fn sandbox_access_files(&self) -> Vec<PathBuf> { vec![] }
fn sandbox_access_files_dirs(&self) -> Vec<PathBuf> { vec![] }
fn sandbox_access_files(&self) -> Vec<PathBuf> {
vec![]
}
fn sandbox_access_files_dirs(&self) -> Vec<PathBuf> {
vec![]
}
fn read(&self, file: Resource) -> Vec<u8> {
let file = match file {
Resource::Preferences => "prefs.json",
@ -92,8 +110,10 @@ fn resources_for_tests() -> Box<ResourceReaderMethods + Sync + Send> {
}
path.push(file);
let mut buffer = vec![];
File::open(path).expect(&format!("Can't find file: {}", file))
.read_to_end(&mut buffer).expect("Can't read file");
File::open(path)
.expect(&format!("Can't find file: {}", file))
.read_to_end(&mut buffer)
.expect("Can't read file");
buffer
}
}

View file

@ -18,7 +18,6 @@ pub trait FallibleVec<T> {
fn try_push(&mut self, value: T) -> Result<(), FailedAllocationError>;
}
/////////////////////////////////////////////////////////////////
// Vec
@ -52,14 +51,14 @@ fn try_double_vec<T>(vec: &mut Vec<T>) -> Result<(), FailedAllocationError> {
let new_cap: usize = if old_cap == 0 {
4
} else {
old_cap.checked_mul(2).ok_or(FailedAllocationError::new(
"capacity overflow for Vec",
))?
old_cap
.checked_mul(2)
.ok_or(FailedAllocationError::new("capacity overflow for Vec"))?
};
let new_size_bytes = new_cap.checked_mul(mem::size_of::<T>()).ok_or(
FailedAllocationError::new("capacity overflow for Vec"),
)?;
let new_size_bytes = new_cap
.checked_mul(mem::size_of::<T>())
.ok_or(FailedAllocationError::new("capacity overflow for Vec"))?;
let new_ptr = unsafe {
if old_cap == 0 {
@ -75,15 +74,12 @@ fn try_double_vec<T>(vec: &mut Vec<T>) -> Result<(), FailedAllocationError> {
));
}
let new_vec = unsafe {
Vec::from_raw_parts(new_ptr as *mut T, old_len, new_cap)
};
let new_vec = unsafe { Vec::from_raw_parts(new_ptr as *mut T, old_len, new_cap) };
mem::forget(mem::replace(vec, new_vec));
Ok(())
}
/////////////////////////////////////////////////////////////////
// SmallVec
@ -107,8 +103,7 @@ impl<T: Array> FallibleVec<T::Item> for SmallVec<T> {
#[cfg(feature = "known_system_malloc")]
#[inline(never)]
#[cold]
fn try_double_small_vec<T>(svec: &mut SmallVec<T>)
-> Result<(), FailedAllocationError>
fn try_double_small_vec<T>(svec: &mut SmallVec<T>) -> Result<(), FailedAllocationError>
where
T: Array,
{
@ -122,20 +117,20 @@ where
let new_cap: usize = if old_cap == 0 {
4
} else {
old_cap.checked_mul(2).ok_or(FailedAllocationError::new(
"capacity overflow for SmallVec",
))?
old_cap
.checked_mul(2)
.ok_or(FailedAllocationError::new("capacity overflow for SmallVec"))?
};
// This surely shouldn't fail, if |old_cap| was previously accepted as a
// valid value. But err on the side of caution.
let old_size_bytes = old_cap.checked_mul(mem::size_of::<T>()).ok_or(
FailedAllocationError::new("capacity overflow for SmallVec"),
)?;
let old_size_bytes = old_cap
.checked_mul(mem::size_of::<T>())
.ok_or(FailedAllocationError::new("capacity overflow for SmallVec"))?;
let new_size_bytes = new_cap.checked_mul(mem::size_of::<T>()).ok_or(
FailedAllocationError::new("capacity overflow for SmallVec"),
)?;
let new_size_bytes = new_cap
.checked_mul(mem::size_of::<T>())
.ok_or(FailedAllocationError::new("capacity overflow for SmallVec"))?;
let new_ptr;
if svec.spilled() {
@ -149,8 +144,7 @@ where
unsafe {
new_ptr = alloc::alloc(new_size_bytes, 0);
if !new_ptr.is_null() && old_size_bytes > 0 {
copy_nonoverlapping(old_ptr as *const u8,
new_ptr as *mut u8, old_size_bytes);
copy_nonoverlapping(old_ptr as *const u8, new_ptr as *mut u8, old_size_bytes);
}
}
}
@ -161,9 +155,7 @@ where
));
}
let new_vec = unsafe {
Vec::from_raw_parts(new_ptr as *mut T::Item, old_len, new_cap)
};
let new_vec = unsafe { Vec::from_raw_parts(new_ptr as *mut T::Item, old_len, new_cap) };
let new_svec = SmallVec::from_vec(new_vec);
mem::forget(mem::replace(svec, new_svec));

View file

@ -5,8 +5,9 @@
extern crate app_units;
extern crate euclid;
extern crate malloc_size_of;
#[macro_use]
extern crate malloc_size_of_derive;
extern crate style_traits;
#[macro_use] extern crate malloc_size_of_derive;
extern crate webrender_api;
use app_units::{Au, MAX_AU, MIN_AU};
@ -48,7 +49,7 @@ impl MaxRect for Rect<Au> {
fn max_rect() -> Rect<Au> {
Rect::new(
Point2D::new(MIN_AU / 2, MIN_AU / 2),
Size2D::new(MAX_AU, MAX_AU)
Size2D::new(MAX_AU, MAX_AU),
)
}
}
@ -64,12 +65,22 @@ impl MaxRect for LayoutRect {
/// A helper function to convert a rect of `f32` pixels to a rect of app units.
pub fn f32_rect_to_au_rect(rect: Rect<f32>) -> Rect<Au> {
Rect::new(Point2D::new(Au::from_f32_px(rect.origin.x), Au::from_f32_px(rect.origin.y)),
Size2D::new(Au::from_f32_px(rect.size.width), Au::from_f32_px(rect.size.height)))
Rect::new(
Point2D::new(
Au::from_f32_px(rect.origin.x),
Au::from_f32_px(rect.origin.y),
),
Size2D::new(
Au::from_f32_px(rect.size.width),
Au::from_f32_px(rect.size.height),
),
)
}
/// A helper function to convert a rect of `Au` pixels to a rect of f32 units.
pub fn au_rect_to_f32_rect(rect: Rect<Au>) -> Rect<f32> {
Rect::new(Point2D::new(rect.origin.x.to_f32_px(), rect.origin.y.to_f32_px()),
Size2D::new(rect.size.width.to_f32_px(), rect.size.height.to_f32_px()))
Rect::new(
Point2D::new(rect.origin.x.to_f32_px(), rect.origin.y.to_f32_px()),
Size2D::new(rect.size.width.to_f32_px(), rect.size.height.to_f32_px()),
)
}

View file

@ -32,9 +32,9 @@ use unicode_script::Script;
use webrender_api;
macro_rules! ot_tag {
($t1:expr, $t2:expr, $t3:expr, $t4:expr) => (
($t1:expr, $t2:expr, $t3:expr, $t4:expr) => {
(($t1 as u32) << 24) | (($t2 as u32) << 16) | (($t3 as u32) << 8) | ($t4 as u32)
);
};
}
pub const GPOS: u32 = ot_tag!('G', 'P', 'O', 'S');
@ -87,10 +87,12 @@ trait FontTableTagConversions {
impl FontTableTagConversions for FontTableTag {
fn tag_to_str(&self) -> String {
let bytes = [(self >> 24) as u8,
(self >> 16) as u8,
(self >> 8) as u8,
(self >> 0) as u8];
let bytes = [
(self >> 24) as u8,
(self >> 16) as u8,
(self >> 8) as u8,
(self >> 0) as u8,
];
str::from_utf8(&bytes).unwrap().to_owned()
}
}
@ -101,18 +103,18 @@ pub trait FontTableMethods {
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct FontMetrics {
pub underline_size: Au,
pub underline_size: Au,
pub underline_offset: Au,
pub strikeout_size: Au,
pub strikeout_size: Au,
pub strikeout_offset: Au,
pub leading: Au,
pub x_height: Au,
pub em_size: Au,
pub ascent: Au,
pub descent: Au,
pub max_advance: Au,
pub average_advance: Au,
pub line_gap: Au,
pub leading: Au,
pub x_height: Au,
pub em_size: Au,
pub ascent: Au,
pub descent: Au,
pub max_advance: Au,
pub average_advance: Au,
pub line_gap: Au,
}
/// `FontDescriptor` describes the parameters of a `Font`. It represents rendering a given font
@ -149,10 +151,12 @@ pub struct Font {
}
impl Font {
pub fn new(handle: FontHandle,
descriptor: FontDescriptor,
actual_pt_size: Au,
font_key: webrender_api::FontInstanceKey) -> Font {
pub fn new(
handle: FontHandle,
descriptor: FontDescriptor,
actual_pt_size: Au,
font_key: webrender_api::FontInstanceKey,
) -> Font {
let metrics = handle.metrics();
Font {
@ -218,28 +222,39 @@ impl Font {
text: text.to_owned(),
options: *options,
};
let result = self.shape_cache.borrow_mut().entry(lookup_key).or_insert_with(|| {
let start_time = time::precise_time_ns();
let mut glyphs = GlyphStore::new(text.len(),
options.flags.contains(ShapingFlags::IS_WHITESPACE_SHAPING_FLAG),
options.flags.contains(ShapingFlags::RTL_FLAG));
let result = self
.shape_cache
.borrow_mut()
.entry(lookup_key)
.or_insert_with(|| {
let start_time = time::precise_time_ns();
let mut glyphs = GlyphStore::new(
text.len(),
options
.flags
.contains(ShapingFlags::IS_WHITESPACE_SHAPING_FLAG),
options.flags.contains(ShapingFlags::RTL_FLAG),
);
if self.can_do_fast_shaping(text, options) {
debug!("shape_text: Using ASCII fast path.");
self.shape_text_fast(text, options, &mut glyphs);
} else {
debug!("shape_text: Using Harfbuzz.");
if shaper.is_none() {
shaper = Some(Shaper::new(this));
if self.can_do_fast_shaping(text, options) {
debug!("shape_text: Using ASCII fast path.");
self.shape_text_fast(text, options, &mut glyphs);
} else {
debug!("shape_text: Using Harfbuzz.");
if shaper.is_none() {
shaper = Some(Shaper::new(this));
}
shaper
.as_ref()
.unwrap()
.shape_text(text, options, &mut glyphs);
}
shaper.as_ref().unwrap().shape_text(text, options, &mut glyphs);
}
let end_time = time::precise_time_ns();
TEXT_SHAPING_PERFORMANCE_COUNTER.fetch_add((end_time - start_time) as usize,
Ordering::Relaxed);
Arc::new(glyphs)
}).clone();
let end_time = time::precise_time_ns();
TEXT_SHAPING_PERFORMANCE_COUNTER
.fetch_add((end_time - start_time) as usize, Ordering::Relaxed);
Arc::new(glyphs)
}).clone();
self.shaper = shaper;
result
}
@ -285,12 +300,21 @@ impl Font {
pub fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
let result = self.handle.table_for_tag(tag);
let status = if result.is_some() { "Found" } else { "Didn't find" };
let status = if result.is_some() {
"Found"
} else {
"Didn't find"
};
debug!("{} font table[{}] with family={}, face={}",
status, tag.tag_to_str(),
self.handle.family_name().unwrap_or("unavailable".to_owned()),
self.handle.face_name().unwrap_or("unavailable".to_owned()));
debug!(
"{} font table[{}] with family={}, face={}",
status,
tag.tag_to_str(),
self.handle
.family_name()
.unwrap_or("unavailable".to_owned()),
self.handle.face_name().unwrap_or("unavailable".to_owned())
);
result
}
@ -308,18 +332,21 @@ impl Font {
self.glyph_index(codepoint).is_some()
}
pub fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId)
-> FractionalPixel {
pub fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel {
self.handle.glyph_h_kerning(first_glyph, second_glyph)
}
pub fn glyph_h_advance(&self, glyph: GlyphId) -> FractionalPixel {
*self.glyph_advance_cache.borrow_mut().entry(glyph).or_insert_with(|| {
match self.handle.glyph_h_advance(glyph) {
Some(adv) => adv,
None => 10f64 as FractionalPixel // FIXME: Need fallback strategy
}
})
*self
.glyph_advance_cache
.borrow_mut()
.entry(glyph)
.or_insert_with(|| {
match self.handle.glyph_h_advance(glyph) {
Some(adv) => adv,
None => 10f64 as FractionalPixel, // FIXME: Need fallback strategy
}
})
}
}
@ -339,10 +366,12 @@ impl FontGroup {
pub fn new(style: &FontStyleStruct) -> FontGroup {
let descriptor = FontDescriptor::from(style);
let families =
style.font_family.0.iter()
.map(|family| FontGroupFamily::new(descriptor.clone(), &family))
.collect();
let families = style
.font_family
.0
.iter()
.map(|family| FontGroupFamily::new(descriptor.clone(), &family))
.collect();
FontGroup {
descriptor,
@ -358,25 +387,25 @@ impl FontGroup {
pub fn find_by_codepoint<S: FontSource>(
&mut self,
mut font_context: &mut FontContext<S>,
codepoint: char
codepoint: char,
) -> Option<FontRef> {
let has_glyph = |font: &FontRef| font.borrow().has_glyph_for(codepoint);
let font = self.find(&mut font_context, |font| has_glyph(font));
if font.is_some() {
return font
return font;
}
if let Some(ref fallback) = self.last_matching_fallback {
if has_glyph(&fallback) {
return self.last_matching_fallback.clone()
return self.last_matching_fallback.clone();
}
}
let font = self.find_fallback(&mut font_context, Some(codepoint), has_glyph);
if font.is_some() {
self.last_matching_fallback = font.clone();
return font
return font;
}
self.first(&mut font_context)
@ -385,7 +414,7 @@ impl FontGroup {
/// Find the first available font in the group, or the first available fallback font.
pub fn first<S: FontSource>(
&mut self,
mut font_context: &mut FontContext<S>
mut font_context: &mut FontContext<S>,
) -> Option<FontRef> {
self.find(&mut font_context, |_| true)
.or_else(|| self.find_fallback(&mut font_context, None, |_| true))
@ -393,16 +422,13 @@ impl FontGroup {
/// Find a font which returns true for `predicate`. This method mutates because we may need to
/// load new font data in the process of finding a suitable font.
fn find<S, P>(
&mut self,
mut font_context: &mut FontContext<S>,
predicate: P,
) -> Option<FontRef>
fn find<S, P>(&mut self, mut font_context: &mut FontContext<S>, predicate: P) -> Option<FontRef>
where
S: FontSource,
P: FnMut(&FontRef) -> bool,
{
self.families.iter_mut()
self.families
.iter_mut()
.filter_map(|family| family.font(&mut font_context))
.find(predicate)
}
@ -422,15 +448,9 @@ impl FontGroup {
P: FnMut(&FontRef) -> bool,
{
iter::once(FontFamilyDescriptor::default())
.chain(
fallback_font_families(codepoint).into_iter().map(|family| {
FontFamilyDescriptor::new(
FontFamilyName::from(family),
FontSearchScope::Local,
)
})
)
.filter_map(|family| font_context.font(&self.descriptor, &family))
.chain(fallback_font_families(codepoint).into_iter().map(|family| {
FontFamilyDescriptor::new(FontFamilyName::from(family), FontSearchScope::Local)
})).filter_map(|family| font_context.font(&self.descriptor, &family))
.find(predicate)
}
}
@ -448,10 +468,8 @@ struct FontGroupFamily {
impl FontGroupFamily {
fn new(font_descriptor: FontDescriptor, family: &SingleFontFamily) -> FontGroupFamily {
let family_descriptor = FontFamilyDescriptor::new(
FontFamilyName::from(family),
FontSearchScope::Any
);
let family_descriptor =
FontFamilyDescriptor::new(FontFamilyName::from(family), FontSearchScope::Any);
FontGroupFamily {
font_descriptor,
@ -477,17 +495,19 @@ impl FontGroupFamily {
pub struct RunMetrics {
// may be negative due to negative width (i.e., kerning of '.' in 'P.T.')
pub advance_width: Au,
pub ascent: Au, // nonzero
pub ascent: Au, // nonzero
pub descent: Au, // nonzero
// this bounding box is relative to the left origin baseline.
// so, bounding_box.position.y = -ascent
pub bounding_box: Rect<Au>
pub bounding_box: Rect<Au>,
}
impl RunMetrics {
pub fn new(advance: Au, ascent: Au, descent: Au) -> RunMetrics {
let bounds = Rect::new(Point2D::new(Au(0), -ascent),
Size2D::new(advance, ascent + descent));
let bounds = Rect::new(
Point2D::new(Au(0), -ascent),
Size2D::new(advance, ascent + descent),
);
// TODO(Issue #125): support loose and tight bounding boxes; using the
// ascent+descent and advance is sometimes too generous and
@ -540,11 +560,13 @@ impl FontFamilyName {
impl<'a> From<&'a SingleFontFamily> for FontFamilyName {
fn from(other: &'a SingleFontFamily) -> FontFamilyName {
match *other {
SingleFontFamily::FamilyName(ref family_name) =>
FontFamilyName::Specific(family_name.name.clone()),
SingleFontFamily::FamilyName(ref family_name) => {
FontFamilyName::Specific(family_name.name.clone())
},
SingleFontFamily::Generic(ref generic_name) =>
FontFamilyName::Generic(generic_name.clone()),
SingleFontFamily::Generic(ref generic_name) => {
FontFamilyName::Generic(generic_name.clone())
},
}
}
}

View file

@ -40,14 +40,15 @@ pub struct FontTemplateInfo {
impl FontTemplates {
pub fn new() -> FontTemplates {
FontTemplates {
templates: vec!(),
}
FontTemplates { templates: vec![] }
}
/// Find a font in this family that matches a given descriptor.
pub fn find_font_for_style(&mut self, desc: &FontTemplateDescriptor, fctx: &FontContextHandle)
-> Option<Arc<FontTemplateData>> {
pub fn find_font_for_style(
&mut self,
desc: &FontTemplateDescriptor,
fctx: &FontContextHandle,
) -> Option<Arc<FontTemplateData>> {
// TODO(Issue #189): optimize lookup for
// regular/bold/italic/bolditalic with fixed offsets and a
// static decision table for fallback between these values.
@ -63,7 +64,8 @@ impl FontTemplates {
let (mut best_template_data, mut best_distance) = (None, f32::MAX);
for template in &mut self.templates {
if let Some((template_data, distance)) =
template.data_for_approximate_descriptor(fctx, desc) {
template.data_for_approximate_descriptor(fctx, desc)
{
if distance < best_distance {
best_template_data = Some(template_data);
best_distance = distance
@ -71,7 +73,7 @@ impl FontTemplates {
}
}
if best_template_data.is_some() {
return best_template_data
return best_template_data;
}
// If a request is made for a font family that exists,
@ -103,8 +105,16 @@ impl FontTemplates {
/// Commands that the FontContext sends to the font cache thread.
#[derive(Debug, Deserialize, Serialize)]
pub enum Command {
GetFontTemplate(FontTemplateDescriptor, FontFamilyDescriptor, IpcSender<Reply>),
GetFontInstance(webrender_api::FontKey, Au, IpcSender<webrender_api::FontInstanceKey>),
GetFontTemplate(
FontTemplateDescriptor,
FontFamilyDescriptor,
IpcSender<Reply>,
),
GetFontInstance(
webrender_api::FontKey,
Au,
IpcSender<webrender_api::FontInstanceKey>,
),
AddWebFont(LowercaseString, EffectiveSources, IpcSender<()>),
AddDownloadedWebFont(LowercaseString, ServoUrl, Vec<u8>, IpcSender<()>),
Exit(IpcSender<()>),
@ -148,7 +158,7 @@ fn populate_generic_fonts() -> HashMap<FontFamilyName, LowercaseString> {
) {
let family_name = match system_default_family(generic_name) {
Some(system_default) => LowercaseString::new(&system_default),
None => LowercaseString::new(mapped_name)
None => LowercaseString::new(mapped_name),
};
let generic_name = FontFamilyName::Generic(Atom::from(generic_name));
@ -156,7 +166,6 @@ fn populate_generic_fonts() -> HashMap<FontFamilyName, LowercaseString> {
generic_fonts.insert(generic_name, family_name);
}
generic_fonts
}
@ -167,50 +176,50 @@ impl FontCache {
match msg {
Command::GetFontTemplate(template_descriptor, family_descriptor, result) => {
let maybe_font_template = self.find_font_template(&template_descriptor, &family_descriptor);
let maybe_font_template =
self.find_font_template(&template_descriptor, &family_descriptor);
let _ = result.send(Reply::GetFontTemplateReply(maybe_font_template));
}
},
Command::GetFontInstance(font_key, size, result) => {
let webrender_api = &self.webrender_api;
let instance_key = *self.font_instances
.entry((font_key, size))
.or_insert_with(|| {
let key = webrender_api.generate_font_instance_key();
let mut txn = webrender_api::Transaction::new();
txn.add_font_instance(key,
font_key,
size,
None,
None,
Vec::new());
webrender_api.update_resources(txn.resource_updates);
key
});
let instance_key =
*self
.font_instances
.entry((font_key, size))
.or_insert_with(|| {
let key = webrender_api.generate_font_instance_key();
let mut txn = webrender_api::Transaction::new();
txn.add_font_instance(key, font_key, size, None, None, Vec::new());
webrender_api.update_resources(txn.resource_updates);
key
});
let _ = result.send(instance_key);
}
},
Command::AddWebFont(family_name, sources, result) => {
self.handle_add_web_font(family_name, sources, result);
}
},
Command::AddDownloadedWebFont(family_name, url, bytes, result) => {
let templates = &mut self.web_families.get_mut(&family_name).unwrap();
templates.add_template(Atom::from(url.to_string()), Some(bytes));
drop(result.send(()));
}
},
Command::Ping => (),
Command::Exit(result) => {
let _ = result.send(());
break;
}
},
}
}
}
fn handle_add_web_font(&mut self,
family_name: LowercaseString,
mut sources: EffectiveSources,
sender: IpcSender<()>) {
fn handle_add_web_font(
&mut self,
family_name: LowercaseString,
mut sources: EffectiveSources,
sender: IpcSender<()>,
) {
let src = if let Some(src) = sources.next() {
src
} else {
@ -236,7 +245,7 @@ impl FontCache {
destination: Destination::Font,
// TODO: Add a proper origin - Can't import GlobalScope from gfx
// We can leave origin to be set by default
.. RequestInit::default()
..RequestInit::default()
};
let channel_to_self = self.channel_to_self.clone();
@ -248,19 +257,27 @@ impl FontCache {
FetchResponseMsg::ProcessRequestBody |
FetchResponseMsg::ProcessRequestEOF => (),
FetchResponseMsg::ProcessResponse(meta_result) => {
trace!("@font-face {} metadata ok={:?}", family_name, meta_result.is_ok());
trace!(
"@font-face {} metadata ok={:?}",
family_name,
meta_result.is_ok()
);
*response_valid.lock().unwrap() = meta_result.is_ok();
}
},
FetchResponseMsg::ProcessResponseChunk(new_bytes) => {
trace!("@font-face {} chunk={:?}", family_name, new_bytes);
if *response_valid.lock().unwrap() {
bytes.lock().unwrap().extend(new_bytes.into_iter())
}
}
},
FetchResponseMsg::ProcessResponseEOF(response) => {
trace!("@font-face {} EOF={:?}", family_name, response);
if response.is_err() || !*response_valid.lock().unwrap() {
let msg = Command::AddWebFont(family_name.clone(), sources.clone(), sender.clone());
let msg = Command::AddWebFont(
family_name.clone(),
sources.clone(),
sender.clone(),
);
channel_to_self.send(msg).unwrap();
return;
}
@ -270,23 +287,31 @@ impl FontCache {
Ok(san) => san,
Err(_) => {
// FIXME(servo/fontsan#1): get an error message
debug!("Sanitiser rejected web font: \
family={} url={:?}", family_name, url);
let msg = Command::AddWebFont(family_name.clone(), sources.clone(), sender.clone());
debug!(
"Sanitiser rejected web font: \
family={} url={:?}",
family_name, url
);
let msg = Command::AddWebFont(
family_name.clone(),
sources.clone(),
sender.clone(),
);
channel_to_self.send(msg).unwrap();
return;
},
};
let command =
Command::AddDownloadedWebFont(family_name.clone(),
url.clone(),
bytes,
sender.clone());
let command = Command::AddDownloadedWebFont(
family_name.clone(),
url.clone(),
bytes,
sender.clone(),
);
channel_to_self.send(command).unwrap();
}
},
}
});
}
},
Source::Local(ref font) => {
let font_face_name = LowercaseString::new(&font.name);
let templates = &mut self.web_families.get_mut(&family_name).unwrap();
@ -301,7 +326,7 @@ impl FontCache {
let msg = Command::AddWebFont(family_name, sources, sender);
self.channel_to_self.send(msg).unwrap();
}
}
},
}
}
@ -319,7 +344,7 @@ impl FontCache {
fn transform_family(&self, family_name: &FontFamilyName) -> LowercaseString {
match self.generic_fonts.get(family_name) {
None => LowercaseString::from(family_name),
Some(mapped_family) => (*mapped_family).clone()
Some(mapped_family) => (*mapped_family).clone(),
}
}
@ -347,7 +372,10 @@ impl FontCache {
s.find_font_for_style(template_descriptor, &self.font_context)
} else {
debug!("FontList: Couldn't find font family with name={}", &*family_name);
debug!(
"FontList: Couldn't find font family with name={}",
&*family_name
);
None
}
}
@ -371,17 +399,19 @@ impl FontCache {
let webrender_api = &self.webrender_api;
let webrender_fonts = &mut self.webrender_fonts;
let font_key = *webrender_fonts.entry(template.identifier.clone()).or_insert_with(|| {
let font_key = webrender_api.generate_font_key();
let mut txn = webrender_api::Transaction::new();
match (template.bytes_if_in_memory(), template.native_font()) {
(Some(bytes), _) => txn.add_raw_font(font_key, bytes, 0),
(None, Some(native_font)) => txn.add_native_font(font_key, native_font),
(None, None) => txn.add_raw_font(font_key, template.bytes().clone(), 0),
}
webrender_api.update_resources(txn.resource_updates);
font_key
});
let font_key = *webrender_fonts
.entry(template.identifier.clone())
.or_insert_with(|| {
let font_key = webrender_api.generate_font_key();
let mut txn = webrender_api::Transaction::new();
match (template.bytes_if_in_memory(), template.native_font()) {
(Some(bytes), _) => txn.add_raw_font(font_key, bytes, 0),
(None, Some(native_font)) => txn.add_native_font(font_key, native_font),
(None, None) => txn.add_raw_font(font_key, template.bytes().clone(), 0),
}
webrender_api.update_resources(txn.resource_updates);
font_key
});
FontTemplateInfo {
font_template: template,
@ -395,14 +425,15 @@ impl FontCache {
family_descriptor: &FontFamilyDescriptor,
) -> Option<FontTemplateInfo> {
match family_descriptor.scope {
FontSearchScope::Any => {
self.find_font_in_web_family(&template_descriptor, &family_descriptor.name)
.or_else(|| self.find_font_in_local_family(&template_descriptor, &family_descriptor.name))
}
FontSearchScope::Any => self
.find_font_in_web_family(&template_descriptor, &family_descriptor.name)
.or_else(|| {
self.find_font_in_local_family(&template_descriptor, &family_descriptor.name)
}),
FontSearchScope::Local => {
self.find_font_in_local_family(&template_descriptor, &family_descriptor.name)
}
},
}.map(|t| self.get_font_template_info(t))
}
}
@ -415,59 +446,82 @@ pub struct FontCacheThread {
}
impl FontCacheThread {
pub fn new(core_resource_thread: CoreResourceThread,
webrender_api: webrender_api::RenderApi) -> FontCacheThread {
pub fn new(
core_resource_thread: CoreResourceThread,
webrender_api: webrender_api::RenderApi,
) -> FontCacheThread {
let (chan, port) = ipc::channel().unwrap();
let channel_to_self = chan.clone();
thread::Builder::new().name("FontCacheThread".to_owned()).spawn(move || {
// TODO: Allow users to specify these.
let generic_fonts = populate_generic_fonts();
thread::Builder::new()
.name("FontCacheThread".to_owned())
.spawn(move || {
// TODO: Allow users to specify these.
let generic_fonts = populate_generic_fonts();
let mut cache = FontCache {
port: port,
channel_to_self,
generic_fonts,
local_families: HashMap::new(),
web_families: HashMap::new(),
font_context: FontContextHandle::new(),
core_resource_thread,
webrender_api,
webrender_fonts: HashMap::new(),
font_instances: HashMap::new(),
};
let mut cache = FontCache {
port: port,
channel_to_self,
generic_fonts,
local_families: HashMap::new(),
web_families: HashMap::new(),
font_context: FontContextHandle::new(),
core_resource_thread,
webrender_api,
webrender_fonts: HashMap::new(),
font_instances: HashMap::new(),
};
cache.refresh_local_families();
cache.run();
}).expect("Thread spawning failed");
cache.refresh_local_families();
cache.run();
}).expect("Thread spawning failed");
FontCacheThread {
chan: chan,
}
FontCacheThread { chan: chan }
}
pub fn add_web_font(&self, family: FamilyName, sources: EffectiveSources, sender: IpcSender<()>) {
self.chan.send(Command::AddWebFont(LowercaseString::new(&family.name), sources, sender)).unwrap();
pub fn add_web_font(
&self,
family: FamilyName,
sources: EffectiveSources,
sender: IpcSender<()>,
) {
self.chan
.send(Command::AddWebFont(
LowercaseString::new(&family.name),
sources,
sender,
)).unwrap();
}
pub fn exit(&self) {
let (response_chan, response_port) = ipc::channel().unwrap();
self.chan.send(Command::Exit(response_chan)).expect("Couldn't send FontCacheThread exit message");
response_port.recv().expect("Couldn't receive FontCacheThread reply");
self.chan
.send(Command::Exit(response_chan))
.expect("Couldn't send FontCacheThread exit message");
response_port
.recv()
.expect("Couldn't receive FontCacheThread reply");
}
}
impl FontSource for FontCacheThread {
fn get_font_instance(&mut self, key: webrender_api::FontKey, size: Au) -> webrender_api::FontInstanceKey {
let (response_chan, response_port) =
ipc::channel().expect("failed to create IPC channel");
self.chan.send(Command::GetFontInstance(key, size, response_chan))
fn get_font_instance(
&mut self,
key: webrender_api::FontKey,
size: Au,
) -> webrender_api::FontInstanceKey {
let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
self.chan
.send(Command::GetFontInstance(key, size, response_chan))
.expect("failed to send message to font cache thread");
let instance_key = response_port.recv();
if instance_key.is_err() {
let font_thread_has_closed = self.chan.send(Command::Ping).is_err();
assert!(font_thread_has_closed, "Failed to receive a response from live font cache");
assert!(
font_thread_has_closed,
"Failed to receive a response from live font cache"
);
panic!("Font cache thread has already exited.");
}
instance_key.unwrap()
@ -478,23 +532,27 @@ impl FontSource for FontCacheThread {
template_descriptor: FontTemplateDescriptor,
family_descriptor: FontFamilyDescriptor,
) -> Option<FontTemplateInfo> {
let (response_chan, response_port) =
ipc::channel().expect("failed to create IPC channel");
self.chan.send(Command::GetFontTemplate(template_descriptor, family_descriptor, response_chan))
.expect("failed to send message to font cache thread");
let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
self.chan
.send(Command::GetFontTemplate(
template_descriptor,
family_descriptor,
response_chan,
)).expect("failed to send message to font cache thread");
let reply = response_port.recv();
if reply.is_err() {
let font_thread_has_closed = self.chan.send(Command::Ping).is_err();
assert!(font_thread_has_closed, "Failed to receive a response from live font cache");
assert!(
font_thread_has_closed,
"Failed to receive a response from live font cache"
);
panic!("Font cache thread has already exited.");
}
match reply.unwrap() {
Reply::GetFontTemplateReply(data) => {
data
}
Reply::GetFontTemplateReply(data) => data,
}
}
}

View file

@ -21,14 +21,18 @@ use style::computed_values::font_variant_caps::T as FontVariantCaps;
use style::properties::style_structs::Font as FontStyleStruct;
use webrender_api;
static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
/// An epoch for the font context cache. The cache is flushed if the current epoch does not match
/// this one.
static FONT_CACHE_EPOCH: AtomicUsize = ATOMIC_USIZE_INIT;
pub trait FontSource {
fn get_font_instance(&mut self, key: webrender_api::FontKey, size: Au) -> webrender_api::FontInstanceKey;
fn get_font_instance(
&mut self,
key: webrender_api::FontKey,
size: Au,
) -> webrender_api::FontInstanceKey;
fn font_template(
&mut self,
@ -74,7 +78,7 @@ impl<S: FontSource> FontContext<S> {
fn expire_font_caches_if_necessary(&mut self) {
let current_epoch = FONT_CACHE_EPOCH.load(Ordering::SeqCst);
if current_epoch == self.epoch {
return
return;
}
self.font_cache.clear();
@ -95,7 +99,7 @@ impl<S: FontSource> FontContext<S> {
};
if let Some(ref font_group) = self.font_group_cache.get(&cache_key) {
return (*font_group).clone()
return (*font_group).clone();
}
let font_group = Rc::new(RefCell::new(FontGroup::new(&cache_key.style)));
@ -115,27 +119,31 @@ impl<S: FontSource> FontContext<S> {
family_descriptor: family_descriptor.clone(),
};
self.font_cache.get(&cache_key).map(|v| v.clone()).unwrap_or_else(|| {
debug!(
"FontContext::font cache miss for font_descriptor={:?} family_descriptor={:?}",
font_descriptor,
family_descriptor
);
self.font_cache
.get(&cache_key)
.map(|v| v.clone())
.unwrap_or_else(|| {
debug!(
"FontContext::font cache miss for font_descriptor={:?} family_descriptor={:?}",
font_descriptor, family_descriptor
);
let font =
self.font_template(&font_descriptor.template_descriptor, family_descriptor)
.and_then(|template_info| self.create_font(template_info, font_descriptor.to_owned()).ok())
.map(|font| Rc::new(RefCell::new(font)));
let font = self
.font_template(&font_descriptor.template_descriptor, family_descriptor)
.and_then(|template_info| {
self.create_font(template_info, font_descriptor.to_owned())
.ok()
}).map(|font| Rc::new(RefCell::new(font)));
self.font_cache.insert(cache_key, font.clone());
font
})
self.font_cache.insert(cache_key, font.clone());
font
})
}
fn font_template(
&mut self,
template_descriptor: &FontTemplateDescriptor,
family_descriptor: &FontFamilyDescriptor
family_descriptor: &FontFamilyDescriptor,
) -> Option<FontTemplateInfo> {
let cache_key = FontTemplateCacheKey {
template_descriptor: template_descriptor.clone(),
@ -164,7 +172,7 @@ impl<S: FontSource> FontContext<S> {
fn create_font(
&mut self,
info: FontTemplateInfo,
descriptor: FontDescriptor
descriptor: FontDescriptor,
) -> Result<Font, ()> {
// TODO: (Bug #3463): Currently we only support fake small-caps
// painting. We should also support true small-caps (where the
@ -177,11 +185,18 @@ impl<S: FontSource> FontContext<S> {
let handle = FontHandle::new_from_template(
&self.platform_handle,
info.font_template,
Some(actual_pt_size)
Some(actual_pt_size),
)?;
let font_instance_key = self.font_source.get_font_instance(info.font_key, actual_pt_size);
Ok(Font::new(handle, descriptor.to_owned(), actual_pt_size, font_instance_key))
let font_instance_key = self
.font_source
.get_font_instance(info.font_key, actual_pt_size);
Ok(Font::new(
handle,
descriptor.to_owned(),
actual_pt_size,
font_instance_key,
))
}
}
@ -219,7 +234,10 @@ impl PartialEq for FontGroupCacheKey {
impl Eq for FontGroupCacheKey {}
impl Hash for FontGroupCacheKey {
fn hash<H>(&self, hasher: &mut H) where H: Hasher {
fn hash<H>(&self, hasher: &mut H)
where
H: Hasher,
{
self.style.hash.hash(hasher)
}
}

View file

@ -26,7 +26,6 @@ pub struct FontTemplateDescriptor {
pub style: FontStyle,
}
/// FontTemplateDescriptor contains floats, which are not Eq because of NaN. However,
/// we know they will never be NaN, so we can manually implement Eq.
impl Eq for FontTemplateDescriptor {}
@ -41,14 +40,9 @@ fn style_to_number(s: &FontStyle) -> f32 {
}
}
impl FontTemplateDescriptor {
#[inline]
pub fn new(
weight: FontWeight,
stretch: FontStretch,
style: FontStyle,
) -> Self {
pub fn new(weight: FontWeight, stretch: FontStretch, style: FontStyle) -> Self {
Self {
weight,
stretch,
@ -138,7 +132,10 @@ impl FontTemplate {
}
/// Get the descriptor. Returns `None` when instantiating the data fails.
pub fn descriptor(&mut self, font_context: &FontContextHandle) -> Option<FontTemplateDescriptor> {
pub fn descriptor(
&mut self,
font_context: &FontContextHandle,
) -> Option<FontTemplateDescriptor> {
// The font template data can be unloaded when nothing is referencing
// it (via the Weak reference to the Arc above). However, if we have
// already loaded a font, store the style information about it separately,
@ -147,18 +144,22 @@ impl FontTemplate {
self.descriptor.or_else(|| {
if self.instantiate(font_context).is_err() {
return None
return None;
};
Some(self.descriptor.expect("Instantiation succeeded but no descriptor?"))
Some(
self.descriptor
.expect("Instantiation succeeded but no descriptor?"),
)
})
}
/// Get the data for creating a font if it matches a given descriptor.
pub fn data_for_descriptor(&mut self,
fctx: &FontContextHandle,
requested_desc: &FontTemplateDescriptor)
-> Option<Arc<FontTemplateData>> {
pub fn data_for_descriptor(
&mut self,
fctx: &FontContextHandle,
requested_desc: &FontTemplateDescriptor,
) -> Option<Arc<FontTemplateData>> {
self.descriptor(&fctx).and_then(|descriptor| {
if *requested_desc == descriptor {
self.data().ok()
@ -176,21 +177,20 @@ impl FontTemplate {
requested_descriptor: &FontTemplateDescriptor,
) -> Option<(Arc<FontTemplateData>, f32)> {
self.descriptor(&font_context).and_then(|descriptor| {
self.data().ok().map(|data| {
(data, descriptor.distance_from(requested_descriptor))
})
self.data()
.ok()
.map(|data| (data, descriptor.distance_from(requested_descriptor)))
})
}
fn instantiate(&mut self, font_context: &FontContextHandle) -> Result<(), ()> {
if !self.is_valid {
return Err(())
return Err(());
}
let data = self.data().map_err(|_| ())?;
let handle: Result<FontHandle, ()> = FontHandleMethods::new_from_template(font_context,
data,
None);
let handle: Result<FontHandle, ()> =
FontHandleMethods::new_from_template(font_context, data, None);
self.is_valid = handle.is_ok();
let handle = handle?;
self.descriptor = Some(FontTemplateDescriptor::new(
@ -220,7 +220,7 @@ impl FontTemplate {
};
if let Some(data) = maybe_data {
return Ok(data)
return Ok(data);
}
assert!(self.strong_ref.is_none());

View file

@ -9,14 +9,18 @@ extern crate app_units;
extern crate bitflags;
// Mac OS-specific library dependencies
#[cfg(target_os = "macos")] extern crate byteorder;
#[cfg(target_os = "macos")] extern crate core_foundation;
#[cfg(target_os = "macos")] extern crate core_graphics;
#[cfg(target_os = "macos")] extern crate core_text;
#[cfg(target_os = "macos")]
extern crate byteorder;
#[cfg(target_os = "macos")]
extern crate core_foundation;
#[cfg(target_os = "macos")]
extern crate core_graphics;
#[cfg(target_os = "macos")]
extern crate core_text;
// Windows-specific library dependencies
#[cfg(target_os = "windows")] extern crate dwrote;
#[cfg(target_os = "windows")] extern crate truetype;
#[cfg(target_os = "windows")]
extern crate dwrote;
extern crate euclid;
extern crate fnv;
@ -24,8 +28,8 @@ extern crate fnv;
#[cfg(target_os = "linux")]
extern crate fontconfig;
extern crate fontsan;
#[cfg(any(target_os = "linux", target_os = "android"))] extern crate freetype;
#[cfg(any(target_os = "linux", target_os = "android"))] extern crate servo_allocator;
#[cfg(any(target_os = "linux", target_os = "android"))]
extern crate freetype;
extern crate gfx_traits;
// Eventually we would like the shaper to be pluggable, as many operating systems have their own
@ -35,23 +39,33 @@ extern crate harfbuzz_sys as harfbuzz;
extern crate ipc_channel;
#[macro_use]
extern crate lazy_static;
#[cfg(any(target_os = "linux", target_os = "android"))] extern crate libc;
#[cfg(any(target_os = "linux", target_os = "android"))]
extern crate libc;
#[macro_use]
extern crate log;
#[cfg_attr(target_os = "windows", macro_use)]
extern crate malloc_size_of;
extern crate net_traits;
extern crate ordered_float;
#[cfg(all(feature = "unstable", any(target_feature = "sse2", target_feature = "neon")))]
#[cfg(all(
feature = "unstable",
any(target_feature = "sse2", target_feature = "neon")
))]
extern crate packed_simd;
extern crate range;
#[macro_use] extern crate serde;
#[macro_use]
extern crate serde;
#[cfg(any(target_os = "linux", target_os = "android"))]
extern crate servo_allocator;
extern crate servo_arc;
#[macro_use] extern crate servo_atoms;
#[macro_use]
extern crate servo_atoms;
extern crate servo_url;
extern crate smallvec;
extern crate style;
extern crate time;
#[cfg(target_os = "windows")]
extern crate truetype;
extern crate ucd;
extern crate unicode_bidi;
extern crate unicode_script;
@ -61,7 +75,8 @@ extern crate xi_unicode;
extern crate xml5ever;
// Fonts
#[macro_use] pub mod font;
#[macro_use]
pub mod font;
pub mod font_cache_thread;
pub mod font_context;
pub mod font_template;

View file

@ -116,21 +116,18 @@ struct FontFamily {
struct FontAlias {
from: String,
to: String,
weight: Option<i32>
weight: Option<i32>,
}
struct FontList {
families: Vec<FontFamily>,
aliases: Vec<FontAlias>
aliases: Vec<FontAlias>,
}
impl FontList {
fn new() -> FontList {
// Possible paths containing the font mapping xml file.
let paths = [
"/etc/fonts.xml",
"/system/etc/system_fonts.xml"
];
let paths = ["/etc/fonts.xml", "/system/etc/system_fonts.xml"];
// Try to load and parse paths until one of them success.
let mut result = None;
@ -146,7 +143,7 @@ impl FontList {
None => FontList {
families: Self::fallback_font_families(),
aliases: Vec::new(),
}
},
}
}
@ -154,25 +151,26 @@ impl FontList {
fn from_path(path: &str) -> Option<FontList> {
let xml = match Self::load_file(path) {
Ok(xml) => xml,
_=> { return None; },
_ => {
return None;
},
};
let dom: RcDom = parse_document(RcDom::default(), Default::default())
.one(xml);
let dom: RcDom = parse_document(RcDom::default(), Default::default()).one(xml);
let doc = &dom.document;
// find familyset root node
let children = doc.children.borrow();
let familyset = children.iter().find(|child| {
match child.data {
NodeData::Element { ref name, .. } => &*name.local == "familyset",
_ => false,
}
let familyset = children.iter().find(|child| match child.data {
NodeData::Element { ref name, .. } => &*name.local == "familyset",
_ => false,
});
let familyset = match familyset {
Some(node) => node,
_ => { return None; }
_ => {
return None;
},
};
// Parse familyset node
@ -181,7 +179,11 @@ impl FontList {
for node in familyset.children.borrow().iter() {
match node.data {
NodeData::Element { ref name, ref attrs, .. } => {
NodeData::Element {
ref name,
ref attrs,
..
} => {
if &*name.local == "family" {
Self::parse_family(&node, attrs, &mut families);
} else if &*name.local == "alias" {
@ -191,13 +193,13 @@ impl FontList {
}
}
},
_=> {}
_ => {},
}
}
Some(FontList {
families: families,
aliases: aliases
aliases: aliases,
})
}
@ -209,17 +211,16 @@ impl FontList {
("Droid Sans", "DroidSans.ttf"),
];
alternatives.iter().filter(|item| {
Path::new(&Self::font_absolute_path(item.1)).exists()
}).map(|item| {
FontFamily {
alternatives
.iter()
.filter(|item| Path::new(&Self::font_absolute_path(item.1)).exists())
.map(|item| FontFamily {
name: item.0.into(),
fonts: vec![Font {
filename: item.1.into(),
weight: None,
}]
}
}). collect()
}],
}).collect()
}
// All Android fonts are located in /system/fonts
@ -227,15 +228,14 @@ impl FontList {
format!("/system/fonts/{}", filename)
}
fn find_family(&self, name: &str) -> Option<&FontFamily>{
fn find_family(&self, name: &str) -> Option<&FontFamily> {
self.families.iter().find(|f| f.name == name)
}
fn find_alias(&self, name: &str) -> Option<&FontAlias>{
fn find_alias(&self, name: &str) -> Option<&FontAlias> {
self.aliases.iter().find(|f| f.from == name)
}
fn load_file(path: &str) -> Result<String, io::Error> {
let mut file = File::open(path)?;
let mut content = String::new();
@ -253,14 +253,16 @@ impl FontList {
// <font weight="300" style="italic">Roboto-LightItalic.ttf</font>
// <font weight="400" style="normal">Roboto-Regular.ttf</font>
// </family>
fn parse_family(familyset: &Node, attrs: &RefCell<Vec<Attribute>>, out:&mut Vec<FontFamily>) {
fn parse_family(familyset: &Node, attrs: &RefCell<Vec<Attribute>>, out: &mut Vec<FontFamily>) {
// Fallback to old Android API v17 xml format if required
let using_api_17 = familyset.children.borrow().iter().any(|node| {
match node.data {
let using_api_17 = familyset
.children
.borrow()
.iter()
.any(|node| match node.data {
NodeData::Element { ref name, .. } => &*name.local == "nameset",
_=> false,
}
});
_ => false,
});
if using_api_17 {
Self::parse_family_v17(familyset, out);
return;
@ -269,25 +271,31 @@ impl FontList {
// Parse family name
let name = match Self::find_attrib("name", attrs) {
Some(name) => name,
_ => { return; },
_ => {
return;
},
};
let mut fonts = Vec::new();
// Parse font variants
for node in familyset.children.borrow().iter() {
match node.data {
NodeData::Element { ref name, ref attrs, .. } => {
NodeData::Element {
ref name,
ref attrs,
..
} => {
if &*name.local == "font" {
FontList::parse_font(&node, attrs, &mut fonts);
}
},
_=> {}
_ => {},
}
}
out.push(FontFamily {
name: name,
fonts: fonts
fonts: fonts,
});
}
@ -308,7 +316,7 @@ impl FontList {
// <file>Roboto-BoldItalic.ttf</file>
// </fileset>
// </family>
fn parse_family_v17(familyset: &Node, out:&mut Vec<FontFamily>) {
fn parse_family_v17(familyset: &Node, out: &mut Vec<FontFamily>) {
let mut nameset = Vec::new();
let mut fileset = Vec::new();
for node in familyset.children.borrow().iter() {
@ -320,21 +328,23 @@ impl FontList {
Self::collect_contents_with_tag(node, "file", &mut fileset);
}
},
_=> {}
_ => {},
}
}
// Create a families for each variation
for name in nameset {
let fonts: Vec<Font> = fileset.iter().map(|f| Font {
filename: f.clone(),
weight: None,
}).collect();
let fonts: Vec<Font> = fileset
.iter()
.map(|f| Font {
filename: f.clone(),
weight: None,
}).collect();
if !fonts.is_empty() {
out.push(FontFamily {
name: name,
fonts: fonts
fonts: fonts,
})
}
}
@ -342,11 +352,13 @@ impl FontList {
// Example:
// <font weight="100" style="normal">Roboto-Thin.ttf</font>
fn parse_font(node: &Node, attrs: &RefCell<Vec<Attribute>>, out:&mut Vec<Font>) {
fn parse_font(node: &Node, attrs: &RefCell<Vec<Attribute>>, out: &mut Vec<Font>) {
// Parse font filename
let filename = match Self::text_content(node) {
Some(filename) => filename,
_ => { return; }
_ => {
return;
},
};
// Parse font weight
@ -367,17 +379,21 @@ impl FontList {
// <alias name="helvetica" to="sans-serif" />
// <alias name="tahoma" to="sans-serif" />
// <alias name="verdana" to="sans-serif" />
fn parse_alias(attrs: &RefCell<Vec<Attribute>>, out:&mut Vec<FontAlias>) {
fn parse_alias(attrs: &RefCell<Vec<Attribute>>, out: &mut Vec<FontAlias>) {
// Parse alias name and referenced font
let from = match Self::find_attrib("name", attrs) {
Some(from) => from,
_ => { return; },
_ => {
return;
},
};
// Parse referenced font
let to = match Self::find_attrib("to", attrs) {
Some(to) => to,
_ => { return; },
_ => {
return;
},
};
// Parse optional weight filter
@ -391,23 +407,28 @@ impl FontList {
}
fn find_attrib(name: &str, attrs: &RefCell<Vec<Attribute>>) -> Option<String> {
attrs.borrow().iter().find(|attr| &*attr.name.local == name).map(|s| String::from(&s.value))
attrs
.borrow()
.iter()
.find(|attr| &*attr.name.local == name)
.map(|s| String::from(&s.value))
}
fn text_content(node: &Node) -> Option<String> {
node.children.borrow().get(0).and_then(|child| {
match child.data {
node.children
.borrow()
.get(0)
.and_then(|child| match child.data {
NodeData::Text { ref contents } => {
let mut result = String::new();
result.push_str(&contents.borrow());
Some(result)
},
_ => None
}
})
_ => None,
})
}
fn collect_contents_with_tag(node: &Node, tag: &str, out:&mut Vec<String>) {
fn collect_contents_with_tag(node: &Node, tag: &str, out: &mut Vec<String>) {
for child in node.children.borrow().iter() {
match child.data {
NodeData::Element { ref name, .. } => {
@ -417,14 +438,17 @@ impl FontList {
}
}
},
_=> {}
_ => {},
}
}
}
}
// Functions used by FontCacheThread
pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) {
pub fn for_each_available_family<F>(mut callback: F)
where
F: FnMut(String),
{
for family in &FONT_LIST.families {
callback(family.name.clone());
}
@ -434,7 +458,8 @@ pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) {
}
pub fn for_each_variation<F>(family_name: &str, mut callback: F)
where F: FnMut(String)
where
F: FnMut(String),
{
if let Some(family) = FONT_LIST.find_family(family_name) {
for font in &family.fonts {
@ -453,7 +478,7 @@ pub fn for_each_variation<F>(family_name: &str, mut callback: F)
callback(FontList::font_absolute_path(&font.filename))
}
},
_ => {}
_ => {},
}
}
}
@ -473,46 +498,44 @@ pub fn system_default_family(generic_name: &str) -> Option<String> {
// Based on gfxAndroidPlatform::GetCommonFallbackFonts() in Gecko
pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
let mut families = vec!();
let mut families = vec![];
if let Some(block) = codepoint.and_then(|c| c.block()) {
match block {
UnicodeBlock::Armenian => {
families.push("Droid Sans Armenian");
}
},
UnicodeBlock::Hebrew => {
families.push("Droid Sans Hebrew");
}
},
UnicodeBlock::Arabic => {
families.push("Droid Sans Arabic");
}
},
UnicodeBlock::Devanagari => {
families.push("Noto Sans Devanagari");
families.push("Droid Sans Devanagari");
}
},
UnicodeBlock::Tamil => {
families.push("Noto Sans Tamil");
families.push("Droid Sans Tamil");
}
},
UnicodeBlock::Thai => {
families.push("Noto Sans Thai");
families.push("Droid Sans Thai");
}
},
UnicodeBlock::Georgian |
UnicodeBlock::GeorgianSupplement => {
UnicodeBlock::Georgian | UnicodeBlock::GeorgianSupplement => {
families.push("Droid Sans Georgian");
}
},
UnicodeBlock::Ethiopic |
UnicodeBlock::EthiopicSupplement => {
UnicodeBlock::Ethiopic | UnicodeBlock::EthiopicSupplement => {
families.push("Droid Sans Ethiopic");
}
},
_ => {
if is_cjk(codepoint.unwrap()) {
@ -520,7 +543,7 @@ pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
families.push("Noto Sans CJK JP");
families.push("Droid Sans Japanese");
}
}
},
}
}

View file

@ -98,14 +98,21 @@ fn create_face(
let face_index = 0 as FT_Long;
let result = if let Some(ref bytes) = template.bytes {
FT_New_Memory_Face(lib, bytes.as_ptr(), bytes.len() as FT_Long, face_index, &mut face)
FT_New_Memory_Face(
lib,
bytes.as_ptr(),
bytes.len() as FT_Long,
face_index,
&mut face,
)
} else {
// This will trigger a synchronous file read in the layout thread, which we may want to
// revisit at some point. See discussion here:
//
// https://github.com/servo/servo/pull/20506#issuecomment-378838800
let filename = CString::new(&*template.identifier).expect("filename contains NUL byte!");
let filename =
CString::new(&*template.identifier).expect("filename contains NUL byte!");
FT_New_Face(lib, filename.as_ptr(), face_index, &mut face)
};
@ -122,25 +129,27 @@ fn create_face(
}
impl FontHandleMethods for FontHandle {
fn new_from_template(fctx: &FontContextHandle,
template: Arc<FontTemplateData>,
pt_size: Option<Au>)
-> Result<FontHandle, ()> {
fn new_from_template(
fctx: &FontContextHandle,
template: Arc<FontTemplateData>,
pt_size: Option<Au>,
) -> Result<FontHandle, ()> {
let ft_ctx: FT_Library = fctx.ctx.ctx;
if ft_ctx.is_null() { return Err(()); }
if ft_ctx.is_null() {
return Err(());
}
let face = create_face(ft_ctx, &template, pt_size)?;
let mut handle = FontHandle {
face: face,
font_data: template.clone(),
handle: fctx.clone(),
can_do_fast_shaping: false,
face: face,
font_data: template.clone(),
handle: fctx.clone(),
can_do_fast_shaping: false,
};
// TODO (#11310): Implement basic support for GPOS and GSUB.
handle.can_do_fast_shaping = handle.has_table(KERN) &&
!handle.has_table(GPOS) &&
!handle.has_table(GSUB);
handle.can_do_fast_shaping =
handle.has_table(KERN) && !handle.has_table(GPOS) && !handle.has_table(GSUB);
Ok(handle)
}
@ -203,7 +212,7 @@ impl FontHandleMethods for FontHandle {
7 => FontStretchKeyword::Expanded,
8 => FontStretchKeyword::ExtraExpanded,
9 => FontStretchKeyword::UltraExpanded,
_ => FontStretchKeyword::Normal
_ => FontStretchKeyword::Normal,
}
} else {
FontStretchKeyword::Normal
@ -218,20 +227,26 @@ impl FontHandleMethods for FontHandle {
if idx != 0 as FT_UInt {
Some(idx as GlyphId)
} else {
debug!("Invalid codepoint: U+{:04X} ('{}')", codepoint as u32, codepoint);
debug!(
"Invalid codepoint: U+{:04X} ('{}')",
codepoint as u32, codepoint
);
None
}
}
}
fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId)
-> FractionalPixel {
fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel {
assert!(!self.face.is_null());
let mut delta = FT_Vector { x: 0, y: 0 };
unsafe {
FT_Get_Kerning(self.face, first_glyph, second_glyph,
FT_Kerning_Mode::FT_KERNING_DEFAULT as FT_UInt,
&mut delta);
FT_Get_Kerning(
self.face,
first_glyph,
second_glyph,
FT_Kerning_Mode::FT_KERNING_DEFAULT as FT_UInt,
&mut delta,
);
}
fixed_to_float_ft(delta.x as i32)
}
@ -243,9 +258,7 @@ impl FontHandleMethods for FontHandle {
fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
assert!(!self.face.is_null());
unsafe {
let res = FT_Load_Glyph(self.face,
glyph as FT_UInt,
GLYPH_LOAD_FLAGS);
let res = FT_Load_Glyph(self.face, glyph as FT_UInt, GLYPH_LOAD_FLAGS);
if succeeded(res) {
let void_glyph = (*self.face).glyph;
let slot: FT_GlyphSlot = mem::transmute(void_glyph);
@ -291,23 +304,24 @@ impl FontHandleMethods for FontHandle {
x_height = self.font_units_to_au(os2.sx_height as f64);
}
let average_advance = self.glyph_index('0')
.and_then(|idx| self.glyph_h_advance(idx))
.map_or(max_advance, |advance| self.font_units_to_au(advance));
let average_advance = self
.glyph_index('0')
.and_then(|idx| self.glyph_h_advance(idx))
.map_or(max_advance, |advance| self.font_units_to_au(advance));
let metrics = FontMetrics {
underline_size: underline_size,
underline_size: underline_size,
underline_offset: underline_offset,
strikeout_size: strikeout_size,
strikeout_size: strikeout_size,
strikeout_offset: strikeout_offset,
leading: leading,
x_height: x_height,
em_size: em_size,
ascent: ascent,
descent: -descent, // linux font's seem to use the opposite sign from mac
max_advance: max_advance,
average_advance: average_advance,
line_gap: height,
leading: leading,
x_height: x_height,
em_size: em_size,
ascent: ascent,
descent: -descent, // linux font's seem to use the opposite sign from mac
max_advance: max_advance,
average_advance: average_advance,
line_gap: height,
};
debug!("Font metrics (@{}px): {:?}", em_size.to_f32_px(), metrics);
@ -320,13 +334,25 @@ impl FontHandleMethods for FontHandle {
unsafe {
// Get the length
let mut len = 0;
if !succeeded(FT_Load_Sfnt_Table(self.face, tag, 0, ptr::null_mut(), &mut len)) {
return None
if !succeeded(FT_Load_Sfnt_Table(
self.face,
tag,
0,
ptr::null_mut(),
&mut len,
)) {
return None;
}
// Get the bytes
let mut buf = vec![0u8; len as usize];
if !succeeded(FT_Load_Sfnt_Table(self.face, tag, 0, buf.as_mut_ptr(), &mut len)) {
return None
if !succeeded(FT_Load_Sfnt_Table(
self.face,
tag,
0,
buf.as_mut_ptr(),
&mut len,
)) {
return None;
}
Some(FontTable { buffer: buf })
}
@ -338,25 +364,33 @@ impl FontHandleMethods for FontHandle {
}
impl<'a> FontHandle {
fn set_char_size(face: FT_Face, pt_size: Au) -> Result<(), ()>{
fn set_char_size(face: FT_Face, pt_size: Au) -> Result<(), ()> {
let char_size = pt_size.to_f64_px() * 64.0 + 0.5;
unsafe {
let result = FT_Set_Char_Size(face, char_size as FT_F26Dot6, 0, 0, 0);
if succeeded(result) { Ok(()) } else { Err(()) }
if succeeded(result) {
Ok(())
} else {
Err(())
}
}
}
fn has_table(&self, tag: FontTableTag) -> bool {
unsafe {
succeeded(FT_Load_Sfnt_Table(self.face, tag as FT_ULong, 0, ptr::null_mut(), &mut 0))
succeeded(FT_Load_Sfnt_Table(
self.face,
tag as FT_ULong,
0,
ptr::null_mut(),
&mut 0,
))
}
}
fn face_rec_mut(&'a self) -> &'a mut FT_FaceRec {
unsafe {
&mut (*self.face)
}
unsafe { &mut (*self.face) }
}
fn font_units_to_au(&self, value: f64) -> Au {
@ -378,11 +412,12 @@ impl<'a> FontHandle {
fn os2_table(&self) -> Option<OS2Table> {
unsafe {
let os2 = FT_Get_Sfnt_Table(self.face_rec_mut(), FT_Sfnt_Tag::FT_SFNT_OS2) as *mut TT_OS2;
let os2 =
FT_Get_Sfnt_Table(self.face_rec_mut(), FT_Sfnt_Tag::FT_SFNT_OS2) as *mut TT_OS2;
let valid = !os2.is_null() && (*os2).version != 0xffff;
if !valid {
return None
return None;
}
Some(OS2Table {

View file

@ -23,10 +23,10 @@ pub struct User {
size: usize,
}
extern fn ft_alloc(mem: FT_Memory, req_size: c_long) -> *mut c_void {
extern "C" fn ft_alloc(mem: FT_Memory, req_size: c_long) -> *mut c_void {
unsafe {
let ptr = malloc(req_size as usize);
let ptr = ptr as *mut c_void; // libc::c_void vs std::os::raw::c_void
let ptr = ptr as *mut c_void; // libc::c_void vs std::os::raw::c_void
let actual_size = usable_size(ptr);
let user = (*mem).user as *mut User;
(*user).size += actual_size;
@ -34,7 +34,7 @@ extern fn ft_alloc(mem: FT_Memory, req_size: c_long) -> *mut c_void {
}
}
extern fn ft_free(mem: FT_Memory, ptr: *mut c_void) {
extern "C" fn ft_free(mem: FT_Memory, ptr: *mut c_void) {
unsafe {
let actual_size = usable_size(ptr);
let user = (*mem).user as *mut User;
@ -43,8 +43,12 @@ extern fn ft_free(mem: FT_Memory, ptr: *mut c_void) {
}
}
extern fn ft_realloc(mem: FT_Memory, _old_size: c_long, new_req_size: c_long,
old_ptr: *mut c_void) -> *mut c_void {
extern "C" fn ft_realloc(
mem: FT_Memory,
_old_size: c_long,
new_req_size: c_long,
old_ptr: *mut c_void,
) -> *mut c_void {
unsafe {
let old_actual_size = usable_size(old_ptr);
let new_ptr = realloc(old_ptr as *mut _, new_req_size as usize);
@ -108,9 +112,7 @@ impl MallocSizeOf for FontContextHandle {
impl FontContextHandle {
pub fn new() -> FontContextHandle {
let user = Box::into_raw(Box::new(User {
size: 0,
}));
let user = Box::into_raw(Box::new(User { size: 0 }));
let mem = Box::into_raw(Box::new(FT_MemoryRec_ {
user: user as *mut c_void,
alloc: Some(ft_alloc),
@ -121,12 +123,18 @@ impl FontContextHandle {
let mut ctx: FT_Library = ptr::null_mut();
let result = FT_New_Library(mem, &mut ctx);
if !succeeded(result) { panic!("Unable to initialize FreeType library"); }
if !succeeded(result) {
panic!("Unable to initialize FreeType library");
}
FT_Add_Default_Modules(ctx);
FontContextHandle {
ctx: Rc::new(FreeTypeLibraryHandle { ctx: ctx, mem: mem, user: user }),
ctx: Rc::new(FreeTypeLibraryHandle {
ctx: ctx,
mem: mem,
user: user,
}),
}
}
}

View file

@ -20,7 +20,10 @@ static FC_FILE: &'static [u8] = b"file\0";
static FC_INDEX: &'static [u8] = b"index\0";
static FC_FONTFORMAT: &'static [u8] = b"fontformat\0";
pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) {
pub fn for_each_available_family<F>(mut callback: F)
where
F: FnMut(String),
{
unsafe {
let config = FcConfigGetCurrent();
let font_set = FcConfigGetFonts(config, FcSetSystem);
@ -29,19 +32,21 @@ pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) {
let mut family: *mut FcChar8 = ptr::null_mut();
let mut format: *mut FcChar8 = ptr::null_mut();
let mut v: c_int = 0;
if FcPatternGetString(*font, FC_FONTFORMAT.as_ptr() as *mut c_char, v, &mut format) != FcResultMatch {
if FcPatternGetString(*font, FC_FONTFORMAT.as_ptr() as *mut c_char, v, &mut format) !=
FcResultMatch
{
continue;
}
// Skip bitmap fonts. They aren't supported by FreeType.
let fontformat = c_str_to_string(format as *const c_char);
if fontformat != "TrueType" &&
fontformat != "CFF" &&
fontformat != "Type 1" {
if fontformat != "TrueType" && fontformat != "CFF" && fontformat != "Type 1" {
continue;
}
while FcPatternGetString(*font, FC_FAMILY.as_ptr() as *mut c_char, v, &mut family) == FcResultMatch {
while FcPatternGetString(*font, FC_FAMILY.as_ptr() as *mut c_char, v, &mut family) ==
FcResultMatch
{
let family_name = c_str_to_string(family as *const c_char);
callback(family_name);
v += 1;
@ -51,7 +56,8 @@ pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) {
}
pub fn for_each_variation<F>(family_name: &str, mut callback: F)
where F: FnMut(String)
where
F: FnMut(String),
{
debug!("getting variations for {}", family_name);
unsafe {
@ -62,7 +68,11 @@ pub fn for_each_variation<F>(family_name: &str, mut callback: F)
assert!(!pattern.is_null());
let family_name_c = CString::new(family_name).unwrap();
let family_name = family_name_c.as_ptr();
let ok = FcPatternAddString(pattern, FC_FAMILY.as_ptr() as *mut c_char, family_name as *mut FcChar8);
let ok = FcPatternAddString(
pattern,
FC_FAMILY.as_ptr() as *mut c_char,
family_name as *mut FcChar8,
);
assert_ne!(ok, 0);
let object_set = FcObjectSetCreate();
@ -85,7 +95,8 @@ pub fn for_each_variation<F>(family_name: &str, mut callback: F)
panic!();
};
let mut index: libc::c_int = 0;
let result = FcPatternGetInteger(*font, FC_INDEX.as_ptr() as *mut c_char, 0, &mut index);
let result =
FcPatternGetInteger(*font, FC_INDEX.as_ptr() as *mut c_char, 0, &mut index);
let index = if result == FcResultMatch {
index
} else {
@ -119,7 +130,12 @@ pub fn system_default_family(generic_name: &str) -> Option<String> {
let family_name = if result == FcResultMatch {
let mut match_string: *mut FcChar8 = ptr::null_mut();
FcPatternGetString(family_match, FC_FAMILY.as_ptr() as *mut c_char, 0, &mut match_string);
FcPatternGetString(
family_match,
FC_FAMILY.as_ptr() as *mut c_char,
0,
&mut match_string,
);
let result = c_str_to_string(match_string as *const c_char);
FcPatternDestroy(family_match);
Some(result)
@ -136,12 +152,7 @@ pub static SANS_SERIF_FONT_FAMILY: &'static str = "DejaVu Sans";
// Based on gfxPlatformGtk::GetCommonFallbackFonts() in Gecko
pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
let mut families = vec!(
"DejaVu Serif",
"FreeSerif",
"DejaVu Sans",
"FreeSans",
);
let mut families = vec!["DejaVu Serif", "FreeSerif", "DejaVu Sans", "FreeSans"];
if let Some(codepoint) = codepoint {
if is_cjk(codepoint) {

View file

@ -15,7 +15,6 @@ use webrender_api::NativeFontHandle;
#[derive(Deserialize, Serialize)]
pub struct FontTemplateData {
// If you add members here, review the Debug impl below
pub bytes: Option<Vec<u8>>,
pub identifier: Atom,
}
@ -23,9 +22,11 @@ pub struct FontTemplateData {
impl fmt::Debug for FontTemplateData {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("FontTemplateData")
.field("bytes", &self.bytes.as_ref().map(|b| format!("[{} bytes]", b.len())))
.field("identifier", &self.identifier)
.finish()
.field(
"bytes",
&self.bytes.as_ref().map(|b| format!("[{} bytes]", b.len())),
).field("identifier", &self.identifier)
.finish()
}
}

View file

@ -3,7 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/// Implementation of Quartz (CoreGraphics) fonts.
use app_units::Au;
use byteorder::{BigEndian, ByteOrder};
use core_foundation::base::CFIndex;
@ -111,8 +110,8 @@ impl FontHandle {
return None;
}
let pt_per_font_unit = self.ctfont.pt_size() as f64 /
self.ctfont.units_per_em() as f64;
let pt_per_font_unit =
self.ctfont.pt_size() as f64 / self.ctfont.units_per_em() as f64;
result.px_per_font_unit = pt_to_px(pt_per_font_unit);
}
start = end;
@ -160,15 +159,15 @@ impl fmt::Debug for CachedKernTable {
}
}
impl FontHandleMethods for FontHandle {
fn new_from_template(_fctx: &FontContextHandle,
template: Arc<FontTemplateData>,
pt_size: Option<Au>)
-> Result<FontHandle, ()> {
fn new_from_template(
_fctx: &FontContextHandle,
template: Arc<FontTemplateData>,
pt_size: Option<Au>,
) -> Result<FontHandle, ()> {
let size = match pt_size {
Some(s) => s.to_f64_px(),
None => 0.0
None => 0.0,
};
match template.ctfont(size) {
Some(ref ctfont) => {
@ -181,13 +180,11 @@ impl FontHandleMethods for FontHandle {
handle.h_kern_subtable = handle.find_h_kern_subtable();
// TODO (#11310): Implement basic support for GPOS and GSUB.
handle.can_do_fast_shaping = handle.h_kern_subtable.is_some() &&
handle.table_for_tag(GPOS).is_none() &&
handle.table_for_tag(GSUB).is_none();
handle.table_for_tag(GPOS).is_none() &&
handle.table_for_tag(GSUB).is_none();
Ok(handle)
}
None => {
Err(())
}
},
None => Err(()),
}
}
@ -213,13 +210,14 @@ impl FontHandleMethods for FontHandle {
}
fn boldness(&self) -> FontWeight {
let normalized = self.ctfont.all_traits().normalized_weight(); // [-1.0, 1.0]
let normalized = self.ctfont.all_traits().normalized_weight(); // [-1.0, 1.0]
// TODO(emilio): It may make sense to make this range [.01, 10.0], to
// align with css-fonts-4's range of [1, 1000].
let normalized = if normalized <= 0.0 {
4.0 + normalized * 3.0 // [1.0, 4.0]
4.0 + normalized * 3.0 // [1.0, 4.0]
} else {
4.0 + normalized * 5.0 // [4.0, 9.0]
4.0 + normalized * 5.0 // [4.0, 9.0]
}; // [1.0, 9.0], centered on 4.0
FontWeight(normalized as f32 * 100.)
}
@ -228,7 +226,7 @@ impl FontHandleMethods for FontHandle {
use style::values::computed::Percentage;
use style::values::generics::NonNegative;
let normalized = self.ctfont.all_traits().normalized_width(); // [-1.0, 1.0]
let normalized = self.ctfont.all_traits().normalized_width(); // [-1.0, 1.0]
FontStretch(NonNegative(Percentage(normalized as f32 + 1.0)))
}
@ -237,9 +235,9 @@ impl FontHandleMethods for FontHandle {
let mut glyphs: [CGGlyph; 1] = [0 as CGGlyph];
let count: CFIndex = 1;
let result = self.ctfont.get_glyphs_for_characters(&characters[0],
&mut glyphs[0],
count);
let result = self
.ctfont
.get_glyphs_for_characters(&characters[0], &mut glyphs[0], count);
if !result {
// No glyph for this character
@ -265,10 +263,12 @@ impl FontHandleMethods for FontHandle {
fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
let glyphs = [glyph as CGGlyph];
let advance = self.ctfont.get_advances_for_glyphs(kCTFontDefaultOrientation,
&glyphs[0],
ptr::null_mut(),
1);
let advance = self.ctfont.get_advances_for_glyphs(
kCTFontDefaultOrientation,
&glyphs[0],
ptr::null_mut(),
1,
);
Some(advance as FractionalPixel)
}
@ -283,39 +283,42 @@ impl FontHandleMethods for FontHandle {
let line_gap = (ascent + descent + leading + 0.5).floor();
let max_advance_width = au_from_pt(bounding_rect.size.width as f64);
let average_advance = self.glyph_index('0')
.and_then(|idx| self.glyph_h_advance(idx))
.map(Au::from_f64_px)
.unwrap_or(max_advance_width);
let average_advance = self
.glyph_index('0')
.and_then(|idx| self.glyph_h_advance(idx))
.map(Au::from_f64_px)
.unwrap_or(max_advance_width);
let metrics = FontMetrics {
underline_size: au_from_pt(self.ctfont.underline_thickness() as f64),
underline_size: au_from_pt(self.ctfont.underline_thickness() as f64),
// TODO(Issue #201): underline metrics are not reliable. Have to pull out of font table
// directly.
//
// see also: https://bugs.webkit.org/show_bug.cgi?id=16768
// see also: https://bugreports.qt-project.org/browse/QTBUG-13364
underline_offset: au_from_pt(self.ctfont.underline_position() as f64),
strikeout_size: Au(0), // FIXME(Issue #942)
strikeout_size: Au(0), // FIXME(Issue #942)
strikeout_offset: Au(0), // FIXME(Issue #942)
leading: au_from_pt(leading),
x_height: au_from_pt((self.ctfont.x_height() as f64) * scale),
em_size: em_size,
ascent: au_from_pt(ascent * scale),
descent: au_from_pt(descent * scale),
max_advance: max_advance_width,
average_advance: average_advance,
line_gap: Au::from_f64_px(line_gap),
leading: au_from_pt(leading),
x_height: au_from_pt((self.ctfont.x_height() as f64) * scale),
em_size: em_size,
ascent: au_from_pt(ascent * scale),
descent: au_from_pt(descent * scale),
max_advance: max_advance_width,
average_advance: average_advance,
line_gap: Au::from_f64_px(line_gap),
};
debug!("Font metrics (@{} pt): {:?}", self.ctfont.pt_size() as f64, metrics);
debug!(
"Font metrics (@{} pt): {:?}",
self.ctfont.pt_size() as f64,
metrics
);
metrics
}
fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
let result: Option<CFData> = self.ctfont.get_font_table(tag);
result.and_then(|data| {
Some(FontTable::wrap(data))
})
result.and_then(|data| Some(FontTable::wrap(data)))
}
fn identifier(&self) -> Atom {

View file

@ -6,7 +6,7 @@ use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
#[derive(Clone, Debug)]
pub struct FontContextHandle {
ctx: ()
ctx: (),
}
impl FontContextHandle {

View file

@ -6,14 +6,20 @@ use core_text;
use text::util::unicode_plane;
use ucd::{Codepoint, UnicodeBlock};
pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) {
pub fn for_each_available_family<F>(mut callback: F)
where
F: FnMut(String),
{
let family_names = core_text::font_collection::get_family_names();
for family_name in family_names.iter() {
callback(family_name.to_string());
}
}
pub fn for_each_variation<F>(family_name: &str, mut callback: F) where F: FnMut(String) {
pub fn for_each_variation<F>(family_name: &str, mut callback: F)
where
F: FnMut(String),
{
debug!("Looking for faces of family: {}", family_name);
let family_collection = core_text::font_collection::create_for_family(family_name);
@ -31,7 +37,7 @@ pub fn system_default_family(_generic_name: &str) -> Option<String> {
// Based on gfxPlatformMac::GetCommonFallbackFonts() in Gecko
pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
let mut families = vec!("Lucida Grande");
let mut families = vec!["Lucida Grande"];
if let Some(codepoint) = codepoint {
match unicode_plane(codepoint) {
@ -45,66 +51,65 @@ pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
UnicodeBlock::Thaana |
UnicodeBlock::NKo => {
families.push("Geeza Pro");
}
},
UnicodeBlock::Devanagari => {
families.push("Devanagari Sangam MN");
}
},
UnicodeBlock::Gurmukhi => {
families.push("Gurmukhi MN");
}
},
UnicodeBlock::Gujarati => {
families.push("Gujarati Sangam MN");
}
},
UnicodeBlock::Tamil => {
families.push("Tamil MN");
}
},
UnicodeBlock::Lao => {
families.push("Lao MN");
}
},
UnicodeBlock::Tibetan => {
families.push("Songti SC");
}
},
UnicodeBlock::Myanmar => {
families.push("Myanmar MN");
}
},
UnicodeBlock::Ethiopic |
UnicodeBlock::EthiopicSupplement |
UnicodeBlock::EthiopicExtended |
UnicodeBlock::EthiopicExtendedA => {
families.push("Kefa");
}
},
UnicodeBlock::Cherokee => {
families.push("Plantagenet Cherokee");
}
},
UnicodeBlock::UnifiedCanadianAboriginalSyllabics |
UnicodeBlock::UnifiedCanadianAboriginalSyllabicsExtended => {
families.push("Euphemia UCAS");
}
},
UnicodeBlock::Mongolian |
UnicodeBlock::YiSyllables |
UnicodeBlock::YiRadicals => {
families.push("STHeiti");
}
},
UnicodeBlock::Khmer |
UnicodeBlock::KhmerSymbols => {
UnicodeBlock::Khmer | UnicodeBlock::KhmerSymbols => {
families.push("Khmer MN");
}
},
UnicodeBlock::TaiLe => {
families.push("Microsoft Tai Le");
}
},
UnicodeBlock::GeneralPunctuation |
UnicodeBlock::SuperscriptsandSubscripts |
@ -134,11 +139,11 @@ pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
families.push("Apple Symbols");
families.push("Menlo");
families.push("STIXGeneral");
}
},
UnicodeBlock::BraillePatterns => {
families.push("Apple Braille");
}
},
UnicodeBlock::Bopomofo |
UnicodeBlock::HangulCompatibilityJamo |
@ -147,7 +152,7 @@ pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
UnicodeBlock::CJKStrokes |
UnicodeBlock::KatakanaPhoneticExtensions => {
families.push("Hiragino Sans GB");
}
},
UnicodeBlock::YijingHexagramSymbols |
UnicodeBlock::CyrillicExtendedB |
@ -158,27 +163,27 @@ pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
UnicodeBlock::HalfwidthandFullwidthForms |
UnicodeBlock::Specials => {
families.push("Apple Symbols");
}
},
_ => {}
_ => {},
}
}
}
},
// https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Multilingual_Plane
1 => {
families.push("Apple Symbols");
families.push("STIXGeneral");
}
},
// https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Ideographic_Plane
2 => {
// Systems with MS Office may have these fonts
families.push("MingLiU-ExtB");
families.push("SimSun-ExtB");
}
},
_ => {}
_ => {},
}
}

View file

@ -27,7 +27,6 @@ use webrender_api::NativeFontHandle;
#[derive(Deserialize, Serialize)]
pub struct FontTemplateData {
// If you add members here, review the Debug impl below
/// The `CTFont` object, if present. This is cached here so that we don't have to keep creating
/// `CTFont` instances over and over. It can always be recreated from the `identifier` and/or
/// `font_data` fields.
@ -38,21 +37,21 @@ pub struct FontTemplateData {
ctfont: CachedCTFont,
pub identifier: Atom,
pub font_data: Option<Arc<Vec<u8>>>
pub font_data: Option<Arc<Vec<u8>>>,
}
impl fmt::Debug for FontTemplateData {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("FontTemplateData")
.field("ctfont", &self.ctfont)
.field("identifier", &self.identifier)
.field(
"font_data",
&self.font_data
.field("ctfont", &self.ctfont)
.field("identifier", &self.identifier)
.field(
"font_data",
&self
.font_data
.as_ref()
.map(|bytes| format!("[{} bytes]", bytes.len()))
)
.finish()
.map(|bytes| format!("[{} bytes]", bytes.len())),
).finish()
}
}
@ -64,7 +63,7 @@ impl FontTemplateData {
Ok(FontTemplateData {
ctfont: CachedCTFont(Mutex::new(HashMap::new())),
identifier: identifier.to_owned(),
font_data: font_data.map(Arc::new)
font_data: font_data.map(Arc::new),
})
}
@ -83,10 +82,10 @@ impl FontTemplateData {
match cgfont_result {
Ok(cgfont) => {
Some(core_text::font::new_from_CGFont(&cgfont, clamped_pt_size))
}
Err(_) => None
},
Err(_) => None,
}
}
},
None => core_text::font::new_from_name(&*self.identifier, clamped_pt_size).ok(),
};
if let Some(ctfont) = ctfont {
@ -104,16 +103,23 @@ impl FontTemplateData {
return font_data;
}
let path = ServoUrl::parse(&*self.ctfont(0.0)
.expect("No Core Text font available!")
.url()
.expect("No URL for Core Text font!")
.get_string()
.to_string()).expect("Couldn't parse Core Text font URL!")
.as_url().to_file_path()
.expect("Core Text font didn't name a path!");
let path = ServoUrl::parse(
&*self
.ctfont(0.0)
.expect("No Core Text font available!")
.url()
.expect("No URL for Core Text font!")
.get_string()
.to_string(),
).expect("Couldn't parse Core Text font URL!")
.as_url()
.to_file_path()
.expect("Core Text font didn't name a path!");
let mut bytes = Vec::new();
File::open(path).expect("Couldn't open font file!").read_to_end(&mut bytes).unwrap();
File::open(path)
.expect("Couldn't open font file!")
.read_to_end(&mut bytes)
.unwrap();
bytes
}
@ -125,7 +131,8 @@ impl FontTemplateData {
/// Returns the native font that underlies this font template, if applicable.
pub fn native_font(&self) -> Option<NativeFontHandle> {
self.ctfont(0.0).map(|ctfont| NativeFontHandle(ctfont.copy_to_CGFont()))
self.ctfont(0.0)
.map(|ctfont| NativeFontHandle(ctfont.copy_to_CGFont()))
}
}
@ -140,14 +147,19 @@ impl Deref for CachedCTFont {
}
impl Serialize for CachedCTFont {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_none()
}
}
impl<'de> Deserialize<'de> for CachedCTFont {
fn deserialize<D>(deserializer: D) -> Result<CachedCTFont, D::Error>
where D: Deserializer<'de> {
where
D: Deserializer<'de>,
{
struct NoneOptionVisitor;
impl<'de> Visitor<'de> for NoneOptionVisitor {
@ -158,7 +170,10 @@ impl<'de> Deserialize<'de> for CachedCTFont {
}
#[inline]
fn visit_none<E>(self) -> Result<CachedCTFont, E> where E: Error {
fn visit_none<E>(self) -> Result<CachedCTFont, E>
where
E: Error,
{
Ok(CachedCTFont(Mutex::new(HashMap::new())))
}
}

View file

@ -23,7 +23,9 @@ mod freetype {
/// Creates a String from the given null-terminated buffer.
/// Panics if the buffer does not contain UTF-8.
unsafe fn c_str_to_string(s: *const c_char) -> String {
str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap().to_owned()
str::from_utf8(CStr::from_ptr(s).to_bytes())
.unwrap()
.to_owned()
}
pub mod font;

View file

@ -27,10 +27,18 @@ use text::glyph::GlyphId;
use truetype;
// 1em = 12pt = 16px, assuming 72 points per inch and 96 px per inch
fn pt_to_px(pt: f64) -> f64 { pt / 72. * 96. }
fn em_to_px(em: f64) -> f64 { em * 16. }
fn au_from_em(em: f64) -> Au { Au::from_f64_px(em_to_px(em)) }
fn au_from_pt(pt: f64) -> Au { Au::from_f64_px(pt_to_px(pt)) }
fn pt_to_px(pt: f64) -> f64 {
pt / 72. * 96.
}
fn em_to_px(em: f64) -> f64 {
em * 16.
}
fn au_from_em(em: f64) -> Au {
Au::from_f64_px(em_to_px(em))
}
fn au_from_pt(pt: f64) -> Au {
Au::from_f64_px(pt_to_px(pt))
}
pub struct FontTable {
data: Vec<u8>,
@ -38,7 +46,9 @@ pub struct FontTable {
impl FontTable {
pub fn wrap(data: &[u8]) -> FontTable {
FontTable { data: data.to_vec() }
FontTable {
data: data.to_vec(),
}
}
}
@ -139,7 +149,7 @@ impl FontInfo {
} else {
return Err(());
}
}
},
};
let mut os2_table_cursor = Cursor::new(os2_table_bytes.as_ref().unwrap());
@ -163,18 +173,20 @@ impl FontInfo {
let weight = StyleFontWeight(weight_val as f32);
let stretch = StyleFontStretch(NonNegative(match min(9, max(1, width_val)) {
1 => FontStretchKeyword::UltraCondensed,
2 => FontStretchKeyword::ExtraCondensed,
3 => FontStretchKeyword::Condensed,
4 => FontStretchKeyword::SemiCondensed,
5 => FontStretchKeyword::Normal,
6 => FontStretchKeyword::SemiExpanded,
7 => FontStretchKeyword::Expanded,
8 => FontStretchKeyword::ExtraExpanded,
9 => FontStretchKeyword::UltraExpanded,
_ => return Err(()),
}.compute()));
let stretch = StyleFontStretch(NonNegative(
match min(9, max(1, width_val)) {
1 => FontStretchKeyword::UltraCondensed,
2 => FontStretchKeyword::ExtraCondensed,
3 => FontStretchKeyword::Condensed,
4 => FontStretchKeyword::SemiCondensed,
5 => FontStretchKeyword::Normal,
6 => FontStretchKeyword::SemiExpanded,
7 => FontStretchKeyword::Expanded,
8 => FontStretchKeyword::ExtraExpanded,
9 => FontStretchKeyword::UltraExpanded,
_ => return Err(()),
}.compute(),
));
let style = if italic_bool {
GenericFontStyle::Italic
@ -212,18 +224,20 @@ impl FontInfo {
// slightly blacker black
FontWeight::ExtraBlack => 1000.,
});
let stretch = StyleFontStretch(NonNegative(match font.stretch() {
FontStretch::Undefined => FontStretchKeyword::Normal,
FontStretch::UltraCondensed => FontStretchKeyword::UltraCondensed,
FontStretch::ExtraCondensed => FontStretchKeyword::ExtraCondensed,
FontStretch::Condensed => FontStretchKeyword::Condensed,
FontStretch::SemiCondensed => FontStretchKeyword::SemiCondensed,
FontStretch::Normal => FontStretchKeyword::Normal,
FontStretch::SemiExpanded => FontStretchKeyword::SemiExpanded,
FontStretch::Expanded => FontStretchKeyword::Expanded,
FontStretch::ExtraExpanded => FontStretchKeyword::ExtraExpanded,
FontStretch::UltraExpanded => FontStretchKeyword::UltraExpanded,
}.compute()));
let stretch = StyleFontStretch(NonNegative(
match font.stretch() {
FontStretch::Undefined => FontStretchKeyword::Normal,
FontStretch::UltraCondensed => FontStretchKeyword::UltraCondensed,
FontStretch::ExtraCondensed => FontStretchKeyword::ExtraCondensed,
FontStretch::Condensed => FontStretchKeyword::Condensed,
FontStretch::SemiCondensed => FontStretchKeyword::SemiCondensed,
FontStretch::Normal => FontStretchKeyword::Normal,
FontStretch::SemiExpanded => FontStretchKeyword::SemiExpanded,
FontStretch::Expanded => FontStretchKeyword::Expanded,
FontStretch::ExtraExpanded => FontStretchKeyword::ExtraExpanded,
FontStretch::UltraExpanded => FontStretchKeyword::UltraExpanded,
}.compute(),
));
Ok(FontInfo {
family_name: font.family_name(),
@ -246,13 +260,14 @@ pub struct FontHandle {
scaled_du_to_px: f32,
}
impl FontHandle {
}
impl FontHandle {}
impl FontHandleMethods for FontHandle {
fn new_from_template(_: &FontContextHandle, template: Arc<FontTemplateData>, pt_size: Option<Au>)
-> Result<Self, ()>
{
fn new_from_template(
_: &FontContextHandle,
template: Arc<FontTemplateData>,
pt_size: Option<Au>,
) -> Result<Self, ()> {
let (info, face) = if let Some(ref raw_font) = template.bytes {
let font_file = FontFile::new_from_data(&raw_font);
if font_file.is_none() {
@ -260,7 +275,9 @@ impl FontHandleMethods for FontHandle {
return Err(());
}
let face = font_file.unwrap().create_face(0, dwrote::DWRITE_FONT_SIMULATIONS_NONE);
let face = font_file
.unwrap()
.create_face(0, dwrote::DWRITE_FONT_SIMULATIONS_NONE);
let info = FontInfo::new_from_face(&face)?;
(info, face)
} else {
@ -350,32 +367,34 @@ impl FontHandleMethods for FontHandle {
let dm = self.face.metrics();
let au_from_du = |du| -> Au { Au::from_f32_px(du as f32 * self.du_to_px) };
let au_from_du_s = |du| -> Au { Au:: from_f32_px(du as f32 * self.scaled_du_to_px) };
let au_from_du_s = |du| -> Au { Au::from_f32_px(du as f32 * self.scaled_du_to_px) };
// anything that we calculate and don't just pull out of self.face.metrics
// is pulled out here for clarity
let leading = dm.ascent - dm.capHeight;
let metrics = FontMetrics {
underline_size: au_from_du(dm.underlineThickness as i32),
underline_size: au_from_du(dm.underlineThickness as i32),
underline_offset: au_from_du_s(dm.underlinePosition as i32),
strikeout_size: au_from_du(dm.strikethroughThickness as i32),
strikeout_size: au_from_du(dm.strikethroughThickness as i32),
strikeout_offset: au_from_du_s(dm.strikethroughPosition as i32),
leading: au_from_du_s(leading as i32),
x_height: au_from_du_s(dm.xHeight as i32),
em_size: au_from_em(self.em_size as f64),
ascent: au_from_du_s(dm.ascent as i32),
descent: au_from_du_s(dm.descent as i32),
max_advance: au_from_pt(0.0), // FIXME
average_advance: au_from_pt(0.0), // FIXME
line_gap: au_from_du_s((dm.ascent + dm.descent + dm.lineGap as u16) as i32),
leading: au_from_du_s(leading as i32),
x_height: au_from_du_s(dm.xHeight as i32),
em_size: au_from_em(self.em_size as f64),
ascent: au_from_du_s(dm.ascent as i32),
descent: au_from_du_s(dm.descent as i32),
max_advance: au_from_pt(0.0), // FIXME
average_advance: au_from_pt(0.0), // FIXME
line_gap: au_from_du_s((dm.ascent + dm.descent + dm.lineGap as u16) as i32),
};
debug!("Font metrics (@{} pt): {:?}", self.em_size * 12., metrics);
metrics
}
fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
self.face.get_font_table(tag).map(|bytes| FontTable { data: bytes })
self.face
.get_font_table(tag)
.map(|bytes| FontTable { data: bytes })
}
fn identifier(&self) -> Atom {

View file

@ -21,7 +21,10 @@ pub fn system_default_family(_: &str) -> Option<String> {
Some("Verdana".to_owned())
}
pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) {
pub fn for_each_available_family<F>(mut callback: F)
where
F: FnMut(String),
{
let system_fc = FontCollection::system();
for family in system_fc.families_iter() {
callback(family.name());
@ -37,7 +40,10 @@ pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) {
// we'll stringify, and then put them all in a HashMap with
// the actual FontDescriptor there.
pub fn for_each_variation<F>(family_name: &str, mut callback: F) where F: FnMut(String) {
pub fn for_each_variation<F>(family_name: &str, mut callback: F)
where
F: FnMut(String),
{
let system_fc = FontCollection::system();
if let Some(family) = system_fc.get_font_family_by_name(family_name) {
let count = family.get_font_count();
@ -65,12 +71,14 @@ pub fn descriptor_from_atom(ident: &Atom) -> FontDescriptor {
pub fn font_from_atom(ident: &Atom) -> Font {
let fonts = FONT_ATOM_MAP.lock().unwrap();
FontCollection::system().get_font_from_descriptor(fonts.get(ident).unwrap()).unwrap()
FontCollection::system()
.get_font_from_descriptor(fonts.get(ident).unwrap())
.unwrap()
}
// Based on gfxWindowsPlatform::GetCommonFallbackFonts() in Gecko
pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
let mut families = vec!("Arial");
let mut families = vec!["Arial"];
if let Some(codepoint) = codepoint {
match unicode_plane(codepoint) {
@ -83,31 +91,29 @@ pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
UnicodeBlock::Hebrew => {
families.push("Estrangelo Edessa");
families.push("Cambria");
}
},
UnicodeBlock::Arabic |
UnicodeBlock::ArabicSupplement => {
UnicodeBlock::Arabic | UnicodeBlock::ArabicSupplement => {
families.push("Microsoft Uighur");
}
},
UnicodeBlock::Syriac => {
families.push("Estrangelo Edessa");
}
},
UnicodeBlock::Thaana => {
families.push("MV Boli");
}
},
UnicodeBlock::NKo => {
families.push("Ebrima");
}
},
UnicodeBlock::Devanagari |
UnicodeBlock::Bengali => {
UnicodeBlock::Devanagari | UnicodeBlock::Bengali => {
families.push("Nirmala UI");
families.push("Utsaah");
families.push("Aparajita");
}
},
UnicodeBlock::Gurmukhi |
UnicodeBlock::Gujarati |
@ -123,21 +129,21 @@ pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
UnicodeBlock::SundaneseSupplement |
UnicodeBlock::VedicExtensions => {
families.push("Nirmala UI");
}
},
UnicodeBlock::Thai => {
families.push("Leelawadee UI");
}
},
UnicodeBlock::Lao => {
families.push("Lao UI");
}
},
UnicodeBlock::Myanmar |
UnicodeBlock::MyanmarExtendedA |
UnicodeBlock::MyanmarExtendedB => {
families.push("Myanmar Text");
}
},
UnicodeBlock::HangulJamo |
UnicodeBlock::HangulJamoExtendedA |
@ -145,48 +151,47 @@ pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
UnicodeBlock::HangulJamoExtendedB |
UnicodeBlock::HangulCompatibilityJamo => {
families.push("Malgun Gothic");
}
},
UnicodeBlock::Ethiopic |
UnicodeBlock::EthiopicSupplement |
UnicodeBlock::EthiopicExtended |
UnicodeBlock::EthiopicExtendedA => {
families.push("Nyala");
}
},
UnicodeBlock::Cherokee => {
families.push("Plantagenet Cherokee");
}
},
UnicodeBlock::UnifiedCanadianAboriginalSyllabics |
UnicodeBlock::UnifiedCanadianAboriginalSyllabicsExtended => {
families.push("Euphemia");
families.push("Segoe UI");
}
},
UnicodeBlock::Khmer |
UnicodeBlock::KhmerSymbols => {
UnicodeBlock::Khmer | UnicodeBlock::KhmerSymbols => {
families.push("Khmer UI");
families.push("Leelawadee UI");
}
},
UnicodeBlock::Mongolian => {
families.push("Mongolian Baiti");
}
},
UnicodeBlock::TaiLe => {
families.push("Microsoft Tai Le");
}
},
UnicodeBlock::NewTaiLue => {
families.push("Microsoft New Tai Lue");
}
},
UnicodeBlock::Buginese |
UnicodeBlock::TaiTham |
UnicodeBlock::CombiningDiacriticalMarksExtended => {
families.push("Leelawadee UI");
}
},
UnicodeBlock::GeneralPunctuation |
UnicodeBlock::SuperscriptsandSubscripts |
@ -220,7 +225,7 @@ pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
families.push("Meiryo");
families.push("Lucida Sans Unicode");
families.push("Ebrima");
}
},
UnicodeBlock::GeorgianSupplement |
UnicodeBlock::Tifinagh |
@ -232,11 +237,11 @@ pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
families.push("Segoe UI");
families.push("Segoe UI Symbol");
families.push("Meiryo");
}
},
UnicodeBlock::BraillePatterns => {
families.push("Segoe UI Symbol");
}
},
UnicodeBlock::CJKSymbolsandPunctuation |
UnicodeBlock::Hiragana |
@ -249,21 +254,20 @@ pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
UnicodeBlock::CJKUnifiedIdeographs => {
families.push("Microsoft YaHei");
families.push("Yu Gothic");
}
},
UnicodeBlock::EnclosedCJKLettersandMonths => {
families.push("Malgun Gothic");
}
},
UnicodeBlock::YijingHexagramSymbols => {
families.push("Segoe UI Symbol");
}
},
UnicodeBlock::YiSyllables |
UnicodeBlock::YiRadicals => {
UnicodeBlock::YiSyllables | UnicodeBlock::YiRadicals => {
families.push("Microsoft Yi Baiti");
families.push("Segoe UI");
}
},
UnicodeBlock::Vai |
UnicodeBlock::CyrillicExtendedB |
@ -273,36 +277,34 @@ pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
families.push("Ebrima");
families.push("Segoe UI");
families.push("Cambria Math");
}
},
UnicodeBlock::SylotiNagri |
UnicodeBlock::CommonIndicNumberForms |
UnicodeBlock::Phagspa |
UnicodeBlock::Saurashtra |
UnicodeBlock::DevanagariExtended => {
families.push("Microsoft PhagsPa");
families.push("Nirmala UI");
}
families.push("Microsoft PhagsPa");
families.push("Nirmala UI");
},
UnicodeBlock::KayahLi |
UnicodeBlock::Rejang |
UnicodeBlock::Javanese => {
families.push("Malgun Gothic");
families.push("Javanese Text");
families.push("Leelawadee UI");
}
UnicodeBlock::KayahLi | UnicodeBlock::Rejang | UnicodeBlock::Javanese => {
families.push("Malgun Gothic");
families.push("Javanese Text");
families.push("Leelawadee UI");
},
UnicodeBlock::AlphabeticPresentationForms => {
families.push("Microsoft Uighur");
families.push("Gabriola");
families.push("Sylfaen");
}
},
UnicodeBlock::ArabicPresentationFormsA |
UnicodeBlock::ArabicPresentationFormsB => {
families.push("Traditional Arabic");
families.push("Arabic Typesetting");
}
},
UnicodeBlock::VariationSelectors |
UnicodeBlock::VerticalForms |
@ -312,12 +314,12 @@ pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
UnicodeBlock::HalfwidthandFullwidthForms |
UnicodeBlock::Specials => {
families.push("Microsoft JhengHei");
}
},
_ => {}
_ => {},
}
}
}
},
// https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Multilingual_Plane
1 => {
@ -325,9 +327,9 @@ pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
families.push("Ebrima");
families.push("Nirmala UI");
families.push("Cambria Math");
}
},
_ => {}
_ => {},
}
}

View file

@ -11,7 +11,6 @@ use webrender_api::NativeFontHandle;
#[derive(Deserialize, Serialize)]
pub struct FontTemplateData {
// If you add members here, review the Debug impl below
pub bytes: Option<Vec<u8>>,
pub identifier: Atom,
}
@ -19,20 +18,22 @@ pub struct FontTemplateData {
impl fmt::Debug for FontTemplateData {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("FontTemplateData")
.field(
"bytes",
&self.bytes
.field(
"bytes",
&self
.bytes
.as_ref()
.map(|bytes| format!("[{} bytes]", bytes.len()))
)
.field("identifier", &self.identifier)
.finish()
.map(|bytes| format!("[{} bytes]", bytes.len())),
).field("identifier", &self.identifier)
.finish()
}
}
impl FontTemplateData {
pub fn new(identifier: Atom,
font_data: Option<Vec<u8>>) -> Result<FontTemplateData, io::Error> {
pub fn new(
identifier: Atom,
font_data: Option<Vec<u8>>,
) -> Result<FontTemplateData, io::Error> {
Ok(FontTemplateData {
bytes: font_data,
identifier: identifier,

View file

@ -58,26 +58,24 @@ impl TestFontSource {
}
fn add_face(family: &mut FontTemplates, name: &str, identifier: Option<&str>) {
let mut path: PathBuf = [
env!("CARGO_MANIFEST_DIR"),
"tests",
"support",
"CSSTest",
].iter().collect();
let mut path: PathBuf = [env!("CARGO_MANIFEST_DIR"), "tests", "support", "CSSTest"]
.iter()
.collect();
path.push(format!("{}.ttf", name));
let file = File::open(path).unwrap();
let identifier = Atom::from(identifier.unwrap_or(name));
family.add_template(
identifier,
Some(file.bytes().map(|b| b.unwrap()).collect())
)
family.add_template(identifier, Some(file.bytes().map(|b| b.unwrap()).collect()))
}
}
impl FontSource for TestFontSource {
fn get_font_instance(&mut self, _key: webrender_api::FontKey, _size: Au) -> webrender_api::FontInstanceKey {
fn get_font_instance(
&mut self,
_key: webrender_api::FontKey,
_size: Au,
) -> webrender_api::FontInstanceKey {
webrender_api::FontInstanceKey(webrender_api::IdNamespace(0), 0)
}
@ -92,11 +90,9 @@ impl FontSource for TestFontSource {
self.families
.get_mut(family_descriptor.name())
.and_then(|family| family.find_font_for_style(&template_descriptor, handle))
.map(|template| {
FontTemplateInfo {
font_template: template,
font_key: webrender_api::FontKey(webrender_api::IdNamespace(0), 0),
}
.map(|template| FontTemplateInfo {
font_template: template,
font_key: webrender_api::FontKey(webrender_api::IdNamespace(0), 0),
})
}
}
@ -116,12 +112,14 @@ fn style() -> FontStyleStruct {
}
fn font_family(names: Vec<&str>) -> FontFamily {
let names: Vec<SingleFontFamily> = names.into_iter().map(|name|
SingleFontFamily::FamilyName(FamilyName {
name: Atom::from(name),
syntax: FamilyNameSyntax::Quoted,
})
).collect();
let names: Vec<SingleFontFamily> = names
.into_iter()
.map(|name| {
SingleFontFamily::FamilyName(FamilyName {
name: Atom::from(name),
syntax: FamilyNameSyntax::Quoted,
})
}).collect();
FontFamily(FontFamilyList::new(names.into_boxed_slice()))
}
@ -156,19 +154,36 @@ fn test_font_group_find_by_codepoint() {
let mut context = FontContext::new(source);
let mut style = style();
style.set_font_family(font_family(vec!("CSSTest ASCII", "CSSTest Basic")));
style.set_font_family(font_family(vec!["CSSTest ASCII", "CSSTest Basic"]));
let group = context.font_group(Arc::new(style));
let font = group.borrow_mut().find_by_codepoint(&mut context, 'a').unwrap();
let font = group
.borrow_mut()
.find_by_codepoint(&mut context, 'a')
.unwrap();
assert_eq!(&*font.borrow().identifier(), "csstest-ascii");
assert_eq!(count.get(), 1, "only the first font in the list should have been loaded");
assert_eq!(
count.get(),
1,
"only the first font in the list should have been loaded"
);
let font = group.borrow_mut().find_by_codepoint(&mut context, 'a').unwrap();
let font = group
.borrow_mut()
.find_by_codepoint(&mut context, 'a')
.unwrap();
assert_eq!(&*font.borrow().identifier(), "csstest-ascii");
assert_eq!(count.get(), 1, "we shouldn't load the same font a second time");
assert_eq!(
count.get(),
1,
"we shouldn't load the same font a second time"
);
let font = group.borrow_mut().find_by_codepoint(&mut context, 'á').unwrap();
let font = group
.borrow_mut()
.find_by_codepoint(&mut context, 'á')
.unwrap();
assert_eq!(&*font.borrow().identifier(), "csstest-basic-regular");
assert_eq!(count.get(), 2, "both fonts should now have been loaded");
}
@ -179,19 +194,27 @@ fn test_font_fallback() {
let mut context = FontContext::new(source);
let mut style = style();
style.set_font_family(font_family(vec!("CSSTest ASCII")));
style.set_font_family(font_family(vec!["CSSTest ASCII"]));
let group = context.font_group(Arc::new(style));
let font = group.borrow_mut().find_by_codepoint(&mut context, 'a').unwrap();
let font = group
.borrow_mut()
.find_by_codepoint(&mut context, 'a')
.unwrap();
assert_eq!(
&*font.borrow().identifier(), "csstest-ascii",
&*font.borrow().identifier(),
"csstest-ascii",
"a family in the group should be used if there is a matching glyph"
);
let font = group.borrow_mut().find_by_codepoint(&mut context, 'á').unwrap();
let font = group
.borrow_mut()
.find_by_codepoint(&mut context, 'á')
.unwrap();
assert_eq!(
&*font.borrow().identifier(), "fallback",
&*font.borrow().identifier(),
"fallback",
"a fallback font should be used if there is no matching glyph in the group"
);
}
@ -212,10 +235,8 @@ fn test_font_template_is_cached() {
pt_size: Au(10),
};
let family_descriptor = FontFamilyDescriptor::new(
FontFamilyName::from("CSSTest Basic"),
FontSearchScope::Any,
);
let family_descriptor =
FontFamilyDescriptor::new(FontFamilyName::from("CSSTest Basic"), FontSearchScope::Any);
let font1 = context.font(&font_descriptor, &family_descriptor).unwrap();
@ -228,5 +249,9 @@ fn test_font_template_is_cached() {
"the same font should not have been returned"
);
assert_eq!(count.get(), 1, "we should only have fetched the template data from the cache thread once");
assert_eq!(
count.get(),
1,
"we should only have fetched the template data from the cache thread once"
);
}

View file

@ -2,9 +2,12 @@
* 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/. */
#[cfg(not(target_os = "macos"))] extern crate gfx;
#[cfg(not(target_os = "macos"))] extern crate servo_atoms;
#[cfg(not(target_os = "macos"))] extern crate style;
#[cfg(not(target_os = "macos"))]
extern crate gfx;
#[cfg(not(target_os = "macos"))]
extern crate servo_atoms;
#[cfg(not(target_os = "macos"))]
extern crate style;
// Test doesn't yet run on Mac, see https://github.com/servo/servo/pull/19928 for explanation.
#[cfg(not(target_os = "macos"))]
@ -28,14 +31,16 @@ fn test_font_template_descriptor() {
"support",
"dejavu-fonts-ttf-2.37",
"ttf",
].iter().collect();
]
.iter()
.collect();
path.push(format!("{}.ttf", filename));
let file = File::open(path).unwrap();
let mut template = FontTemplate::new(
Atom::from(filename),
Some(file.bytes().map(|b| b.unwrap()).collect())
Some(file.bytes().map(|b| b.unwrap()).collect()),
).unwrap();
let context = FontContextHandle::new();
@ -43,27 +48,39 @@ fn test_font_template_descriptor() {
template.descriptor(&context).unwrap()
}
assert_eq!(descriptor("DejaVuSans"), FontTemplateDescriptor {
weight: FontWeight::normal(),
stretch: FontStretch::hundred(),
style: FontStyle::Normal,
});
assert_eq!(
descriptor("DejaVuSans"),
FontTemplateDescriptor {
weight: FontWeight::normal(),
stretch: FontStretch::hundred(),
style: FontStyle::Normal,
}
);
assert_eq!(descriptor("DejaVuSans-Bold"), FontTemplateDescriptor {
weight: FontWeight::bold(),
stretch: FontStretch::hundred(),
style: FontStyle::Normal,
});
assert_eq!(
descriptor("DejaVuSans-Bold"),
FontTemplateDescriptor {
weight: FontWeight::bold(),
stretch: FontStretch::hundred(),
style: FontStyle::Normal,
}
);
assert_eq!(descriptor("DejaVuSans-Oblique"), FontTemplateDescriptor {
weight: FontWeight::normal(),
stretch: FontStretch::hundred(),
style: FontStyle::Italic,
});
assert_eq!(
descriptor("DejaVuSans-Oblique"),
FontTemplateDescriptor {
weight: FontWeight::normal(),
stretch: FontStretch::hundred(),
style: FontStyle::Italic,
}
);
assert_eq!(descriptor("DejaVuSansCondensed-BoldOblique"), FontTemplateDescriptor {
weight: FontWeight::bold(),
stretch: FontStretch(NonNegative(Percentage(0.875))),
style: FontStyle::Italic,
});
assert_eq!(
descriptor("DejaVuSansCondensed-BoldOblique"),
FontTemplateDescriptor {
weight: FontWeight::bold(),
stretch: FontStretch(NonNegative(Percentage(0.875))),
style: FontStyle::Italic,
}
);
}

View file

@ -29,26 +29,13 @@ fn test_transform_compress_none() {
#[test]
fn test_transform_discard_newline() {
let test_strs = [
(" foo bar",
" foo bar"),
("foo bar ",
"foo bar "),
("foo\n bar",
"foo bar"),
("foo \nbar",
"foo bar"),
(" foo bar \nbaz",
" foo bar baz"),
("foo bar baz",
"foo bar baz"),
("foobarbaz\n\n",
"foobarbaz"),
(" foo bar", " foo bar"),
("foo bar ", "foo bar "),
("foo\n bar", "foo bar"),
("foo \nbar", "foo bar"),
(" foo bar \nbaz", " foo bar baz"),
("foo bar baz", "foo bar baz"),
("foobarbaz\n\n", "foobarbaz"),
];
let mode = CompressionMode::DiscardNewline;
@ -62,26 +49,13 @@ fn test_transform_discard_newline() {
#[test]
fn test_transform_compress_whitespace() {
let test_strs = [
(" foo bar",
"foo bar"),
("foo bar ",
"foo bar "),
("foo\n bar",
"foo\n bar"),
("foo \nbar",
"foo \nbar"),
(" foo bar \nbaz",
"foo bar \nbaz"),
("foo bar baz",
"foo bar baz"),
("foobarbaz\n\n",
"foobarbaz\n\n"),
(" foo bar", "foo bar"),
("foo bar ", "foo bar "),
("foo\n bar", "foo\n bar"),
("foo \nbar", "foo \nbar"),
(" foo bar \nbaz", "foo bar \nbaz"),
("foo bar baz", "foo bar baz"),
("foobarbaz\n\n", "foobarbaz\n\n"),
];
let mode = CompressionMode::CompressWhitespace;
@ -95,26 +69,13 @@ fn test_transform_compress_whitespace() {
#[test]
fn test_transform_compress_whitespace_newline() {
let test_strs = vec![
(" foo bar",
"foo bar"),
("foo bar ",
"foo bar "),
("foo\n bar",
"foo bar"),
("foo \nbar",
"foo bar"),
(" foo bar \nbaz",
"foo bar baz"),
("foo bar baz",
"foo bar baz"),
("foobarbaz\n\n",
"foobarbaz "),
(" foo bar", "foo bar"),
("foo bar ", "foo bar "),
("foo\n bar", "foo bar"),
("foo \nbar", "foo bar"),
(" foo bar \nbaz", "foo bar baz"),
("foo bar baz", "foo bar baz"),
("foobarbaz\n\n", "foobarbaz "),
];
let mode = CompressionMode::CompressWhitespaceNewline;
@ -128,29 +89,14 @@ fn test_transform_compress_whitespace_newline() {
#[test]
fn test_transform_compress_whitespace_newline_no_incoming() {
let test_strs = [
(" foo bar",
" foo bar"),
("\nfoo bar",
" foo bar"),
("foo bar ",
"foo bar "),
("foo\n bar",
"foo bar"),
("foo \nbar",
"foo bar"),
(" foo bar \nbaz",
" foo bar baz"),
("foo bar baz",
"foo bar baz"),
("foobarbaz\n\n",
"foobarbaz "),
(" foo bar", " foo bar"),
("\nfoo bar", " foo bar"),
("foo bar ", "foo bar "),
("foo\n bar", "foo bar"),
("foo \nbar", "foo bar"),
(" foo bar \nbaz", " foo bar baz"),
("foo bar baz", "foo bar baz"),
("foobarbaz\n\n", "foobarbaz "),
];
let mode = CompressionMode::CompressWhitespaceNewline;

View file

@ -4,7 +4,10 @@
use app_units::Au;
use euclid::Point2D;
#[cfg(all(feature = "unstable", any(target_feature = "sse2", target_feature = "neon")))]
#[cfg(all(
feature = "unstable",
any(target_feature = "sse2", target_feature = "neon")
))]
use packed_simd::u32x4;
use range::{self, EachIndex, Range, RangeIndex};
use std::{fmt, mem, u16};
@ -28,9 +31,7 @@ pub struct GlyphEntry {
impl GlyphEntry {
fn new(value: u32) -> GlyphEntry {
GlyphEntry {
value: value,
}
GlyphEntry { value: value }
}
fn initial() -> GlyphEntry {
@ -54,11 +55,11 @@ impl GlyphEntry {
fn complex(starts_cluster: bool, starts_ligature: bool, glyph_count: usize) -> GlyphEntry {
assert!(glyph_count <= u16::MAX as usize);
debug!("creating complex glyph entry: starts_cluster={}, starts_ligature={}, \
glyph_count={}",
starts_cluster,
starts_ligature,
glyph_count);
debug!(
"creating complex glyph entry: starts_cluster={}, starts_ligature={}, \
glyph_count={}",
starts_cluster, starts_ligature, glyph_count
);
GlyphEntry::new(glyph_count as u32)
}
@ -73,16 +74,16 @@ pub type GlyphId = u32;
// TODO: make this more type-safe.
const FLAG_CHAR_IS_SPACE: u32 = 0x40000000;
const FLAG_CHAR_IS_SPACE: u32 = 0x40000000;
#[cfg(feature = "unstable")]
#[cfg(any(target_feature = "sse2", target_feature = "neon"))]
const FLAG_CHAR_IS_SPACE_SHIFT: u32 = 30;
const FLAG_IS_SIMPLE_GLYPH: u32 = 0x80000000;
const FLAG_IS_SIMPLE_GLYPH: u32 = 0x80000000;
// glyph advance; in Au's.
const GLYPH_ADVANCE_MASK: u32 = 0x3FFF0000;
const GLYPH_ADVANCE_SHIFT: u32 = 16;
const GLYPH_ID_MASK: u32 = 0x0000FFFF;
const GLYPH_ADVANCE_MASK: u32 = 0x3FFF0000;
const GLYPH_ADVANCE_SHIFT: u32 = 16;
const GLYPH_ID_MASK: u32 = 0x0000FFFF;
// Non-simple glyphs (more than one glyph per char; missing glyph,
// newline, tab, large advance, or nonzero x/y offsets) may have one
@ -91,7 +92,7 @@ const GLYPH_ID_MASK: u32 = 0x0000FFFF;
// unicode char.
// The number of detailed glyphs for this char.
const GLYPH_COUNT_MASK: u32 = 0x0000FFFF;
const GLYPH_COUNT_MASK: u32 = 0x0000FFFF;
fn is_simple_glyph_id(id: GlyphId) -> bool {
((id as u32) & GLYPH_ID_MASK) == id
@ -205,8 +206,8 @@ struct DetailedGlyphStore {
impl<'a> DetailedGlyphStore {
fn new() -> DetailedGlyphStore {
DetailedGlyphStore {
detail_buffer: vec!(), // TODO: default size?
detail_lookup: vec!(),
detail_buffer: vec![], // TODO: default size?
detail_lookup: vec![],
lookup_is_sorted: false,
}
}
@ -217,7 +218,10 @@ impl<'a> DetailedGlyphStore {
detail_offset: self.detail_buffer.len(),
};
debug!("Adding entry[off={:?}] for detailed glyphs: {:?}", entry_offset, glyphs);
debug!(
"Adding entry[off={:?}] for detailed glyphs: {:?}",
entry_offset, glyphs
);
/* TODO: don't actually assert this until asserts are compiled
in/out based on severity, debug/release, etc. This assertion
@ -235,9 +239,15 @@ impl<'a> DetailedGlyphStore {
self.lookup_is_sorted = false;
}
fn detailed_glyphs_for_entry(&'a self, entry_offset: ByteIndex, count: u16)
-> &'a [DetailedGlyph] {
debug!("Requesting detailed glyphs[n={}] for entry[off={:?}]", count, entry_offset);
fn detailed_glyphs_for_entry(
&'a self,
entry_offset: ByteIndex,
count: u16,
) -> &'a [DetailedGlyph] {
debug!(
"Requesting detailed glyphs[n={}] for entry[off={:?}]",
count, entry_offset
);
// FIXME: Is this right? --pcwalton
// TODO: should fix this somewhere else
@ -253,18 +263,21 @@ impl<'a> DetailedGlyphStore {
detail_offset: 0, // unused
};
let i = self.detail_lookup.binary_search(&key)
let i = self
.detail_lookup
.binary_search(&key)
.expect("Invalid index not found in detailed glyph lookup table!");
let main_detail_offset = self.detail_lookup[i].detail_offset;
assert!(main_detail_offset + (count as usize) <= self.detail_buffer.len());
// return a slice into the buffer
&self.detail_buffer[main_detail_offset .. main_detail_offset + count as usize]
&self.detail_buffer[main_detail_offset..main_detail_offset + count as usize]
}
fn detailed_glyph_with_index(&'a self,
entry_offset: ByteIndex,
detail_offset: u16)
-> &'a DetailedGlyph {
fn detailed_glyph_with_index(
&'a self,
entry_offset: ByteIndex,
detail_offset: u16,
) -> &'a DetailedGlyph {
assert!((detail_offset as usize) <= self.detail_buffer.len());
assert!(self.lookup_is_sorted);
@ -273,7 +286,9 @@ impl<'a> DetailedGlyphStore {
detail_offset: 0, // unused
};
let i = self.detail_lookup.binary_search(&key)
let i = self
.detail_lookup
.binary_search(&key)
.expect("Invalid index not found in detailed glyph lookup table!");
let main_detail_offset = self.detail_lookup[i].detail_offset;
assert!(main_detail_offset + (detail_offset as usize) < self.detail_buffer.len());
@ -290,7 +305,7 @@ impl<'a> DetailedGlyphStore {
// immutable locations thus don't play well with freezing.
// Thar be dragons here. You have been warned. (Tips accepted.)
let mut unsorted_records: Vec<DetailedGlyphRecord> = vec!();
let mut unsorted_records: Vec<DetailedGlyphRecord> = vec![];
mem::swap(&mut self.detail_lookup, &mut unsorted_records);
let mut mut_records: Vec<DetailedGlyphRecord> = unsorted_records;
mut_records.sort_by(|a, b| {
@ -320,12 +335,13 @@ pub struct GlyphData {
impl GlyphData {
/// Creates a new entry for one glyph.
pub fn new(id: GlyphId,
advance: Au,
offset: Option<Point2D<Au>>,
cluster_start: bool,
ligature_start: bool)
-> GlyphData {
pub fn new(
id: GlyphId,
advance: Au,
offset: Option<Point2D<Au>>,
cluster_start: bool,
ligature_start: bool,
) -> GlyphData {
GlyphData {
id: id,
advance: advance,
@ -351,8 +367,11 @@ impl<'a> GlyphInfo<'a> {
match self {
GlyphInfo::Simple(store, entry_i) => store.entry_buffer[entry_i.to_usize()].id(),
GlyphInfo::Detail(store, entry_i, detail_j) => {
store.detail_store.detailed_glyph_with_index(entry_i, detail_j).id
}
store
.detail_store
.detailed_glyph_with_index(entry_i, detail_j)
.id
},
}
}
@ -362,8 +381,11 @@ impl<'a> GlyphInfo<'a> {
match self {
GlyphInfo::Simple(store, entry_i) => store.entry_buffer[entry_i.to_usize()].advance(),
GlyphInfo::Detail(store, entry_i, detail_j) => {
store.detail_store.detailed_glyph_with_index(entry_i, detail_j).advance
}
store
.detail_store
.detailed_glyph_with_index(entry_i, detail_j)
.advance
},
}
}
@ -371,9 +393,12 @@ impl<'a> GlyphInfo<'a> {
pub fn offset(self) -> Option<Point2D<Au>> {
match self {
GlyphInfo::Simple(_, _) => None,
GlyphInfo::Detail(store, entry_i, detail_j) => {
Some(store.detail_store.detailed_glyph_with_index(entry_i, detail_j).offset)
}
GlyphInfo::Detail(store, entry_i, detail_j) => Some(
store
.detail_store
.detailed_glyph_with_index(entry_i, detail_j)
.offset,
),
}
}
@ -477,14 +502,11 @@ impl<'a> GlyphStore {
}
/// Adds a single glyph.
pub fn add_glyph_for_byte_index(&mut self,
i: ByteIndex,
character: char,
data: &GlyphData) {
pub fn add_glyph_for_byte_index(&mut self, i: ByteIndex, character: char, data: &GlyphData) {
let glyph_is_compressible = is_simple_glyph_id(data.id) &&
is_simple_advance(data.advance) &&
data.offset == Point2D::zero() &&
data.cluster_start; // others are stored in detail buffer
data.offset == Point2D::zero() &&
data.cluster_start; // others are stored in detail buffer
debug_assert!(data.ligature_start); // can't compress ligature continuation glyphs.
debug_assert!(i < self.len());
@ -512,20 +534,29 @@ impl<'a> GlyphStore {
let glyph_count = data_for_glyphs.len();
let first_glyph_data = data_for_glyphs[0];
let glyphs_vec: Vec<DetailedGlyph> = (0..glyph_count).map(|i| {
DetailedGlyph::new(data_for_glyphs[i].id,
data_for_glyphs[i].advance,
data_for_glyphs[i].offset)
}).collect();
let glyphs_vec: Vec<DetailedGlyph> = (0..glyph_count)
.map(|i| {
DetailedGlyph::new(
data_for_glyphs[i].id,
data_for_glyphs[i].advance,
data_for_glyphs[i].offset,
)
}).collect();
self.has_detailed_glyphs = true;
self.detail_store.add_detailed_glyphs_for_entry(i, &glyphs_vec);
self.detail_store
.add_detailed_glyphs_for_entry(i, &glyphs_vec);
let entry = GlyphEntry::complex(first_glyph_data.cluster_start,
first_glyph_data.ligature_start,
glyph_count);
let entry = GlyphEntry::complex(
first_glyph_data.cluster_start,
first_glyph_data.ligature_start,
glyph_count,
);
debug!("Adding multiple glyphs[idx={:?}, count={}]: {:?}", i, glyph_count, entry);
debug!(
"Adding multiple glyphs[idx={:?}, count={}]: {:?}",
i, glyph_count, entry
);
self.entry_buffer[i.to_usize()] = entry;
}
@ -540,9 +571,13 @@ impl<'a> GlyphStore {
}
GlyphIterator {
store: self,
byte_index: if self.is_rtl { range.end() } else { range.begin() - ByteIndex(1) },
byte_range: *range,
store: self,
byte_index: if self.is_rtl {
range.end()
} else {
range.begin() - ByteIndex(1)
},
byte_range: *range,
glyph_range: None,
}
}
@ -551,7 +586,12 @@ impl<'a> GlyphStore {
// and advance of the glyph in the range at the given advance, if reached. Otherwise, returns the
// the number of glyphs and the advance for the given range.
#[inline]
pub fn range_index_of_advance(&self, range: &Range<ByteIndex>, advance: Au, extra_word_spacing: Au) -> (usize, Au) {
pub fn range_index_of_advance(
&self,
range: &Range<ByteIndex>,
advance: Au,
extra_word_spacing: Au,
) -> (usize, Au) {
let mut index = 0;
let mut current_advance = Au(0);
for glyph in self.iter_glyphs_for_byte_range(range) {
@ -580,7 +620,11 @@ impl<'a> GlyphStore {
}
#[inline]
pub fn advance_for_byte_range_slow_path(&self, range: &Range<ByteIndex>, extra_word_spacing: Au) -> Au {
pub fn advance_for_byte_range_slow_path(
&self,
range: &Range<ByteIndex>,
extra_word_spacing: Au,
) -> Au {
self.iter_glyphs_for_byte_range(range)
.fold(Au(0), |advance, glyph| {
if glyph.char_is_space() {
@ -594,7 +638,11 @@ impl<'a> GlyphStore {
#[inline]
#[cfg(feature = "unstable")]
#[cfg(any(target_feature = "sse2", target_feature = "neon"))]
fn advance_for_byte_range_simple_glyphs(&self, range: &Range<ByteIndex>, extra_word_spacing: Au) -> Au {
fn advance_for_byte_range_simple_glyphs(
&self,
range: &Range<ByteIndex>,
extra_word_spacing: Au,
) -> Au {
let advance_mask = u32x4::splat(GLYPH_ADVANCE_MASK);
let space_flag_mask = u32x4::splat(FLAG_CHAR_IS_SPACE);
let mut simd_advance = u32x4::splat(0);
@ -614,16 +662,14 @@ impl<'a> GlyphStore {
simd_spaces = simd_spaces + spaces;
}
let advance =
(simd_advance.extract(0) +
simd_advance.extract(1) +
simd_advance.extract(2) +
simd_advance.extract(3)) as i32;
let spaces =
(simd_spaces.extract(0) +
simd_spaces.extract(1) +
simd_spaces.extract(2) +
simd_spaces.extract(3)) as i32;
let advance = (simd_advance.extract(0) +
simd_advance.extract(1) +
simd_advance.extract(2) +
simd_advance.extract(3)) as i32;
let spaces = (simd_spaces.extract(0) +
simd_spaces.extract(1) +
simd_spaces.extract(2) +
simd_spaces.extract(3)) as i32;
let mut leftover_advance = Au(0);
let mut leftover_spaces = 0;
for i in leftover_entries..range.end().to_usize() {
@ -637,8 +683,15 @@ impl<'a> GlyphStore {
/// When SIMD isn't available, fallback to the slow path.
#[inline]
#[cfg(not(all(feature = "unstable", any(target_feature = "sse2", target_feature = "neon"))))]
fn advance_for_byte_range_simple_glyphs(&self, range: &Range<ByteIndex>, extra_word_spacing: Au) -> Au {
#[cfg(not(all(
feature = "unstable",
any(target_feature = "sse2", target_feature = "neon")
)))]
fn advance_for_byte_range_simple_glyphs(
&self,
range: &Range<ByteIndex>,
extra_word_spacing: Au,
) -> Au {
self.advance_for_byte_range_slow_path(range, extra_word_spacing)
}
@ -676,23 +729,27 @@ impl fmt::Debug for GlyphStore {
let mut detailed_buffer = self.detail_store.detail_buffer.iter();
for entry in self.entry_buffer.iter() {
if entry.is_simple() {
write!(formatter,
" simple id={:?} advance={:?}\n",
entry.id(),
entry.advance())?;
continue
write!(
formatter,
" simple id={:?} advance={:?}\n",
entry.id(),
entry.advance()
)?;
continue;
}
if entry.is_initial() {
continue
continue;
}
write!(formatter, " complex...")?;
if detailed_buffer.next().is_none() {
continue
continue;
}
write!(formatter,
" detailed id={:?} advance={:?}\n",
entry.id(),
entry.advance())?;
write!(
formatter,
" detailed id={:?} advance={:?}\n",
entry.id(),
entry.advance()
)?;
}
Ok(())
}
@ -712,27 +769,37 @@ impl<'a> GlyphIterator<'a> {
fn next_glyph_range(&mut self) -> Option<GlyphInfo<'a>> {
match self.glyph_range.as_mut().unwrap().next() {
Some(j) => {
Some(GlyphInfo::Detail(self.store, self.byte_index, j.get() as u16 /* ??? */))
}
Some(GlyphInfo::Detail(
self.store,
self.byte_index,
j.get() as u16, /* ??? */
))
},
None => {
// No more glyphs for current character. Try to get another.
self.glyph_range = None;
self.next()
}
},
}
}
// Slow path when there is a complex glyph.
#[inline(never)]
fn next_complex_glyph(&mut self, entry: &GlyphEntry, i: ByteIndex) -> Option<GlyphInfo<'a>> {
let glyphs = self.store.detail_store.detailed_glyphs_for_entry(i, entry.glyph_count());
self.glyph_range = Some(range::each_index(ByteIndex(0), ByteIndex(glyphs.len() as isize)));
let glyphs = self
.store
.detail_store
.detailed_glyphs_for_entry(i, entry.glyph_count());
self.glyph_range = Some(range::each_index(
ByteIndex(0),
ByteIndex(glyphs.len() as isize),
));
self.next()
}
}
impl<'a> Iterator for GlyphIterator<'a> {
type Item = GlyphInfo<'a>;
type Item = GlyphInfo<'a>;
// I tried to start with something simpler and apply FlatMap, but the
// inability to store free variables in the FlatMap struct was problematic.
@ -744,7 +811,7 @@ impl<'a> Iterator for GlyphIterator<'a> {
fn next(&mut self) -> Option<GlyphInfo<'a>> {
// Would use 'match' here but it borrows contents in a way that interferes with mutation.
if self.glyph_range.is_some() {
return self.next_glyph_range()
return self.next_glyph_range();
}
// No glyph range. Look at next byte.
@ -755,7 +822,7 @@ impl<'a> Iterator for GlyphIterator<'a> {
};
let i = self.byte_index;
if !self.byte_range.contains(i) {
return None
return None;
}
debug_assert!(i < self.store.len());
let entry = self.store.entry_buffer[i.to_usize()];

View file

@ -9,4 +9,3 @@ pub mod glyph;
pub mod shaping;
pub mod text_run;
pub mod util;

View file

@ -147,10 +147,11 @@ impl Drop for Shaper {
impl Shaper {
pub fn new(font: *const Font) -> Shaper {
unsafe {
let hb_face: *mut hb_face_t =
hb_face_create_for_tables(Some(font_table_func),
font as *const c_void as *mut c_void,
None);
let hb_face: *mut hb_face_t = hb_face_create_for_tables(
Some(font_table_func),
font as *const c_void as *mut c_void,
None,
);
let hb_font: *mut hb_font_t = hb_font_create(hb_face);
// Set points-per-em. if zero, performs no hinting in that direction.
@ -158,12 +159,19 @@ impl Shaper {
hb_font_set_ppem(hb_font, pt_size as c_uint, pt_size as c_uint);
// Set scaling. Note that this takes 16.16 fixed point.
hb_font_set_scale(hb_font,
Shaper::float_to_fixed(pt_size) as c_int,
Shaper::float_to_fixed(pt_size) as c_int);
hb_font_set_scale(
hb_font,
Shaper::float_to_fixed(pt_size) as c_int,
Shaper::float_to_fixed(pt_size) as c_int,
);
// configure static function callbacks.
hb_font_set_funcs(hb_font, HB_FONT_FUNCS.0, font as *mut Font as *mut c_void, None);
hb_font_set_funcs(
hb_font,
HB_FONT_FUNCS.0,
font as *mut Font as *mut c_void,
None,
);
Shaper {
hb_face: hb_face,
@ -188,22 +196,30 @@ impl ShaperMethods for Shaper {
fn shape_text(&self, text: &str, options: &ShapingOptions, glyphs: &mut GlyphStore) {
unsafe {
let hb_buffer: *mut hb_buffer_t = hb_buffer_create();
hb_buffer_set_direction(hb_buffer, if options.flags.contains(ShapingFlags::RTL_FLAG) {
HB_DIRECTION_RTL
} else {
HB_DIRECTION_LTR
});
hb_buffer_set_direction(
hb_buffer,
if options.flags.contains(ShapingFlags::RTL_FLAG) {
HB_DIRECTION_RTL
} else {
HB_DIRECTION_LTR
},
);
hb_buffer_set_script(hb_buffer, options.script.to_hb_script());
hb_buffer_add_utf8(hb_buffer,
text.as_ptr() as *const c_char,
text.len() as c_int,
0,
text.len() as c_int);
hb_buffer_add_utf8(
hb_buffer,
text.as_ptr() as *const c_char,
text.len() as c_int,
0,
text.len() as c_int,
);
let mut features = Vec::new();
if options.flags.contains(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG) {
if options
.flags
.contains(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG)
{
features.push(hb_feature_t {
tag: LIGA,
value: 0,
@ -211,7 +227,10 @@ impl ShaperMethods for Shaper {
end: hb_buffer_get_length(hb_buffer),
})
}
if options.flags.contains(ShapingFlags::DISABLE_KERNING_SHAPING_FLAG) {
if options
.flags
.contains(ShapingFlags::DISABLE_KERNING_SHAPING_FLAG)
{
features.push(hb_feature_t {
tag: KERN,
value: 0,
@ -220,7 +239,12 @@ impl ShaperMethods for Shaper {
})
}
hb_shape(self.hb_font, hb_buffer, features.as_mut_ptr(), features.len() as u32);
hb_shape(
self.hb_font,
hb_buffer,
features.as_mut_ptr(),
features.len() as u32,
);
self.save_glyph_results(text, options, glyphs, hb_buffer);
hb_buffer_destroy(hb_buffer);
}
@ -228,18 +252,21 @@ impl ShaperMethods for Shaper {
}
impl Shaper {
fn save_glyph_results(&self,
text: &str,
options: &ShapingOptions,
glyphs: &mut GlyphStore,
buffer: *mut hb_buffer_t) {
fn save_glyph_results(
&self,
text: &str,
options: &ShapingOptions,
glyphs: &mut GlyphStore,
buffer: *mut hb_buffer_t,
) {
let glyph_data = ShapedGlyphData::new(buffer);
let glyph_count = glyph_data.len();
let byte_max = text.len();
debug!("Shaped text[byte count={}], got back {} glyph info records.",
byte_max,
glyph_count);
debug!(
"Shaped text[byte count={}], got back {} glyph info records.",
byte_max, glyph_count
);
// make map of what chars have glyphs
let mut byte_to_glyph = vec![NO_GLYPH; byte_max];
@ -250,9 +277,10 @@ impl Shaper {
if loc < byte_max {
byte_to_glyph[loc] = i as i32;
} else {
debug!("ERROR: tried to set out of range byte_to_glyph: idx={}, glyph idx={}",
loc,
i);
debug!(
"ERROR: tried to set out of range byte_to_glyph: idx={}, glyph idx={}",
loc, i
);
}
debug!("{} -> {}", i, loc);
}
@ -296,10 +324,14 @@ impl Shaper {
}
// if there's just one glyph, then we don't need further checks.
if glyph_span.len() == 1 { break; }
if glyph_span.len() == 1 {
break;
}
// if no glyphs were found yet, extend the char byte range more.
if glyph_span.len() == 0 { continue; }
if glyph_span.len() == 0 {
continue;
}
// If byte_range now includes all the byte offsets found in glyph_span, then we
// have found a contiguous "cluster" and can stop extending it.
@ -308,11 +340,11 @@ impl Shaper {
let loc = glyph_data.byte_offset_of_glyph(j) as usize;
if !(byte_range.start <= loc && loc < byte_range.end) {
all_glyphs_are_within_cluster = false;
break
break;
}
}
if all_glyphs_are_within_cluster {
break
break;
}
// Otherwise, the bytes we have seen so far correspond to a non-contiguous set of
@ -348,34 +380,29 @@ impl Shaper {
const TAB_COLS: i32 = 8;
let (space_glyph_id, space_advance) = glyph_space_advance(self.font);
let advance = Au::from_f64_px(space_advance) * TAB_COLS;
let data = GlyphData::new(space_glyph_id,
advance,
Default::default(),
true,
true);
let data =
GlyphData::new(space_glyph_id, advance, Default::default(), true, true);
glyphs.add_glyph_for_byte_index(byte_idx, character, &data);
} else {
let shape = glyph_data.entry_for_glyph(glyph_span.start, &mut y_pos);
let advance = self.advance_for_shaped_glyph(shape.advance, character, options);
let data = GlyphData::new(shape.codepoint,
advance,
shape.offset,
true,
true);
let data = GlyphData::new(shape.codepoint, advance, shape.offset, true, true);
glyphs.add_glyph_for_byte_index(byte_idx, character, &data);
}
} else {
// collect all glyphs to be assigned to the first character.
let mut datas = vec!();
let mut datas = vec![];
for glyph_i in glyph_span.clone() {
let shape = glyph_data.entry_for_glyph(glyph_i, &mut y_pos);
datas.push(GlyphData::new(shape.codepoint,
shape.advance,
shape.offset,
true, // treat as cluster start
glyph_i > glyph_span.start));
// all but first are ligature continuations
datas.push(GlyphData::new(
shape.codepoint,
shape.advance,
shape.offset,
true, // treat as cluster start
glyph_i > glyph_span.start,
));
// all but first are ligature continuations
}
// now add the detailed glyph entry.
glyphs.add_glyphs_for_byte_index(byte_idx, &datas);
@ -390,8 +417,12 @@ impl Shaper {
glyphs.finalize_changes();
}
fn advance_for_shaped_glyph(&self, mut advance: Au, character: char, options: &ShapingOptions)
-> Au {
fn advance_for_shaped_glyph(
&self,
mut advance: Au,
character: char,
options: &ShapingOptions,
) -> Au {
if let Some(letter_spacing) = options.letter_spacing {
advance = advance + letter_spacing;
};
@ -403,7 +434,8 @@ impl Shaper {
if character == ' ' || character == '\u{a0}' {
// https://drafts.csswg.org/css-text-3/#word-spacing-property
let (length, percent) = options.word_spacing;
advance = (advance + length) + Au::new((advance.0 as f32 * percent.into_inner()) as i32);
advance =
(advance + length) + Au::new((advance.0 as f32 * percent.into_inner()) as i32);
}
advance
@ -420,20 +452,29 @@ lazy_static! {
let hb_funcs = hb_font_funcs_create();
hb_font_funcs_set_nominal_glyph_func(hb_funcs, Some(glyph_func), ptr::null_mut(), None);
hb_font_funcs_set_glyph_h_advance_func(
hb_funcs, Some(glyph_h_advance_func), ptr::null_mut(), None);
hb_funcs,
Some(glyph_h_advance_func),
ptr::null_mut(),
None,
);
hb_font_funcs_set_glyph_h_kerning_func(
hb_funcs, Some(glyph_h_kerning_func), ptr::null_mut(), None);
hb_funcs,
Some(glyph_h_kerning_func),
ptr::null_mut(),
None,
);
FontFuncs(hb_funcs)
};
}
extern fn glyph_func(_: *mut hb_font_t,
font_data: *mut c_void,
unicode: hb_codepoint_t,
glyph: *mut hb_codepoint_t,
_: *mut c_void)
-> hb_bool_t {
extern "C" fn glyph_func(
_: *mut hb_font_t,
font_data: *mut c_void,
unicode: hb_codepoint_t,
glyph: *mut hb_codepoint_t,
_: *mut c_void,
) -> hb_bool_t {
let font: *const Font = font_data as *const Font;
assert!(!font.is_null());
@ -442,17 +483,18 @@ extern fn glyph_func(_: *mut hb_font_t,
Some(g) => {
*glyph = g as hb_codepoint_t;
true as hb_bool_t
}
None => false as hb_bool_t
},
None => false as hb_bool_t,
}
}
}
extern fn glyph_h_advance_func(_: *mut hb_font_t,
font_data: *mut c_void,
glyph: hb_codepoint_t,
_: *mut c_void)
-> hb_position_t {
extern "C" fn glyph_h_advance_func(
_: *mut hb_font_t,
font_data: *mut c_void,
glyph: hb_codepoint_t,
_: *mut c_void,
) -> hb_position_t {
let font: *mut Font = font_data as *mut Font;
assert!(!font.is_null());
@ -468,19 +510,20 @@ fn glyph_space_advance(font: *const Font) -> (hb_codepoint_t, f64) {
match unsafe { (*font).glyph_index(space_unicode) } {
Some(g) => {
space_glyph = g as hb_codepoint_t;
}
None => panic!("No space info")
},
None => panic!("No space info"),
}
let space_advance = unsafe { (*font).glyph_h_advance(space_glyph as GlyphId) };
(space_glyph, space_advance)
}
extern fn glyph_h_kerning_func(_: *mut hb_font_t,
font_data: *mut c_void,
first_glyph: hb_codepoint_t,
second_glyph: hb_codepoint_t,
_: *mut c_void)
-> hb_position_t {
extern "C" fn glyph_h_kerning_func(
_: *mut hb_font_t,
font_data: *mut c_void,
first_glyph: hb_codepoint_t,
second_glyph: hb_codepoint_t,
_: *mut c_void,
) -> hb_position_t {
let font: *mut Font = font_data as *mut Font;
assert!(!font.is_null());
@ -491,10 +534,11 @@ extern fn glyph_h_kerning_func(_: *mut hb_font_t,
}
// Callback to get a font table out of a font.
extern fn font_table_func(_: *mut hb_face_t,
tag: hb_tag_t,
user_data: *mut c_void)
-> *mut hb_blob_t {
extern "C" fn font_table_func(
_: *mut hb_face_t,
tag: hb_tag_t,
user_data: *mut c_void,
) -> *mut hb_blob_t {
unsafe {
// NB: These asserts have security implications.
let font = user_data as *const Font;
@ -511,20 +555,22 @@ extern fn font_table_func(_: *mut hb_face_t,
let buf = (*font_table_ptr).buffer();
// HarfBuzz calls `destroy_blob_func` when the buffer is no longer needed.
let blob = hb_blob_create(buf.as_ptr() as *const c_char,
buf.len() as c_uint,
HB_MEMORY_MODE_READONLY,
font_table_ptr as *mut c_void,
Some(destroy_blob_func));
let blob = hb_blob_create(
buf.as_ptr() as *const c_char,
buf.len() as c_uint,
HB_MEMORY_MODE_READONLY,
font_table_ptr as *mut c_void,
Some(destroy_blob_func),
);
assert!(!blob.is_null());
blob
}
},
}
}
}
extern fn destroy_blob_func(font_table_ptr: *mut c_void) {
extern "C" fn destroy_blob_func(font_table_ptr: *mut c_void) {
unsafe {
drop(Box::from_raw(font_table_ptr as *mut FontTable));
}

View file

@ -17,4 +17,3 @@ pub mod harfbuzz;
pub trait ShaperMethods {
fn shape_text(&self, text: &str, options: &ShapingOptions, glyphs: &mut GlyphStore);
}

View file

@ -155,7 +155,7 @@ impl<'a> Iterator for CharacterSliceIterator<'a> {
let byte_start = self.range.begin();
let byte_len = match self.text[byte_start.to_usize()..].chars().next() {
Some(ch) => ByteIndex(ch.len_utf8() as isize),
None => unreachable!() // XXX refactor?
None => unreachable!(), // XXX refactor?
};
self.range.adjust_by(byte_len, -byte_len);
@ -178,24 +178,36 @@ impl<'a> Iterator for CharacterSliceIterator<'a> {
impl<'a> TextRun {
/// Constructs a new text run. Also returns if there is a line break at the beginning
pub fn new(font: &mut Font, text: String, options: &ShapingOptions,
bidi_level: bidi::Level, breaker: &mut Option<LineBreakLeafIter>) -> (TextRun, bool) {
pub fn new(
font: &mut Font,
text: String,
options: &ShapingOptions,
bidi_level: bidi::Level,
breaker: &mut Option<LineBreakLeafIter>,
) -> (TextRun, bool) {
let (glyphs, break_at_zero) = TextRun::break_and_shape(font, &text, options, breaker);
(TextRun {
text: Arc::new(text),
font_metrics: font.metrics.clone(),
font_template: font.handle.template(),
font_key: font.font_key,
actual_pt_size: font.actual_pt_size,
glyphs: Arc::new(glyphs),
bidi_level: bidi_level,
extra_word_spacing: Au(0),
}, break_at_zero)
(
TextRun {
text: Arc::new(text),
font_metrics: font.metrics.clone(),
font_template: font.handle.template(),
font_key: font.font_key,
actual_pt_size: font.actual_pt_size,
glyphs: Arc::new(glyphs),
bidi_level: bidi_level,
extra_word_spacing: Au(0),
},
break_at_zero,
)
}
pub fn break_and_shape(font: &mut Font, text: &str, options: &ShapingOptions,
breaker: &mut Option<LineBreakLeafIter>) -> (Vec<GlyphRun>, bool) {
let mut glyphs = vec!();
pub fn break_and_shape(
font: &mut Font,
text: &str,
options: &ShapingOptions,
breaker: &mut Option<LineBreakLeafIter>,
) -> (Vec<GlyphRun>, bool) {
let mut glyphs = vec![];
let mut slice = 0..0;
let mut finished = false;
@ -203,7 +215,7 @@ impl<'a> TextRun {
if breaker.is_none() {
if text.len() == 0 {
return (glyphs, true)
return (glyphs, true);
}
*breaker = Some(LineBreakLeafIter::new(&text, 0));
}
@ -225,29 +237,39 @@ impl<'a> TextRun {
// Split off any trailing whitespace into a separate glyph run.
let mut whitespace = slice.end..slice.end;
if let Some((i, _)) = word.char_indices().rev()
.take_while(|&(_, c)| char_is_whitespace(c)).last() {
whitespace.start = slice.start + i;
slice.end = whitespace.start;
} else if idx != text.len() && options.flags.contains(ShapingFlags::KEEP_ALL_FLAG) {
// If there's no whitespace and word-break is set to
// keep-all, try increasing the slice.
continue;
}
if let Some((i, _)) = word
.char_indices()
.rev()
.take_while(|&(_, c)| char_is_whitespace(c))
.last()
{
whitespace.start = slice.start + i;
slice.end = whitespace.start;
} else if idx != text.len() && options.flags.contains(ShapingFlags::KEEP_ALL_FLAG) {
// If there's no whitespace and word-break is set to
// keep-all, try increasing the slice.
continue;
}
if slice.len() > 0 {
glyphs.push(GlyphRun {
glyph_store: font.shape_text(&text[slice.clone()], options),
range: Range::new(ByteIndex(slice.start as isize),
ByteIndex(slice.len() as isize)),
range: Range::new(
ByteIndex(slice.start as isize),
ByteIndex(slice.len() as isize),
),
});
}
if whitespace.len() > 0 {
let mut options = options.clone();
options.flags.insert(ShapingFlags::IS_WHITESPACE_SHAPING_FLAG);
options
.flags
.insert(ShapingFlags::IS_WHITESPACE_SHAPING_FLAG);
glyphs.push(GlyphRun {
glyph_store: font.shape_text(&text[whitespace.clone()], &options),
range: Range::new(ByteIndex(whitespace.start as isize),
ByteIndex(whitespace.len() as isize)),
range: Range::new(
ByteIndex(whitespace.start as isize),
ByteIndex(whitespace.len() as isize),
),
});
}
slice.start = whitespace.end;
@ -265,36 +287,46 @@ impl<'a> TextRun {
pub fn advance_for_range(&self, range: &Range<ByteIndex>) -> Au {
if range.is_empty() {
return Au(0)
return Au(0);
}
// TODO(Issue #199): alter advance direction for RTL
// TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text
self.natural_word_slices_in_range(range)
.fold(Au(0), |advance, slice| {
advance + slice.glyphs.advance_for_byte_range(&slice.range, self.extra_word_spacing)
advance + slice
.glyphs
.advance_for_byte_range(&slice.range, self.extra_word_spacing)
})
}
pub fn metrics_for_range(&self, range: &Range<ByteIndex>) -> RunMetrics {
RunMetrics::new(self.advance_for_range(range),
self.font_metrics.ascent,
self.font_metrics.descent)
RunMetrics::new(
self.advance_for_range(range),
self.font_metrics.ascent,
self.font_metrics.descent,
)
}
pub fn metrics_for_slice(&self, glyphs: &GlyphStore, slice_range: &Range<ByteIndex>)
-> RunMetrics {
RunMetrics::new(glyphs.advance_for_byte_range(slice_range, self.extra_word_spacing),
self.font_metrics.ascent,
self.font_metrics.descent)
pub fn metrics_for_slice(
&self,
glyphs: &GlyphStore,
slice_range: &Range<ByteIndex>,
) -> RunMetrics {
RunMetrics::new(
glyphs.advance_for_byte_range(slice_range, self.extra_word_spacing),
self.font_metrics.ascent,
self.font_metrics.descent,
)
}
pub fn min_width_for_range(&self, range: &Range<ByteIndex>) -> Au {
debug!("iterating outer range {:?}", range);
self.natural_word_slices_in_range(range).fold(Au(0), |max_piece_width, slice| {
debug!("iterated on {:?}[{:?}]", slice.offset, slice.range);
max(max_piece_width, self.advance_for_range(&slice.range))
})
self.natural_word_slices_in_range(range)
.fold(Au(0), |max_piece_width, slice| {
debug!("iterated on {:?}[{:?}]", slice.offset, slice.range);
max(max_piece_width, self.advance_for_range(&slice.range))
})
}
pub fn minimum_splittable_inline_size(&self, range: &Range<ByteIndex>) -> Au {
@ -309,13 +341,15 @@ impl<'a> TextRun {
let self_ptr = self as *const TextRun;
INDEX_OF_FIRST_GLYPH_RUN_CACHE.with(|index_of_first_glyph_run_cache| {
if let Some((last_text_run, last_index, last_result)) =
index_of_first_glyph_run_cache.get() {
index_of_first_glyph_run_cache.get()
{
if last_text_run == self_ptr && last_index == index {
return Some(last_result)
return Some(last_result);
}
}
if let Ok(result) = (&**self.glyphs).binary_search_by(|current| current.compare(&index)) {
if let Ok(result) = (&**self.glyphs).binary_search_by(|current| current.compare(&index))
{
index_of_first_glyph_run_cache.set(Some((self_ptr, index, result)));
Some(result)
} else {
@ -339,18 +373,22 @@ impl<'a> TextRun {
let mut remaining = advance;
self.natural_word_slices_in_range(range)
.map(|slice| {
let (slice_index, slice_advance) =
slice.glyphs.range_index_of_advance(&slice.range, remaining, self.extra_word_spacing);
let (slice_index, slice_advance) = slice.glyphs.range_index_of_advance(
&slice.range,
remaining,
self.extra_word_spacing,
);
remaining -= slice_advance;
slice_index
})
.sum()
}).sum()
}
/// Returns an iterator that will iterate over all slices of glyphs that represent natural
/// words in the given range.
pub fn natural_word_slices_in_range(&'a self, range: &Range<ByteIndex>)
-> NaturalWordSliceIterator<'a> {
pub fn natural_word_slices_in_range(
&'a self,
range: &Range<ByteIndex>,
) -> NaturalWordSliceIterator<'a> {
let index = match self.index_of_first_glyph_run_containing(range.begin()) {
None => self.glyphs.len(),
Some(index) => index,
@ -365,20 +403,22 @@ impl<'a> TextRun {
/// Returns an iterator that over natural word slices in visual order (left to right or
/// right to left, depending on the bidirectional embedding level).
pub fn natural_word_slices_in_visual_order(&'a self, range: &Range<ByteIndex>)
-> NaturalWordSliceIterator<'a> {
pub fn natural_word_slices_in_visual_order(
&'a self,
range: &Range<ByteIndex>,
) -> NaturalWordSliceIterator<'a> {
// Iterate in reverse order if bidi level is RTL.
let reverse = self.bidi_level.is_rtl();
let index = if reverse {
match self.index_of_first_glyph_run_containing(range.end() - ByteIndex(1)) {
Some(i) => i + 1, // In reverse mode, index points one past the next element.
None => 0
None => 0,
}
} else {
match self.index_of_first_glyph_run_containing(range.begin()) {
Some(i) => i,
None => self.glyphs.len()
None => self.glyphs.len(),
}
};
NaturalWordSliceIterator {
@ -391,8 +431,10 @@ impl<'a> TextRun {
/// Returns an iterator that will iterate over all slices of glyphs that represent individual
/// characters in the given range.
pub fn character_slices_in_range(&'a self, range: &Range<ByteIndex>)
-> CharacterSliceIterator<'a> {
pub fn character_slices_in_range(
&'a self,
range: &Range<ByteIndex>,
) -> CharacterSliceIterator<'a> {
let index = match self.index_of_first_glyph_run_containing(range.begin()) {
None => self.glyphs.len(),
Some(index) => index,

View file

@ -9,7 +9,7 @@ pub enum CompressionMode {
CompressNone,
CompressWhitespace,
CompressWhitespaceNewline,
DiscardNewline
DiscardNewline,
}
// ported from Gecko's nsTextFrameUtils::TransformText.
@ -22,11 +22,12 @@ pub enum CompressionMode {
// * Issue #114: record skipped and kept chars for mapping original to new text
//
// * Untracked: various edge cases for bidi, CJK, etc.
pub fn transform_text(text: &str,
mode: CompressionMode,
incoming_whitespace: bool,
output_text: &mut String)
-> bool {
pub fn transform_text(
text: &str,
mode: CompressionMode,
incoming_whitespace: bool,
output_text: &mut String,
) -> bool {
let out_whitespace = match mode {
CompressionMode::CompressNone | CompressionMode::DiscardNewline => {
for ch in text.chars() {
@ -53,12 +54,13 @@ pub fn transform_text(text: &str,
if is_always_discardable_char(ch) {
// revert whitespace setting, since this char was discarded
next_in_whitespace = in_whitespace;
// TODO: record skipped char
// TODO: record skipped char
} else {
// TODO: record kept char
output_text.push(ch);
}
} else { /* next_in_whitespace; possibly add a space char */
} else {
/* next_in_whitespace; possibly add a space char */
if in_whitespace {
// TODO: record skipped char
} else {
@ -70,17 +72,17 @@ pub fn transform_text(text: &str,
in_whitespace = next_in_whitespace;
} /* /for str::each_char */
in_whitespace
}
},
};
return out_whitespace;
fn is_in_whitespace(ch: char, mode: CompressionMode) -> bool {
match (ch, mode) {
(' ', _) => true,
(' ', _) => true,
('\t', _) => true,
('\n', CompressionMode::CompressWhitespaceNewline) => true,
(_, _) => false
(_, _) => false,
}
}
@ -89,8 +91,10 @@ pub fn transform_text(text: &str,
return true;
}
match mode {
CompressionMode::DiscardNewline | CompressionMode::CompressWhitespaceNewline => ch == '\n',
_ => false
CompressionMode::DiscardNewline | CompressionMode::CompressWhitespaceNewline => {
ch == '\n'
},
_ => false,
}
}
@ -113,7 +117,7 @@ pub fn is_bidi_control(c: char) -> bool {
'\u{202A}'...'\u{202E}' => true,
'\u{2066}'...'\u{2069}' => true,
'\u{200E}' | '\u{200F}' | '\u{061C}' => true,
_ => false
_ => false,
}
}
@ -143,15 +147,12 @@ pub fn is_cjk(codepoint: char) -> bool {
UnicodeBlock::CJKUnifiedIdeographs |
UnicodeBlock::CJKCompatibilityIdeographs |
UnicodeBlock::CJKCompatibilityForms |
UnicodeBlock::HalfwidthandFullwidthForms => {
return true
}
UnicodeBlock::HalfwidthandFullwidthForms => return true,
_ => {}
_ => {},
}
}
// https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Ideographic_Plane
unicode_plane(codepoint) == 2
}

View file

@ -4,13 +4,15 @@
#![crate_name = "gfx_traits"]
#![crate_type = "rlib"]
#![deny(unsafe_code)]
extern crate malloc_size_of;
#[macro_use] extern crate malloc_size_of_derive;
#[macro_use] extern crate range;
#[macro_use] extern crate serde;
#[macro_use]
extern crate malloc_size_of_derive;
#[macro_use]
extern crate range;
#[macro_use]
extern crate serde;
pub mod print_tree;
@ -32,7 +34,7 @@ impl Epoch {
pub struct StackingContextId(
/// The identifier for this StackingContext, derived from the Flow's memory address
/// and fragment type. As a space optimization, these are combined into a single word.
pub u64
pub u64,
);
impl StackingContextId {
@ -87,7 +89,7 @@ fn next_special_id() -> usize {
SPECIAL_SCROLL_ROOT_ID_MASK
}
pub fn combine_id_with_fragment_type(id: usize, fragment_type: FragmentType) -> usize {
pub fn combine_id_with_fragment_type(id: usize, fragment_type: FragmentType) -> usize {
debug_assert_eq!(id & (fragment_type as usize), 0);
if fragment_type == FragmentType::FragmentBody {
id

View file

@ -1,25 +1,26 @@
// FORK NOTE: Copied from liballoc_system, removed unnecessary APIs,
// APIs take size/align directly instead of Layout
// The minimum alignment guaranteed by the architecture. This value is used to
// add fast paths for low alignment values. In practice, the alignment is a
// constant at the call site and the branch will be optimized out.
#[cfg(all(any(target_arch = "x86",
target_arch = "arm",
target_arch = "mips",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "asmjs",
target_arch = "wasm32")))]
#[cfg(all(any(
target_arch = "x86",
target_arch = "arm",
target_arch = "mips",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "asmjs",
target_arch = "wasm32"
)))]
const MIN_ALIGN: usize = 8;
#[cfg(all(any(target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "mips64",
target_arch = "s390x",
target_arch = "sparc64")))]
#[cfg(all(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "mips64",
target_arch = "s390x",
target_arch = "sparc64"
)))]
const MIN_ALIGN: usize = 16;
pub use self::platform::{alloc, dealloc, realloc};
@ -100,7 +101,6 @@ mod platform {
type DWORD = u32;
type BOOL = i32;
extern "system" {
fn GetProcessHeap() -> HANDLE;
fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID;
@ -123,8 +123,7 @@ mod platform {
}
#[inline]
unsafe fn allocate_with_flags(size: usize, align: usize, flags: DWORD) -> *mut u8
{
unsafe fn allocate_with_flags(size: usize, align: usize, flags: DWORD) -> *mut u8 {
if align <= MIN_ALIGN {
HeapAlloc(GetProcessHeap(), flags, size)
} else {
@ -147,21 +146,16 @@ mod platform {
pub unsafe fn dealloc(ptr: *mut u8, align: usize) {
if align <= MIN_ALIGN {
let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID);
debug_assert!(err != 0, "Failed to free heap memory: {}",
GetLastError());
debug_assert!(err != 0, "Failed to free heap memory: {}", GetLastError());
} else {
let header = get_header(ptr);
let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID);
debug_assert!(err != 0, "Failed to free heap memory: {}",
GetLastError());
debug_assert!(err != 0, "Failed to free heap memory: {}", GetLastError());
}
}
#[inline]
pub unsafe fn realloc(ptr: *mut u8, new_size: usize) -> *mut u8 {
HeapReAlloc(GetProcessHeap(),
0,
ptr as LPVOID,
new_size) as *mut u8
HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut u8
}
}

View file

@ -26,7 +26,6 @@ pub use std::collections::hash_set::{Iter as SetIter, IntoIter as SetIntoIter};
#[derive(Clone)]
pub struct HashMap<K, V, S = RandomState>(StdMap<K, V, S>);
use FailedAllocationError;
impl<K, V, S> Deref for HashMap<K, V, S> {
@ -43,8 +42,9 @@ impl<K, V, S> DerefMut for HashMap<K, V, S> {
}
impl<K, V, S> HashMap<K, V, S>
where K: Eq + Hash,
S: BuildHasher
where
K: Eq + Hash,
S: BuildHasher,
{
#[inline]
pub fn try_with_hasher(hash_builder: S) -> Result<HashMap<K, V, S>, FailedAllocationError> {
@ -52,17 +52,20 @@ impl<K, V, S> HashMap<K, V, S>
}
#[inline]
pub fn try_with_capacity_and_hasher(capacity: usize,
hash_builder: S)
-> Result<HashMap<K, V, S>, FailedAllocationError> {
Ok(HashMap(StdMap::with_capacity_and_hasher(capacity, hash_builder)))
pub fn try_with_capacity_and_hasher(
capacity: usize,
hash_builder: S,
) -> Result<HashMap<K, V, S>, FailedAllocationError> {
Ok(HashMap(StdMap::with_capacity_and_hasher(
capacity,
hash_builder,
)))
}
pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> HashMap<K, V, S> {
HashMap(StdMap::with_capacity_and_hasher(capacity, hash_builder))
}
#[inline]
pub fn try_reserve(&mut self, additional: usize) -> Result<(), FailedAllocationError> {
Ok(self.reserve(additional))
@ -85,7 +88,6 @@ impl<K, V, S> HashMap<K, V, S>
#[derive(Clone)]
pub struct HashSet<T, S = RandomState>(StdSet<T, S>);
impl<T, S> Deref for HashSet<T, S> {
type Target = StdSet<T, S>;
fn deref(&self) -> &Self::Target {
@ -111,17 +113,16 @@ impl<T: Hash + Eq> HashSet<T, RandomState> {
}
}
impl<T, S> HashSet<T, S>
where T: Eq + Hash,
S: BuildHasher
where
T: Eq + Hash,
S: BuildHasher,
{
#[inline]
pub fn with_hasher(hasher: S) -> HashSet<T, S> {
HashSet(StdSet::with_hasher(hasher))
}
#[inline]
pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet<T, S> {
HashSet(StdSet::with_capacity_and_hasher(capacity, hasher))
@ -153,18 +154,21 @@ impl<K: Hash + Eq, V, S: BuildHasher + Default> Default for HashMap<K, V, S> {
}
impl<K, V, S> fmt::Debug for HashMap<K, V, S>
where K: Eq + Hash + fmt::Debug,
V: fmt::Debug,
S: BuildHasher {
where
K: Eq + Hash + fmt::Debug,
V: fmt::Debug,
S: BuildHasher,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl<K, V, S> PartialEq for HashMap<K, V, S>
where K: Eq + Hash,
V: PartialEq,
S: BuildHasher
where
K: Eq + Hash,
V: PartialEq,
S: BuildHasher,
{
fn eq(&self, other: &HashMap<K, V, S>) -> bool {
self.0.eq(&other.0)
@ -172,15 +176,17 @@ impl<K, V, S> PartialEq for HashMap<K, V, S>
}
impl<K, V, S> Eq for HashMap<K, V, S>
where K: Eq + Hash,
V: Eq,
S: BuildHasher
where
K: Eq + Hash,
V: Eq,
S: BuildHasher,
{
}
impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S>
where K: Eq + Hash,
S: BuildHasher
where
K: Eq + Hash,
S: BuildHasher,
{
type Item = (&'a K, &'a V);
type IntoIter = MapIter<'a, K, V>;
@ -191,8 +197,9 @@ impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S>
}
impl<'a, K, V, S> IntoIterator for &'a mut HashMap<K, V, S>
where K: Eq + Hash,
S: BuildHasher
where
K: Eq + Hash,
S: BuildHasher,
{
type Item = (&'a K, &'a mut V);
type IntoIter = MapIterMut<'a, K, V>;
@ -209,8 +216,9 @@ impl<T: Eq + Hash, S: BuildHasher + Default> Default for HashSet<T, S> {
}
impl<T, S> fmt::Debug for HashSet<T, S>
where T: Eq + Hash + fmt::Debug,
S: BuildHasher
where
T: Eq + Hash + fmt::Debug,
S: BuildHasher,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
@ -218,8 +226,9 @@ impl<T, S> fmt::Debug for HashSet<T, S>
}
impl<T, S> PartialEq for HashSet<T, S>
where T: Eq + Hash,
S: BuildHasher
where
T: Eq + Hash,
S: BuildHasher,
{
fn eq(&self, other: &HashSet<T, S>) -> bool {
self.0.eq(&other.0)
@ -227,14 +236,16 @@ impl<T, S> PartialEq for HashSet<T, S>
}
impl<T, S> Eq for HashSet<T, S>
where T: Eq + Hash,
S: BuildHasher
where
T: Eq + Hash,
S: BuildHasher,
{
}
impl<'a, T, S> IntoIterator for &'a HashSet<T, S>
where T: Eq + Hash,
S: BuildHasher
where
T: Eq + Hash,
S: BuildHasher,
{
type Item = &'a T;
type IntoIter = SetIter<'a, T>;
@ -245,16 +256,14 @@ impl<'a, T, S> IntoIterator for &'a HashSet<T, S>
}
impl<T, S> IntoIterator for HashSet<T, S>
where T: Eq + Hash,
S: BuildHasher
where
T: Eq + Hash,
S: BuildHasher,
{
type Item = T;
type IntoIter = SetIntoIter<T>;
fn into_iter(self) -> SetIntoIter<T> {
self.0.into_iter()
}
}

View file

@ -25,7 +25,7 @@ use super::table::BucketState::{Empty, Full};
use FailedAllocationError;
const MIN_NONZERO_RAW_CAPACITY: usize = 32; // must be a power of two
const MIN_NONZERO_RAW_CAPACITY: usize = 32; // must be a power of two
/// The default behavior of HashMap implements a maximum load factor of 90.9%.
#[derive(Clone)]
@ -50,7 +50,9 @@ impl DefaultResizePolicy {
// 3. Ensure it is at least the minimum size.
let mut raw_cap = len * 11 / 10;
assert!(raw_cap >= len, "raw_cap overflow");
raw_cap = raw_cap.checked_next_power_of_two().expect("raw_capacity overflow");
raw_cap = raw_cap
.checked_next_power_of_two()
.expect("raw_capacity overflow");
raw_cap = max(MIN_NONZERO_RAW_CAPACITY, raw_cap);
raw_cap
}
@ -398,8 +400,9 @@ pub struct HashMap<K, V, S = RandomState> {
/// Search for a pre-hashed key.
#[inline]
fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) -> InternalEntry<K, V, M>
where M: Deref<Target = RawTable<K, V>>,
F: FnMut(&K) -> bool
where
M: Deref<Target = RawTable<K, V>>,
F: FnMut(&K) -> bool,
{
// This is the only function where capacity can be zero. To avoid
// undefined behavior when Bucket::new gets the raw bucket in this
@ -420,7 +423,7 @@ fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) -> Inter
hash,
elem: NoElem(bucket, displacement),
};
}
},
Full(bucket) => bucket,
};
@ -449,9 +452,7 @@ fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) -> Inter
}
}
fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>)
-> (K, V, &mut RawTable<K, V>)
{
fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>) -> (K, V, &mut RawTable<K, V>) {
let (empty, retkey, retval) = starting_bucket.take();
let mut gap = match empty.gap_peek() {
Ok(b) => b,
@ -475,12 +476,13 @@ fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>)
/// also pass that bucket's displacement so we don't have to recalculate it.
///
/// `hash`, `key`, and `val` are the elements to "robin hood" into the hashtable.
fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>,
mut displacement: usize,
mut hash: SafeHash,
mut key: K,
mut val: V)
-> FullBucketMut<'a, K, V> {
fn robin_hood<'a, K: 'a, V: 'a>(
bucket: FullBucketMut<'a, K, V>,
mut displacement: usize,
mut hash: SafeHash,
mut key: K,
mut val: V,
) -> FullBucketMut<'a, K, V> {
let size = bucket.table().size();
let raw_capacity = bucket.table().capacity();
// There can be at most `size - dib` buckets to displace, because
@ -513,7 +515,7 @@ fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>,
// FullBucketMut, into just one FullBucketMut. The "table"
// refers to the inner FullBucketMut in this context.
return bucket.into_table();
}
},
Full(bucket) => bucket,
};
@ -531,11 +533,13 @@ fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>,
}
impl<K, V, S> HashMap<K, V, S>
where K: Eq + Hash,
S: BuildHasher
where
K: Eq + Hash,
S: BuildHasher,
{
fn make_hash<X: ?Sized>(&self, x: &X) -> SafeHash
where X: Hash
where
X: Hash,
{
table::make_hash(&self.hash_builder, x)
}
@ -545,8 +549,9 @@ impl<K, V, S> HashMap<K, V, S>
/// search_hashed.
#[inline]
fn search<'a, Q: ?Sized>(&'a self, q: &Q) -> InternalEntry<K, V, &'a RawTable<K, V>>
where K: Borrow<Q>,
Q: Eq + Hash
where
K: Borrow<Q>,
Q: Eq + Hash,
{
let hash = self.make_hash(q);
search_hashed(&self.table, hash, |k| q.eq(k.borrow()))
@ -554,8 +559,9 @@ impl<K, V, S> HashMap<K, V, S>
#[inline]
fn search_mut<'a, Q: ?Sized>(&'a mut self, q: &Q) -> InternalEntry<K, V, &'a mut RawTable<K, V>>
where K: Borrow<Q>,
Q: Eq + Hash
where
K: Borrow<Q>,
Q: Eq + Hash,
{
let hash = self.make_hash(q);
search_hashed(&mut self.table, hash, |k| q.eq(k.borrow()))
@ -574,7 +580,7 @@ impl<K, V, S> HashMap<K, V, S>
Empty(empty) => {
empty.put(hash, k, v);
return;
}
},
Full(b) => b.into_bucket(),
};
buckets.next();
@ -584,8 +590,9 @@ impl<K, V, S> HashMap<K, V, S>
}
impl<K, V, S> HashMap<K, V, S>
where K: Eq + Hash,
S: BuildHasher
where
K: Eq + Hash,
S: BuildHasher,
{
/// Creates an empty `HashMap` which will use the given hash builder to hash
/// keys.
@ -643,7 +650,10 @@ impl<K, V, S> HashMap<K, V, S>
/// map.insert(1, 2);
/// ```
#[inline]
pub fn try_with_capacity_and_hasher(capacity: usize, hash_builder: S) -> Result<HashMap<K, V, S>, FailedAllocationError> {
pub fn try_with_capacity_and_hasher(
capacity: usize,
hash_builder: S,
) -> Result<HashMap<K, V, S>, FailedAllocationError> {
let resize_policy = DefaultResizePolicy::new();
let raw_cap = resize_policy.raw_capacity(capacity);
Ok(HashMap {
@ -708,12 +718,14 @@ impl<K, V, S> HashMap<K, V, S>
self.try_reserve(additional).unwrap();
}
#[inline]
pub fn try_reserve(&mut self, additional: usize) -> Result<(), FailedAllocationError> {
let remaining = self.capacity() - self.len(); // this can't overflow
if remaining < additional {
let min_cap = self.len().checked_add(additional).expect("reserve overflow");
let min_cap = self
.len()
.checked_add(additional)
.expect("reserve overflow");
let raw_cap = self.resize_policy.raw_capacity(min_cap);
self.try_resize(raw_cap)?;
} else if self.table.tag() && remaining <= self.len() {
@ -763,7 +775,7 @@ impl<K, V, S> HashMap<K, V, S>
break;
}
b.into_bucket()
}
},
Empty(b) => b.into_bucket(),
};
bucket.next();
@ -822,7 +834,7 @@ impl<K, V, S> HashMap<K, V, S>
Some(Vacant(elem)) => {
elem.insert(v);
None
}
},
None => unreachable!(),
}
}
@ -892,7 +904,9 @@ impl<K, V, S> HashMap<K, V, S>
/// }
/// ```
pub fn values_mut(&mut self) -> ValuesMut<K, V> {
ValuesMut { inner: self.iter_mut() }
ValuesMut {
inner: self.iter_mut(),
}
}
/// An iterator visiting all key-value pairs in arbitrary order.
@ -913,7 +927,9 @@ impl<K, V, S> HashMap<K, V, S>
/// }
/// ```
pub fn iter(&self) -> Iter<K, V> {
Iter { inner: self.table.iter() }
Iter {
inner: self.table.iter(),
}
}
/// An iterator visiting all key-value pairs in arbitrary order,
@ -940,7 +956,9 @@ impl<K, V, S> HashMap<K, V, S>
/// }
/// ```
pub fn iter_mut(&mut self) -> IterMut<K, V> {
IterMut { inner: self.table.iter_mut() }
IterMut {
inner: self.table.iter_mut(),
}
}
/// Gets the given key's corresponding entry in the map for in-place manipulation.
@ -972,7 +990,8 @@ impl<K, V, S> HashMap<K, V, S>
self.try_reserve(1)?;
let hash = self.make_hash(&key);
Ok(search_hashed(&mut self.table, hash, |q| q.eq(&key))
.into_entry(key).expect("unreachable"))
.into_entry(key)
.expect("unreachable"))
}
/// Returns the number of elements in the map.
@ -1028,8 +1047,14 @@ impl<K, V, S> HashMap<K, V, S>
/// assert!(a.is_empty());
/// ```
#[inline]
pub fn drain(&mut self) -> Drain<K, V> where K: 'static, V: 'static {
Drain { inner: self.table.drain() }
pub fn drain(&mut self) -> Drain<K, V>
where
K: 'static,
V: 'static,
{
Drain {
inner: self.table.drain(),
}
}
/// Clears the map, removing all key-value pairs. Keeps the allocated memory
@ -1046,7 +1071,11 @@ impl<K, V, S> HashMap<K, V, S>
/// assert!(a.is_empty());
/// ```
#[inline]
pub fn clear(&mut self) where K: 'static, V: 'static {
pub fn clear(&mut self)
where
K: 'static,
V: 'static,
{
self.drain();
}
@ -1070,10 +1099,13 @@ impl<K, V, S> HashMap<K, V, S>
/// assert_eq!(map.get(&2), None);
/// ```
pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where K: Borrow<Q>,
Q: Hash + Eq
where
K: Borrow<Q>,
Q: Hash + Eq,
{
self.search(k).into_occupied_bucket().map(|bucket| bucket.into_refs().1)
self.search(k)
.into_occupied_bucket()
.map(|bucket| bucket.into_refs().1)
}
/// Returns true if the map contains a value for the specified key.
@ -1096,8 +1128,9 @@ impl<K, V, S> HashMap<K, V, S>
/// assert_eq!(map.contains_key(&2), false);
/// ```
pub fn contains_key<Q: ?Sized>(&self, k: &Q) -> bool
where K: Borrow<Q>,
Q: Hash + Eq
where
K: Borrow<Q>,
Q: Hash + Eq,
{
self.search(k).into_occupied_bucket().is_some()
}
@ -1124,10 +1157,13 @@ impl<K, V, S> HashMap<K, V, S>
/// assert_eq!(map[&1], "b");
/// ```
pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
where K: Borrow<Q>,
Q: Hash + Eq
where
K: Borrow<Q>,
Q: Hash + Eq,
{
self.search_mut(k).into_occupied_bucket().map(|bucket| bucket.into_mut_refs().1)
self.search_mut(k)
.into_occupied_bucket()
.map(|bucket| bucket.into_mut_refs().1)
}
/// Inserts a key-value pair into the map.
@ -1187,14 +1223,17 @@ impl<K, V, S> HashMap<K, V, S>
/// assert_eq!(map.remove(&1), None);
/// ```
pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V>
where K: Borrow<Q>,
Q: Hash + Eq
where
K: Borrow<Q>,
Q: Hash + Eq,
{
if self.table.size() == 0 {
return None;
}
self.search_mut(k).into_occupied_bucket().map(|bucket| pop_internal(bucket).1)
self.search_mut(k)
.into_occupied_bucket()
.map(|bucket| pop_internal(bucket).1)
}
/// Retains only the elements specified by the predicate.
@ -1211,7 +1250,8 @@ impl<K, V, S> HashMap<K, V, S>
/// assert_eq!(map.len(), 4);
/// ```
pub fn retain<F>(&mut self, mut f: F)
where F: FnMut(&K, &mut V) -> bool
where
F: FnMut(&K, &mut V) -> bool,
{
if self.table.size() == 0 {
return;
@ -1236,41 +1276,43 @@ impl<K, V, S> HashMap<K, V, S>
full.into_bucket()
}
},
Empty(b) => {
b.into_bucket()
}
Empty(b) => b.into_bucket(),
};
bucket.prev(); // reverse iteration
bucket.prev(); // reverse iteration
debug_assert!(elems_left == 0 || bucket.index() != start_index);
}
}
}
impl<K, V, S> PartialEq for HashMap<K, V, S>
where K: Eq + Hash,
V: PartialEq,
S: BuildHasher
where
K: Eq + Hash,
V: PartialEq,
S: BuildHasher,
{
fn eq(&self, other: &HashMap<K, V, S>) -> bool {
if self.len() != other.len() {
return false;
}
self.iter().all(|(key, value)| other.get(key).map_or(false, |v| *value == *v))
self.iter()
.all(|(key, value)| other.get(key).map_or(false, |v| *value == *v))
}
}
impl<K, V, S> Eq for HashMap<K, V, S>
where K: Eq + Hash,
V: Eq,
S: BuildHasher
where
K: Eq + Hash,
V: Eq,
S: BuildHasher,
{
}
impl<K, V, S> Debug for HashMap<K, V, S>
where K: Eq + Hash + Debug,
V: Debug,
S: BuildHasher
where
K: Eq + Hash + Debug,
V: Debug,
S: BuildHasher,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_map().entries(self.iter()).finish()
@ -1278,8 +1320,9 @@ impl<K, V, S> Debug for HashMap<K, V, S>
}
impl<K, V, S> Default for HashMap<K, V, S>
where K: Eq + Hash,
S: BuildHasher + Default
where
K: Eq + Hash,
S: BuildHasher + Default,
{
/// Creates an empty `HashMap<K, V, S>`, with the `Default` value for the hasher.
fn default() -> HashMap<K, V, S> {
@ -1288,9 +1331,10 @@ impl<K, V, S> Default for HashMap<K, V, S>
}
impl<'a, K, Q: ?Sized, V, S> Index<&'a Q> for HashMap<K, V, S>
where K: Eq + Hash + Borrow<Q>,
Q: Eq + Hash,
S: BuildHasher
where
K: Eq + Hash + Borrow<Q>,
Q: Eq + Hash,
S: BuildHasher,
{
type Output = V;
@ -1314,15 +1358,15 @@ pub struct Iter<'a, K: 'a, V: 'a> {
// FIXME(#19839) Remove in favor of `#[derive(Clone)]`
impl<'a, K, V> Clone for Iter<'a, K, V> {
fn clone(&self) -> Iter<'a, K, V> {
Iter { inner: self.inner.clone() }
Iter {
inner: self.inner.clone(),
}
}
}
impl<'a, K: Debug, V: Debug> fmt::Debug for Iter<'a, K, V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list()
.entries(self.clone())
.finish()
f.debug_list().entries(self.clone()).finish()
}
}
@ -1362,15 +1406,15 @@ pub struct Keys<'a, K: 'a, V: 'a> {
// FIXME(#19839) Remove in favor of `#[derive(Clone)]`
impl<'a, K, V> Clone for Keys<'a, K, V> {
fn clone(&self) -> Keys<'a, K, V> {
Keys { inner: self.inner.clone() }
Keys {
inner: self.inner.clone(),
}
}
}
impl<'a, K: Debug, V> fmt::Debug for Keys<'a, K, V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list()
.entries(self.clone())
.finish()
f.debug_list().entries(self.clone()).finish()
}
}
@ -1388,15 +1432,15 @@ pub struct Values<'a, K: 'a, V: 'a> {
// FIXME(#19839) Remove in favor of `#[derive(Clone)]`
impl<'a, K, V> Clone for Values<'a, K, V> {
fn clone(&self) -> Values<'a, K, V> {
Values { inner: self.inner.clone() }
Values {
inner: self.inner.clone(),
}
}
}
impl<'a, K, V: Debug> fmt::Debug for Values<'a, K, V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list()
.entries(self.clone())
.finish()
f.debug_list().entries(self.clone()).finish()
}
}
@ -1423,7 +1467,9 @@ pub struct ValuesMut<'a, K: 'a, V: 'a> {
}
enum InternalEntry<K, V, M> {
Occupied { elem: FullBucket<K, V, M> },
Occupied {
elem: FullBucket<K, V, M>,
},
Vacant {
hash: SafeHash,
elem: VacantEntryState<K, V, M>,
@ -1445,19 +1491,11 @@ impl<'a, K, V> InternalEntry<K, V, &'a mut RawTable<K, V>> {
#[inline]
fn into_entry(self, key: K) -> Option<Entry<'a, K, V>> {
match self {
InternalEntry::Occupied { elem } => {
Some(Occupied(OccupiedEntry {
key: Some(key),
elem,
}))
}
InternalEntry::Vacant { hash, elem } => {
Some(Vacant(VacantEntry {
hash,
key,
elem,
}))
}
InternalEntry::Occupied { elem } => Some(Occupied(OccupiedEntry {
key: Some(key),
elem,
})),
InternalEntry::Vacant { hash, elem } => Some(Vacant(VacantEntry { hash, key, elem })),
InternalEntry::TableIsEmpty => None,
}
}
@ -1471,25 +1509,17 @@ impl<'a, K, V> InternalEntry<K, V, &'a mut RawTable<K, V>> {
/// [`entry`]: struct.HashMap.html#method.entry
pub enum Entry<'a, K: 'a, V: 'a> {
/// An occupied entry.
Occupied( OccupiedEntry<'a, K, V>),
Occupied(OccupiedEntry<'a, K, V>),
/// A vacant entry.
Vacant( VacantEntry<'a, K, V>),
Vacant(VacantEntry<'a, K, V>),
}
impl<'a, K: 'a + Debug, V: 'a + Debug> Debug for Entry<'a, K, V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Vacant(ref v) => {
f.debug_tuple("Entry")
.field(v)
.finish()
}
Occupied(ref o) => {
f.debug_tuple("Entry")
.field(o)
.finish()
}
Vacant(ref v) => f.debug_tuple("Entry").field(v).finish(),
Occupied(ref o) => f.debug_tuple("Entry").field(o).finish(),
}
}
}
@ -1524,9 +1554,7 @@ pub struct VacantEntry<'a, K: 'a, V: 'a> {
impl<'a, K: 'a + Debug, V: 'a> Debug for VacantEntry<'a, K, V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("VacantEntry")
.field(self.key())
.finish()
f.debug_tuple("VacantEntry").field(self.key()).finish()
}
}
@ -1540,8 +1568,9 @@ enum VacantEntryState<K, V, M> {
}
impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S>
where K: Eq + Hash,
S: BuildHasher
where
K: Eq + Hash,
S: BuildHasher,
{
type Item = (&'a K, &'a V);
type IntoIter = Iter<'a, K, V>;
@ -1552,8 +1581,9 @@ impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S>
}
impl<'a, K, V, S> IntoIterator for &'a mut HashMap<K, V, S>
where K: Eq + Hash,
S: BuildHasher
where
K: Eq + Hash,
S: BuildHasher,
{
type Item = (&'a K, &'a mut V);
type IntoIter = IterMut<'a, K, V>;
@ -1564,8 +1594,9 @@ impl<'a, K, V, S> IntoIterator for &'a mut HashMap<K, V, S>
}
impl<K, V, S> IntoIterator for HashMap<K, V, S>
where K: Eq + Hash,
S: BuildHasher
where
K: Eq + Hash,
S: BuildHasher,
{
type Item = (K, V);
type IntoIter = IntoIter<K, V>;
@ -1588,7 +1619,9 @@ impl<K, V, S> IntoIterator for HashMap<K, V, S>
/// let vec: Vec<(&str, isize)> = map.into_iter().collect();
/// ```
fn into_iter(self) -> IntoIter<K, V> {
IntoIter { inner: self.table.into_iter() }
IntoIter {
inner: self.table.into_iter(),
}
}
}
@ -1611,7 +1644,6 @@ impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> {
}
}
impl<'a, K, V> Iterator for IterMut<'a, K, V> {
type Item = (&'a K, &'a mut V);
@ -1632,13 +1664,12 @@ impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> {
}
impl<'a, K, V> fmt::Debug for IterMut<'a, K, V>
where K: fmt::Debug,
V: fmt::Debug,
where
K: fmt::Debug,
V: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list()
.entries(self.inner.iter())
.finish()
f.debug_list().entries(self.inner.iter()).finish()
}
}
@ -1663,9 +1694,7 @@ impl<K, V> ExactSizeIterator for IntoIter<K, V> {
impl<K: Debug, V: Debug> fmt::Debug for IntoIter<K, V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list()
.entries(self.inner.iter())
.finish()
f.debug_list().entries(self.inner.iter()).finish()
}
}
@ -1726,13 +1755,12 @@ impl<'a, K, V> ExactSizeIterator for ValuesMut<'a, K, V> {
}
impl<'a, K, V> fmt::Debug for ValuesMut<'a, K, V>
where K: fmt::Debug,
V: fmt::Debug,
where
K: fmt::Debug,
V: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list()
.entries(self.inner.inner.iter())
.finish()
f.debug_list().entries(self.inner.inner.iter()).finish()
}
}
@ -1756,20 +1784,19 @@ impl<'a, K, V> ExactSizeIterator for Drain<'a, K, V> {
}
impl<'a, K, V> fmt::Debug for Drain<'a, K, V>
where K: fmt::Debug,
V: fmt::Debug,
where
K: fmt::Debug,
V: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list()
.entries(self.inner.iter())
.finish()
f.debug_list().entries(self.inner.iter()).finish()
}
}
// FORK NOTE: Removed Placer impl
impl<'a, K, V> Entry<'a, K, V> {
/// Ensures a value is in the entry by inserting the default if empty, and returns
/// Ensures a value is in the entry by inserting the default if empty, and returns
/// a mutable reference to the value in the entry.
///
/// # Examples
@ -1792,7 +1819,7 @@ impl<'a, K, V> Entry<'a, K, V> {
}
}
/// Ensures a value is in the entry by inserting the result of the default function if empty,
/// Ensures a value is in the entry by inserting the result of the default function if empty,
/// and returns a mutable reference to the value in the entry.
///
/// # Examples
@ -1824,7 +1851,7 @@ impl<'a, K, V> Entry<'a, K, V> {
/// let mut map: HashMap<&str, u32> = HashMap::new();
/// assert_eq!(map.entry("poneyland").key(), &"poneyland");
/// ```
pub fn key(&self) -> &K {
pub fn key(&self) -> &K {
match *self {
Occupied(ref entry) => entry.key(),
Vacant(ref entry) => entry.key(),
@ -1844,7 +1871,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
/// map.entry("poneyland").or_insert(12);
/// assert_eq!(map.entry("poneyland").key(), &"poneyland");
/// ```
pub fn key(&self) -> &K {
pub fn key(&self) -> &K {
self.elem.read().0
}
@ -1866,7 +1893,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
///
/// assert_eq!(map.contains_key("poneyland"), false);
/// ```
pub fn remove_entry(self) -> (K, V) {
pub fn remove_entry(self) -> (K, V) {
let (k, v, _) = pop_internal(self.elem);
(k, v)
}
@ -1886,7 +1913,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
/// assert_eq!(o.get(), &12);
/// }
/// ```
pub fn get(&self) -> &V {
pub fn get(&self) -> &V {
self.elem.read().1
}
@ -1908,7 +1935,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
///
/// assert_eq!(map["poneyland"], 22);
/// ```
pub fn get_mut(&mut self) -> &mut V {
pub fn get_mut(&mut self) -> &mut V {
self.elem.read_mut().1
}
@ -1931,7 +1958,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
///
/// assert_eq!(map["poneyland"], 22);
/// ```
pub fn into_mut(self) -> &'a mut V {
pub fn into_mut(self) -> &'a mut V {
self.elem.into_mut_refs().1
}
@ -1952,7 +1979,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
///
/// assert_eq!(map["poneyland"], 15);
/// ```
pub fn insert(&mut self, mut value: V) -> V {
pub fn insert(&mut self, mut value: V) -> V {
let old_value = self.get_mut();
mem::swap(&mut value, old_value);
value
@ -1975,7 +2002,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
///
/// assert_eq!(map.contains_key("poneyland"), false);
/// ```
pub fn remove(self) -> V {
pub fn remove(self) -> V {
pop_internal(self.elem).1
}
@ -1999,7 +2026,7 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> {
/// let mut map: HashMap<&str, u32> = HashMap::new();
/// assert_eq!(map.entry("poneyland").key(), &"poneyland");
/// ```
pub fn key(&self) -> &K {
pub fn key(&self) -> &K {
&self.key
}
@ -2017,7 +2044,7 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> {
/// v.into_key();
/// }
/// ```
pub fn into_key(self) -> K {
pub fn into_key(self) -> K {
self.key
}
@ -2037,7 +2064,7 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> {
/// }
/// assert_eq!(map["poneyland"], 37);
/// ```
pub fn insert(self, value: V) -> &'a mut V {
pub fn insert(self, value: V) -> &'a mut V {
let b = match self.elem {
NeqElem(mut bucket, disp) => {
if disp >= DISPLACEMENT_THRESHOLD {
@ -2057,8 +2084,9 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> {
}
impl<K, V, S> FromIterator<(K, V)> for HashMap<K, V, S>
where K: Eq + Hash,
S: BuildHasher + Default
where
K: Eq + Hash,
S: BuildHasher + Default,
{
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> HashMap<K, V, S> {
let mut map = HashMap::with_hasher(Default::default());
@ -2068,8 +2096,9 @@ impl<K, V, S> FromIterator<(K, V)> for HashMap<K, V, S>
}
impl<K, V, S> Extend<(K, V)> for HashMap<K, V, S>
where K: Eq + Hash,
S: BuildHasher
where
K: Eq + Hash,
S: BuildHasher,
{
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
// Keys may be already present or show multiple times in the iterator.
@ -2090,9 +2119,10 @@ impl<K, V, S> Extend<(K, V)> for HashMap<K, V, S>
}
impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap<K, V, S>
where K: Eq + Hash + Copy,
V: Copy,
S: BuildHasher
where
K: Eq + Hash + Copy,
V: Copy,
S: BuildHasher,
{
fn extend<T: IntoIterator<Item = (&'a K, &'a V)>>(&mut self, iter: T) {
self.extend(iter.into_iter().map(|(&key, &value)| (key, value)));
@ -2102,16 +2132,18 @@ impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap<K, V, S>
// FORK NOTE: These can be reused
pub use std::collections::hash_map::{DefaultHasher, RandomState};
impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S>
where K: Eq + Hash + Borrow<Q>,
S: BuildHasher,
Q: Eq + Hash
where
K: Eq + Hash + Borrow<Q>,
S: BuildHasher,
Q: Eq + Hash,
{
type Key = K;
fn get(&self, key: &Q) -> Option<&K> {
self.search(key).into_occupied_bucket().map(|bucket| bucket.into_refs().0)
self.search(key)
.into_occupied_bucket()
.map(|bucket| bucket.into_refs().0)
}
fn take(&mut self, key: &Q) -> Option<K> {
@ -2119,7 +2151,9 @@ impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S>
return None;
}
self.search_mut(key).into_occupied_bucket().map(|bucket| pop_internal(bucket).0)
self.search_mut(key)
.into_occupied_bucket()
.map(|bucket| pop_internal(bucket).0)
}
fn replace(&mut self, key: K) -> Option<K> {
@ -2129,11 +2163,11 @@ impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S>
Occupied(mut occupied) => {
let key = occupied.take_key().unwrap();
Some(mem::replace(occupied.elem.read_mut().0, key))
}
},
Vacant(vacant) => {
vacant.insert(());
None
}
},
}
}
}
@ -2170,8 +2204,9 @@ fn assert_covariance() {
fn values_val<'a, 'new>(v: Values<'a, u8, &'static str>) -> Values<'a, u8, &'new str> {
v
}
fn drain<'new>(d: Drain<'static, &'static str, &'static str>)
-> Drain<'new, &'new str, &'new str> {
fn drain<'new>(
d: Drain<'static, &'static str, &'static str>,
) -> Drain<'new, &'new str, &'new str> {
d
}
}
@ -2319,19 +2354,19 @@ mod test_map {
DROP_VECTOR.with(|v| {
assert_eq!(v.borrow()[i], 1);
assert_eq!(v.borrow()[i+100], 1);
assert_eq!(v.borrow()[i + 100], 1);
});
}
DROP_VECTOR.with(|v| {
for i in 0..50 {
assert_eq!(v.borrow()[i], 0);
assert_eq!(v.borrow()[i+100], 0);
assert_eq!(v.borrow()[i + 100], 0);
}
for i in 50..100 {
assert_eq!(v.borrow()[i], 1);
assert_eq!(v.borrow()[i+100], 1);
assert_eq!(v.borrow()[i + 100], 1);
}
});
}
@ -2388,13 +2423,9 @@ mod test_map {
for _ in half.by_ref() {}
DROP_VECTOR.with(|v| {
let nk = (0..100)
.filter(|&i| v.borrow()[i] == 1)
.count();
let nk = (0..100).filter(|&i| v.borrow()[i] == 1).count();
let nv = (0..100)
.filter(|&i| v.borrow()[i + 100] == 1)
.count();
let nv = (0..100).filter(|&i| v.borrow()[i + 100] == 1).count();
assert_eq!(nk, 50);
assert_eq!(nv, 50);
@ -2419,7 +2450,7 @@ mod test_map {
let mut m: HashMap<isize, bool> = HashMap::new();
match m.entry(0) {
Occupied(_) => panic!(),
Vacant(_) => {}
Vacant(_) => {},
}
assert!(*m.entry(0).or_insert(true));
assert_eq!(m.len(), 1);
@ -2574,7 +2605,7 @@ mod test_map {
fn test_iterate() {
let mut m = HashMap::with_capacity(4);
for i in 0..32 {
assert!(m.insert(i, i*2).is_none());
assert!(m.insert(i, i * 2).is_none());
}
assert_eq!(m.len(), 32);
@ -2662,8 +2693,7 @@ mod test_map {
let map_str = format!("{:?}", map);
assert!(map_str == "{1: 2, 3: 4}" ||
map_str == "{3: 4, 1: 2}");
assert!(map_str == "{1: 2, 3: 4}" || map_str == "{3: 4, 1: 2}");
assert_eq!(format!("{:?}", empty), "{}");
}
@ -2876,12 +2906,11 @@ mod test_map {
Occupied(mut view) => {
assert_eq!(view.get(), &10);
assert_eq!(view.insert(100), 10);
}
},
}
assert_eq!(map.get(&1).unwrap(), &100);
assert_eq!(map.len(), 6);
// Existing key (update)
match map.entry(2) {
Vacant(_) => unreachable!(),
@ -2889,7 +2918,7 @@ mod test_map {
let v = view.get_mut();
let new_v = (*v) * 10;
*v = new_v;
}
},
}
assert_eq!(map.get(&2).unwrap(), &200);
assert_eq!(map.len(), 6);
@ -2899,18 +2928,17 @@ mod test_map {
Vacant(_) => unreachable!(),
Occupied(view) => {
assert_eq!(view.remove(), 30);
}
},
}
assert_eq!(map.get(&3), None);
assert_eq!(map.len(), 5);
// Inexistent key (insert)
match map.entry(10) {
Occupied(_) => unreachable!(),
Vacant(view) => {
assert_eq!(*view.insert(1000), 1000);
}
},
}
assert_eq!(map.get(&10).unwrap(), &1000);
assert_eq!(map.len(), 6);
@ -2919,11 +2947,10 @@ mod test_map {
#[test]
fn test_entry_take_doesnt_corrupt() {
#![allow(deprecated)] //rand
// Test for #19292
// Test for #19292
fn check(m: &HashMap<isize, ()>) {
for k in m.keys() {
assert!(m.contains_key(k),
"{} is in keys() but not in the map?", k);
assert!(m.contains_key(k), "{} is in keys() but not in the map?", k);
}
}
@ -2939,11 +2966,11 @@ mod test_map {
for i in 0..1000 {
let x = rng.gen_range(-10, 10);
match m.entry(x) {
Vacant(_) => {}
Vacant(_) => {},
Occupied(e) => {
println!("{}: remove {}", i, x);
e.remove();
}
},
}
check(&m);
@ -3021,7 +3048,7 @@ mod test_map {
Vacant(e) => {
assert_eq!(key, *e.key());
e.insert(value.clone());
}
},
}
assert_eq!(a.len(), 1);
assert_eq!(a[key], value);
@ -3029,7 +3056,7 @@ mod test_map {
#[test]
fn test_retain() {
let mut map: HashMap<isize, isize> = (0..100).map(|x|(x, x*10)).collect();
let mut map: HashMap<isize, isize> = (0..100).map(|x| (x, x * 10)).collect();
map.retain(|&k, _| k % 2 == 0);
assert_eq!(map.len(), 50);

View file

@ -122,8 +122,9 @@ pub struct HashSet<T, S = RandomState> {
}
impl<T, S> HashSet<T, S>
where T: Eq + Hash,
S: BuildHasher
where
T: Eq + Hash,
S: BuildHasher,
{
/// Creates a new empty hash set which will use the given hasher to hash
/// keys.
@ -147,7 +148,9 @@ impl<T, S> HashSet<T, S>
/// ```
#[inline]
pub fn with_hasher(hasher: S) -> HashSet<T, S> {
HashSet { map: HashMap::with_hasher(hasher) }
HashSet {
map: HashMap::with_hasher(hasher),
}
}
/// Creates an empty `HashSet` with with the specified capacity, using
@ -173,7 +176,9 @@ impl<T, S> HashSet<T, S>
/// ```
#[inline]
pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet<T, S> {
HashSet { map: HashMap::with_capacity_and_hasher(capacity, hasher) }
HashSet {
map: HashMap::with_capacity_and_hasher(capacity, hasher),
}
}
/// Returns a reference to the set's [`BuildHasher`].
@ -265,7 +270,9 @@ impl<T, S> HashSet<T, S>
/// }
/// ```
pub fn iter(&self) -> Iter<T> {
Iter { iter: self.map.keys() }
Iter {
iter: self.map.keys(),
}
}
/// Visits the values representing the difference,
@ -319,10 +326,13 @@ impl<T, S> HashSet<T, S>
/// assert_eq!(diff1, diff2);
/// assert_eq!(diff1, [1, 4].iter().collect());
/// ```
pub fn symmetric_difference<'a>(&'a self,
other: &'a HashSet<T, S>)
-> SymmetricDifference<'a, T, S> {
SymmetricDifference { iter: self.difference(other).chain(other.difference(self)) }
pub fn symmetric_difference<'a>(
&'a self,
other: &'a HashSet<T, S>,
) -> SymmetricDifference<'a, T, S> {
SymmetricDifference {
iter: self.difference(other).chain(other.difference(self)),
}
}
/// Visits the values representing the intersection,
@ -369,7 +379,9 @@ impl<T, S> HashSet<T, S>
/// assert_eq!(union, [1, 2, 3, 4].iter().collect());
/// ```
pub fn union<'a>(&'a self, other: &'a HashSet<T, S>) -> Union<'a, T, S> {
Union { iter: self.iter().chain(other.difference(self)) }
Union {
iter: self.iter().chain(other.difference(self)),
}
}
/// Returns the number of elements in the set.
@ -423,7 +435,9 @@ impl<T, S> HashSet<T, S>
/// ```
#[inline]
pub fn drain(&mut self) -> Drain<T> {
Drain { iter: self.map.drain() }
Drain {
iter: self.map.drain(),
}
}
/// Clears the set, removing all values.
@ -438,7 +452,10 @@ impl<T, S> HashSet<T, S>
/// v.clear();
/// assert!(v.is_empty());
/// ```
pub fn clear(&mut self) where T: 'static {
pub fn clear(&mut self)
where
T: 'static,
{
self.map.clear()
}
@ -461,8 +478,9 @@ impl<T, S> HashSet<T, S>
/// [`Eq`]: ../../std/cmp/trait.Eq.html
/// [`Hash`]: ../../std/hash/trait.Hash.html
pub fn contains<Q: ?Sized>(&self, value: &Q) -> bool
where T: Borrow<Q>,
Q: Hash + Eq
where
T: Borrow<Q>,
Q: Hash + Eq,
{
self.map.contains_key(value)
}
@ -476,8 +494,9 @@ impl<T, S> HashSet<T, S>
/// [`Eq`]: ../../std/cmp/trait.Eq.html
/// [`Hash`]: ../../std/hash/trait.Hash.html
pub fn get<Q: ?Sized>(&self, value: &Q) -> Option<&T>
where T: Borrow<Q>,
Q: Hash + Eq
where
T: Borrow<Q>,
Q: Hash + Eq,
{
Recover::get(&self.map, value)
}
@ -598,8 +617,9 @@ impl<T, S> HashSet<T, S>
/// [`Eq`]: ../../std/cmp/trait.Eq.html
/// [`Hash`]: ../../std/hash/trait.Hash.html
pub fn remove<Q: ?Sized>(&mut self, value: &Q) -> bool
where T: Borrow<Q>,
Q: Hash + Eq
where
T: Borrow<Q>,
Q: Hash + Eq,
{
self.map.remove(value).is_some()
}
@ -613,8 +633,9 @@ impl<T, S> HashSet<T, S>
/// [`Eq`]: ../../std/cmp/trait.Eq.html
/// [`Hash`]: ../../std/hash/trait.Hash.html
pub fn take<Q: ?Sized>(&mut self, value: &Q) -> Option<T>
where T: Borrow<Q>,
Q: Hash + Eq
where
T: Borrow<Q>,
Q: Hash + Eq,
{
Recover::take(&mut self.map, value)
}
@ -634,15 +655,17 @@ impl<T, S> HashSet<T, S>
/// assert_eq!(set.len(), 3);
/// ```
pub fn retain<F>(&mut self, mut f: F)
where F: FnMut(&T) -> bool
where
F: FnMut(&T) -> bool,
{
self.map.retain(|k, _| f(k));
}
}
impl<T, S> PartialEq for HashSet<T, S>
where T: Eq + Hash,
S: BuildHasher
where
T: Eq + Hash,
S: BuildHasher,
{
fn eq(&self, other: &HashSet<T, S>) -> bool {
if self.len() != other.len() {
@ -654,14 +677,16 @@ impl<T, S> PartialEq for HashSet<T, S>
}
impl<T, S> Eq for HashSet<T, S>
where T: Eq + Hash,
S: BuildHasher
where
T: Eq + Hash,
S: BuildHasher,
{
}
impl<T, S> fmt::Debug for HashSet<T, S>
where T: Eq + Hash + fmt::Debug,
S: BuildHasher
where
T: Eq + Hash + fmt::Debug,
S: BuildHasher,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_set().entries(self.iter()).finish()
@ -669,8 +694,9 @@ impl<T, S> fmt::Debug for HashSet<T, S>
}
impl<T, S> FromIterator<T> for HashSet<T, S>
where T: Eq + Hash,
S: BuildHasher + Default
where
T: Eq + Hash,
S: BuildHasher + Default,
{
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> HashSet<T, S> {
let mut set = HashSet::with_hasher(Default::default());
@ -680,8 +706,9 @@ impl<T, S> FromIterator<T> for HashSet<T, S>
}
impl<T, S> Extend<T> for HashSet<T, S>
where T: Eq + Hash,
S: BuildHasher
where
T: Eq + Hash,
S: BuildHasher,
{
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
self.map.extend(iter.into_iter().map(|k| (k, ())));
@ -689,8 +716,9 @@ impl<T, S> Extend<T> for HashSet<T, S>
}
impl<'a, T, S> Extend<&'a T> for HashSet<T, S>
where T: 'a + Eq + Hash + Copy,
S: BuildHasher
where
T: 'a + Eq + Hash + Copy,
S: BuildHasher,
{
fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
self.extend(iter.into_iter().cloned());
@ -698,18 +726,22 @@ impl<'a, T, S> Extend<&'a T> for HashSet<T, S>
}
impl<T, S> Default for HashSet<T, S>
where T: Eq + Hash,
S: BuildHasher + Default
where
T: Eq + Hash,
S: BuildHasher + Default,
{
/// Creates an empty `HashSet<T, S>` with the `Default` value for the hasher.
fn default() -> HashSet<T, S> {
HashSet { map: HashMap::default() }
HashSet {
map: HashMap::default(),
}
}
}
impl<'a, 'b, T, S> BitOr<&'b HashSet<T, S>> for &'a HashSet<T, S>
where T: Eq + Hash + Clone,
S: BuildHasher + Default
where
T: Eq + Hash + Clone,
S: BuildHasher + Default,
{
type Output = HashSet<T, S>;
@ -739,8 +771,9 @@ impl<'a, 'b, T, S> BitOr<&'b HashSet<T, S>> for &'a HashSet<T, S>
}
impl<'a, 'b, T, S> BitAnd<&'b HashSet<T, S>> for &'a HashSet<T, S>
where T: Eq + Hash + Clone,
S: BuildHasher + Default
where
T: Eq + Hash + Clone,
S: BuildHasher + Default,
{
type Output = HashSet<T, S>;
@ -770,8 +803,9 @@ impl<'a, 'b, T, S> BitAnd<&'b HashSet<T, S>> for &'a HashSet<T, S>
}
impl<'a, 'b, T, S> BitXor<&'b HashSet<T, S>> for &'a HashSet<T, S>
where T: Eq + Hash + Clone,
S: BuildHasher + Default
where
T: Eq + Hash + Clone,
S: BuildHasher + Default,
{
type Output = HashSet<T, S>;
@ -801,8 +835,9 @@ impl<'a, 'b, T, S> BitXor<&'b HashSet<T, S>> for &'a HashSet<T, S>
}
impl<'a, 'b, T, S> Sub<&'b HashSet<T, S>> for &'a HashSet<T, S>
where T: Eq + Hash + Clone,
S: BuildHasher + Default
where
T: Eq + Hash + Clone,
S: BuildHasher + Default,
{
type Output = HashSet<T, S>;
@ -915,8 +950,9 @@ pub struct Union<'a, T: 'a, S: 'a> {
}
impl<'a, T, S> IntoIterator for &'a HashSet<T, S>
where T: Eq + Hash,
S: BuildHasher
where
T: Eq + Hash,
S: BuildHasher,
{
type Item = &'a T;
type IntoIter = Iter<'a, T>;
@ -927,8 +963,9 @@ impl<'a, T, S> IntoIterator for &'a HashSet<T, S>
}
impl<T, S> IntoIterator for HashSet<T, S>
where T: Eq + Hash,
S: BuildHasher
where
T: Eq + Hash,
S: BuildHasher,
{
type Item = T;
type IntoIter = IntoIter<T>;
@ -954,13 +991,17 @@ impl<T, S> IntoIterator for HashSet<T, S>
/// }
/// ```
fn into_iter(self) -> IntoIter<T> {
IntoIter { iter: self.map.into_iter() }
IntoIter {
iter: self.map.into_iter(),
}
}
}
impl<'a, K> Clone for Iter<'a, K> {
fn clone(&self) -> Iter<'a, K> {
Iter { iter: self.iter.clone() }
Iter {
iter: self.iter.clone(),
}
}
}
impl<'a, K> Iterator for Iter<'a, K> {
@ -1003,10 +1044,7 @@ impl<K> ExactSizeIterator for IntoIter<K> {
impl<K: fmt::Debug> fmt::Debug for IntoIter<K> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let entries_iter = self.iter
.inner
.iter()
.map(|(k, _)| k);
let entries_iter = self.iter.inner.iter().map(|(k, _)| k);
f.debug_list().entries(entries_iter).finish()
}
}
@ -1029,23 +1067,24 @@ impl<'a, K> ExactSizeIterator for Drain<'a, K> {
impl<'a, K: fmt::Debug> fmt::Debug for Drain<'a, K> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let entries_iter = self.iter
.inner
.iter()
.map(|(k, _)| k);
let entries_iter = self.iter.inner.iter().map(|(k, _)| k);
f.debug_list().entries(entries_iter).finish()
}
}
impl<'a, T, S> Clone for Intersection<'a, T, S> {
fn clone(&self) -> Intersection<'a, T, S> {
Intersection { iter: self.iter.clone(), ..*self }
Intersection {
iter: self.iter.clone(),
..*self
}
}
}
impl<'a, T, S> Iterator for Intersection<'a, T, S>
where T: Eq + Hash,
S: BuildHasher
where
T: Eq + Hash,
S: BuildHasher,
{
type Item = &'a T;
@ -1065,8 +1104,9 @@ impl<'a, T, S> Iterator for Intersection<'a, T, S>
}
impl<'a, T, S> fmt::Debug for Intersection<'a, T, S>
where T: fmt::Debug + Eq + Hash,
S: BuildHasher
where
T: fmt::Debug + Eq + Hash,
S: BuildHasher,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list().entries(self.clone()).finish()
@ -1075,13 +1115,17 @@ impl<'a, T, S> fmt::Debug for Intersection<'a, T, S>
impl<'a, T, S> Clone for Difference<'a, T, S> {
fn clone(&self) -> Difference<'a, T, S> {
Difference { iter: self.iter.clone(), ..*self }
Difference {
iter: self.iter.clone(),
..*self
}
}
}
impl<'a, T, S> Iterator for Difference<'a, T, S>
where T: Eq + Hash,
S: BuildHasher
where
T: Eq + Hash,
S: BuildHasher,
{
type Item = &'a T;
@ -1101,8 +1145,9 @@ impl<'a, T, S> Iterator for Difference<'a, T, S>
}
impl<'a, T, S> fmt::Debug for Difference<'a, T, S>
where T: fmt::Debug + Eq + Hash,
S: BuildHasher
where
T: fmt::Debug + Eq + Hash,
S: BuildHasher,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list().entries(self.clone()).finish()
@ -1111,13 +1156,16 @@ impl<'a, T, S> fmt::Debug for Difference<'a, T, S>
impl<'a, T, S> Clone for SymmetricDifference<'a, T, S> {
fn clone(&self) -> SymmetricDifference<'a, T, S> {
SymmetricDifference { iter: self.iter.clone() }
SymmetricDifference {
iter: self.iter.clone(),
}
}
}
impl<'a, T, S> Iterator for SymmetricDifference<'a, T, S>
where T: Eq + Hash,
S: BuildHasher
where
T: Eq + Hash,
S: BuildHasher,
{
type Item = &'a T;
@ -1130,8 +1178,9 @@ impl<'a, T, S> Iterator for SymmetricDifference<'a, T, S>
}
impl<'a, T, S> fmt::Debug for SymmetricDifference<'a, T, S>
where T: fmt::Debug + Eq + Hash,
S: BuildHasher
where
T: fmt::Debug + Eq + Hash,
S: BuildHasher,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list().entries(self.clone()).finish()
@ -1140,13 +1189,16 @@ impl<'a, T, S> fmt::Debug for SymmetricDifference<'a, T, S>
impl<'a, T, S> Clone for Union<'a, T, S> {
fn clone(&self) -> Union<'a, T, S> {
Union { iter: self.iter.clone() }
Union {
iter: self.iter.clone(),
}
}
}
impl<'a, T, S> fmt::Debug for Union<'a, T, S>
where T: fmt::Debug + Eq + Hash,
S: BuildHasher
where
T: fmt::Debug + Eq + Hash,
S: BuildHasher,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list().entries(self.clone()).finish()
@ -1154,8 +1206,9 @@ impl<'a, T, S> fmt::Debug for Union<'a, T, S>
}
impl<'a, T, S> Iterator for Union<'a, T, S>
where T: Eq + Hash,
S: BuildHasher
where
T: Eq + Hash,
S: BuildHasher,
{
type Item = &'a T;
@ -1178,20 +1231,24 @@ fn assert_covariance() {
fn into_iter<'new>(v: IntoIter<&'static str>) -> IntoIter<&'new str> {
v
}
fn difference<'a, 'new>(v: Difference<'a, &'static str, RandomState>)
-> Difference<'a, &'new str, RandomState> {
fn difference<'a, 'new>(
v: Difference<'a, &'static str, RandomState>,
) -> Difference<'a, &'new str, RandomState> {
v
}
fn symmetric_difference<'a, 'new>(v: SymmetricDifference<'a, &'static str, RandomState>)
-> SymmetricDifference<'a, &'new str, RandomState> {
fn symmetric_difference<'a, 'new>(
v: SymmetricDifference<'a, &'static str, RandomState>,
) -> SymmetricDifference<'a, &'new str, RandomState> {
v
}
fn intersection<'a, 'new>(v: Intersection<'a, &'static str, RandomState>)
-> Intersection<'a, &'new str, RandomState> {
fn intersection<'a, 'new>(
v: Intersection<'a, &'static str, RandomState>,
) -> Intersection<'a, &'new str, RandomState> {
v
}
fn union<'a, 'new>(v: Union<'a, &'static str, RandomState>)
-> Union<'a, &'new str, RandomState> {
fn union<'a, 'new>(
v: Union<'a, &'static str, RandomState>,
) -> Union<'a, &'new str, RandomState> {
v
}
fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> {

View file

@ -44,7 +44,10 @@ pub struct FailedAllocationError {
impl FailedAllocationError {
#[inline]
pub fn new(reason: &'static str) -> Self {
Self { reason, allocation_info: None }
Self {
reason,
allocation_info: None,
}
}
}
@ -57,9 +60,11 @@ impl error::Error for FailedAllocationError {
impl fmt::Display for FailedAllocationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.allocation_info {
Some(ref info) => {
write!(f, "{}, allocation: (size: {}, alignment: {})", self.reason, info.size, info.alignment)
},
Some(ref info) => write!(
f,
"{}, allocation: (size: {}, alignment: {})",
self.reason, info.size, info.alignment
),
None => self.reason.fmt(f),
}
}

View file

@ -29,11 +29,11 @@ impl<T: 'static> Unique<T> {
}
}
unsafe impl<T: Send + 'static> Send for Unique<T> { }
unsafe impl<T: Send + 'static> Send for Unique<T> {}
unsafe impl<T: Sync + 'static> Sync for Unique<T> { }
unsafe impl<T: Sync + 'static> Sync for Unique<T> {}
pub struct Shared<T: 'static> {
pub struct Shared<T: 'static> {
ptr: NonZeroPtr<T>,
_marker: PhantomData<T>,
// force it to be !Send/!Sync

View file

@ -203,7 +203,9 @@ impl SafeHash {
//
// Truncate hash to fit in `HashUint`.
let hash_bits = size_of::<HashUint>() * 8;
SafeHash { hash: (1 << (hash_bits - 1)) | (hash as HashUint) }
SafeHash {
hash: (1 << (hash_bits - 1)) | (hash as HashUint),
}
}
}
@ -211,8 +213,9 @@ impl SafeHash {
/// This function wraps up `hash_keyed` to be the only way outside this
/// module to generate a SafeHash.
pub fn make_hash<T: ?Sized, S>(hash_state: &S, t: &T) -> SafeHash
where T: Hash,
S: BuildHasher
where
T: Hash,
S: BuildHasher,
{
let mut state = hash_state.build_hasher();
t.hash(&mut state);
@ -294,7 +297,8 @@ impl<K, V, M> Bucket<K, V, M> {
}
impl<K, V, M> Deref for FullBucket<K, V, M>
where M: Deref<Target = RawTable<K, V>>
where
M: Deref<Target = RawTable<K, V>>,
{
type Target = RawTable<K, V>;
fn deref(&self) -> &RawTable<K, V> {
@ -308,7 +312,6 @@ pub trait Put<K, V> {
unsafe fn borrow_table_mut(&mut self) -> &mut RawTable<K, V>;
}
impl<'t, K, V> Put<K, V> for &'t mut RawTable<K, V> {
unsafe fn borrow_table_mut(&mut self) -> &mut RawTable<K, V> {
*self
@ -316,7 +319,8 @@ impl<'t, K, V> Put<K, V> for &'t mut RawTable<K, V> {
}
impl<K, V, M> Put<K, V> for Bucket<K, V, M>
where M: Put<K, V>
where
M: Put<K, V>,
{
unsafe fn borrow_table_mut(&mut self) -> &mut RawTable<K, V> {
self.table.borrow_table_mut()
@ -324,7 +328,8 @@ impl<K, V, M> Put<K, V> for Bucket<K, V, M>
}
impl<K, V, M> Put<K, V> for FullBucket<K, V, M>
where M: Put<K, V>
where
M: Put<K, V>,
{
unsafe fn borrow_table_mut(&mut self) -> &mut RawTable<K, V> {
self.table.borrow_table_mut()
@ -336,20 +341,17 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> Bucket<K, V, M> {
Bucket::at_index(table, hash.inspect() as usize)
}
pub fn new_from(r: RawBucket<K, V>, t: M)
-> Bucket<K, V, M>
{
Bucket {
raw: r,
table: t,
}
pub fn new_from(r: RawBucket<K, V>, t: M) -> Bucket<K, V, M> {
Bucket { raw: r, table: t }
}
pub fn at_index(table: M, ib_index: usize) -> Bucket<K, V, M> {
// if capacity is 0, then the RawBucket will be populated with bogus pointers.
// This is an uncommon case though, so avoid it in release builds.
debug_assert!(table.capacity() > 0,
"Table should have capacity at this point");
debug_assert!(
table.capacity() > 0,
"Table should have capacity at this point"
);
let ib_index = ib_index & table.capacity_mask;
Bucket {
raw: table.raw_bucket_at(ib_index),
@ -387,11 +389,11 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> Bucket<K, V, M> {
}
// Leaving this bucket in the last cluster for later.
full.into_bucket()
}
},
Empty(b) => {
// Encountered a hole between clusters.
b.into_bucket()
}
},
};
bucket.next();
}
@ -404,18 +406,14 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> Bucket<K, V, M> {
/// this module.
pub fn peek(self) -> BucketState<K, V, M> {
match unsafe { *self.raw.hash() } {
EMPTY_BUCKET => {
Empty(EmptyBucket {
raw: self.raw,
table: self.table,
})
}
_ => {
Full(FullBucket {
raw: self.raw,
table: self.table,
})
}
EMPTY_BUCKET => Empty(EmptyBucket {
raw: self.raw,
table: self.table,
}),
_ => Full(FullBucket {
raw: self.raw,
table: self.table,
}),
}
}
@ -453,19 +451,15 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> EmptyBucket<K, V, M> {
};
match self.next().peek() {
Full(bucket) => {
Ok(GapThenFull {
gap,
full: bucket,
})
}
Full(bucket) => Ok(GapThenFull { gap, full: bucket }),
Empty(e) => Err(e.into_bucket()),
}
}
}
impl<K, V, M> EmptyBucket<K, V, M>
where M: Put<K, V>
where
M: Put<K, V>,
{
/// Puts given key and value pair, along with the key's hash,
/// into this bucket in the hashtable. Note how `self` is 'moved' into
@ -528,7 +522,11 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> FullBucket<K, V, M> {
#[inline]
pub fn hash(&self) -> SafeHash {
unsafe { SafeHash { hash: *self.raw.hash() } }
unsafe {
SafeHash {
hash: *self.raw.hash(),
}
}
}
/// Gets references to the key and value at a given index.
@ -554,12 +552,14 @@ impl<'t, K, V> FullBucket<K, V, &'t mut RawTable<K, V>> {
unsafe {
*self.raw.hash() = EMPTY_BUCKET;
let (k, v) = ptr::read(self.raw.pair());
(EmptyBucket {
raw: self.raw,
table: self.table,
},
k,
v)
(
EmptyBucket {
raw: self.raw,
table: self.table,
},
k,
v,
)
}
}
}
@ -567,7 +567,8 @@ impl<'t, K, V> FullBucket<K, V, &'t mut RawTable<K, V>> {
// This use of `Put` is misleading and restrictive, but safe and sufficient for our use cases
// where `M` is a full bucket or table reference type with mutable access to the table.
impl<K, V, M> FullBucket<K, V, M>
where M: Put<K, V>
where
M: Put<K, V>,
{
pub fn replace(&mut self, h: SafeHash, k: K, v: V) -> (SafeHash, K, V) {
unsafe {
@ -580,7 +581,8 @@ impl<K, V, M> FullBucket<K, V, M>
}
impl<K, V, M> FullBucket<K, V, M>
where M: Deref<Target = RawTable<K, V>> + DerefMut
where
M: Deref<Target = RawTable<K, V>> + DerefMut,
{
/// Gets mutable references to the key and value at a given index.
pub fn read_mut(&mut self) -> (&mut K, &mut V) {
@ -592,7 +594,8 @@ impl<K, V, M> FullBucket<K, V, M>
}
impl<'t, K, V, M> FullBucket<K, V, M>
where M: Deref<Target = RawTable<K, V>> + 't
where
M: Deref<Target = RawTable<K, V>> + 't,
{
/// Exchange a bucket state for immutable references into the table.
/// Because the underlying reference to the table is also consumed,
@ -608,7 +611,8 @@ impl<'t, K, V, M> FullBucket<K, V, M>
}
impl<'t, K, V, M> FullBucket<K, V, M>
where M: Deref<Target = RawTable<K, V>> + DerefMut + 't
where
M: Deref<Target = RawTable<K, V>> + DerefMut + 't,
{
/// This works similarly to `into_refs`, exchanging a bucket state
/// for mutable references into the table.
@ -621,7 +625,8 @@ impl<'t, K, V, M> FullBucket<K, V, M>
}
impl<K, V, M> GapThenFull<K, V, M>
where M: Deref<Target = RawTable<K, V>>
where
M: Deref<Target = RawTable<K, V>>,
{
#[inline]
pub fn full(&self) -> &FullBucket<K, V, M> {
@ -649,13 +654,12 @@ impl<K, V, M> GapThenFull<K, V, M>
self.full = bucket;
Ok(self)
}
},
Empty(b) => Err(b.into_bucket()),
}
}
}
/// Rounds up to a multiple of a power of two. Returns the closest multiple
/// of `target_alignment` that is higher or equal to `unrounded`.
///
@ -681,10 +685,11 @@ fn test_rounding() {
// Returns a tuple of (pairs_offset, end_of_pairs_offset),
// from the start of a mallocated array.
#[inline]
fn calculate_offsets(hashes_size: usize,
pairs_size: usize,
pairs_align: usize)
-> (usize, usize, bool) {
fn calculate_offsets(
hashes_size: usize,
pairs_size: usize,
pairs_align: usize,
) -> (usize, usize, bool) {
let pairs_offset = round_up_to_next(hashes_size, pairs_align);
let (end_of_pairs, oflo) = pairs_offset.overflowing_add(pairs_size);
@ -693,11 +698,12 @@ fn calculate_offsets(hashes_size: usize,
// Returns a tuple of (minimum required malloc alignment, hash_offset,
// array_size), from the start of a mallocated array.
fn calculate_allocation(hash_size: usize,
hash_align: usize,
pairs_size: usize,
pairs_align: usize)
-> (usize, usize, usize, bool) {
fn calculate_allocation(
hash_size: usize,
hash_align: usize,
pairs_size: usize,
pairs_align: usize,
) -> (usize, usize, usize, bool) {
let hash_offset = 0;
let (_, end_of_pairs, oflo) = calculate_offsets(hash_size, pairs_size, pairs_align);
@ -728,7 +734,9 @@ impl<K, V> RawTable<K, V> {
/// Does not initialize the buckets. The caller should ensure they,
/// at the very least, set every hash to EMPTY_BUCKET.
unsafe fn try_new_uninitialized(capacity: usize) -> Result<RawTable<K, V>, FailedAllocationError> {
unsafe fn try_new_uninitialized(
capacity: usize,
) -> Result<RawTable<K, V>, FailedAllocationError> {
if capacity == 0 {
return Ok(RawTable {
size: 0,
@ -751,29 +759,38 @@ impl<K, V> RawTable<K, V> {
// This is great in theory, but in practice getting the alignment
// right is a little subtle. Therefore, calculating offsets has been
// factored out into a different function.
let (alignment, hash_offset, size, oflo) = calculate_allocation(hashes_size,
align_of::<HashUint>(),
pairs_size,
align_of::<(K, V)>());
let (alignment, hash_offset, size, oflo) = calculate_allocation(
hashes_size,
align_of::<HashUint>(),
pairs_size,
align_of::<(K, V)>(),
);
if oflo {
return Err(FailedAllocationError::new("capacity overflow when allocating RawTable" ));
return Err(FailedAllocationError::new(
"capacity overflow when allocating RawTable",
));
}
// One check for overflow that covers calculation and rounding of size.
let size_of_bucket = size_of::<HashUint>().checked_add(size_of::<(K, V)>()).unwrap();
let size_of_bucket = size_of::<HashUint>()
.checked_add(size_of::<(K, V)>())
.unwrap();
let cap_bytes = capacity.checked_mul(size_of_bucket);
if let Some(cap_bytes) = cap_bytes {
if size < cap_bytes {
return Err(FailedAllocationError::new("capacity overflow when allocating RawTable"));
return Err(FailedAllocationError::new(
"capacity overflow when allocating RawTable",
));
}
} else {
return Err(FailedAllocationError::new("capacity overflow when allocating RawTable"));
return Err(FailedAllocationError::new(
"capacity overflow when allocating RawTable",
));
}
// FORK NOTE: Uses alloc shim instead of Heap.alloc
let buffer = alloc(size, alignment);
@ -857,7 +874,9 @@ impl<K, V> RawTable<K, V> {
}
pub fn into_iter(self) -> IntoIter<K, V> {
let RawBuckets { raw, elems_left, .. } = self.raw_buckets();
let RawBuckets {
raw, elems_left, ..
} = self.raw_buckets();
// Replace the marker regardless of lifetime bounds on parameters.
IntoIter {
iter: RawBuckets {
@ -870,7 +889,9 @@ impl<K, V> RawTable<K, V> {
}
pub fn drain(&mut self) -> Drain<K, V> {
let RawBuckets { raw, elems_left, .. } = self.raw_buckets();
let RawBuckets {
raw, elems_left, ..
} = self.raw_buckets();
// Replace the marker regardless of lifetime bounds on parameters.
Drain {
iter: RawBuckets {
@ -937,7 +958,6 @@ impl<'a, K, V> Clone for RawBuckets<'a, K, V> {
}
}
impl<'a, K, V> Iterator for RawBuckets<'a, K, V> {
type Item = RawBucket<K, V>;
@ -1112,12 +1132,16 @@ impl<'a, K, V> Iterator for Drain<'a, K, V> {
#[inline]
fn next(&mut self) -> Option<(SafeHash, K, V)> {
self.iter.next().map(|raw| {
unsafe {
self.table.as_mut().size -= 1;
let (k, v) = ptr::read(raw.pair());
(SafeHash { hash: ptr::replace(&mut *raw.hash(), EMPTY_BUCKET) }, k, v)
}
self.iter.next().map(|raw| unsafe {
self.table.as_mut().size -= 1;
let (k, v) = ptr::read(raw.pair());
(
SafeHash {
hash: ptr::replace(&mut *raw.hash(), EMPTY_BUCKET),
},
k,
v,
)
})
}
@ -1181,17 +1205,19 @@ impl<K, V> Drop for RawTable<K, V> {
unsafe {
// FORK NOTE: Can't needs_drop on stable
// if needs_drop::<(K, V)>() {
// avoid linear runtime for types that don't need drop
self.rev_drop_buckets();
// avoid linear runtime for types that don't need drop
self.rev_drop_buckets();
// }
}
let hashes_size = self.capacity() * size_of::<HashUint>();
let pairs_size = self.capacity() * size_of::<(K, V)>();
let (align, _, _, oflo) = calculate_allocation(hashes_size,
align_of::<HashUint>(),
pairs_size,
align_of::<(K, V)>());
let (align, _, _, oflo) = calculate_allocation(
hashes_size,
align_of::<HashUint>(),
pairs_size,
align_of::<(K, V)>(),
);
debug_assert!(!oflo, "should be impossible");

View file

@ -3,15 +3,15 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate quote;
#[macro_use] extern crate syn;
#[macro_use] extern crate synstructure;
#[macro_use]
extern crate syn;
#[macro_use]
extern crate synstructure;
decl_derive!([JSTraceable] => js_traceable_derive);
fn js_traceable_derive(s: synstructure::Structure) -> quote::Tokens {
let match_body = s.each(|binding| {
Some(quote!(#binding.trace(tracer);))
});
let match_body = s.each(|binding| Some(quote!(#binding.trace(tracer);)));
let ast = s.ast();
let name = ast.ident;
@ -19,7 +19,9 @@ fn js_traceable_derive(s: synstructure::Structure) -> quote::Tokens {
let mut where_clause = where_clause.unwrap_or(&parse_quote!(where)).clone();
for param in ast.generics.type_params() {
let ident = param.ident;
where_clause.predicates.push(parse_quote!(#ident: ::dom::bindings::trace::JSTraceable))
where_clause
.predicates
.push(parse_quote!(#ident: ::dom::bindings::trace::JSTraceable))
}
let tokens = quote! {

View file

@ -32,8 +32,7 @@ pub fn update_animation_state<E>(
new_animations_receiver: &Receiver<Animation>,
pipeline_id: PipelineId,
timer: &Timer,
)
where
) where
E: TElement,
{
let mut new_running_animations = vec![];
@ -66,7 +65,7 @@ where
if running_animations.is_empty() && new_running_animations.is_empty() {
// Nothing to do. Return early so we don't flood the compositor with
// `ChangeRunningAnimationsState` messages.
return
return;
}
let now = timer.seconds();
@ -82,30 +81,32 @@ where
let still_running = !running_animation.is_expired() && match running_animation {
Animation::Transition(_, started_at, ref frame, _expired) => {
now < started_at + frame.duration
}
},
Animation::Keyframes(_, _, _, ref mut state) => {
// This animation is still running, or we need to keep
// iterating.
now < state.started_at + state.duration || state.tick()
}
},
};
if still_running {
animations_still_running.push(running_animation);
continue
continue;
}
if let Animation::Transition(node, _, ref frame, _) = running_animation {
script_chan.send(ConstellationControlMsg::TransitionEnd(node.to_untrusted_node_address(),
frame.property_animation
.property_name().into(),
frame.duration))
.unwrap();
script_chan
.send(ConstellationControlMsg::TransitionEnd(
node.to_untrusted_node_address(),
frame.property_animation.property_name().into(),
frame.duration,
)).unwrap();
}
expired_animations.entry(*key)
.or_insert_with(Vec::new)
.push(running_animation);
expired_animations
.entry(*key)
.or_insert_with(Vec::new)
.push(running_animation);
}
if animations_still_running.is_empty() {
@ -125,16 +126,17 @@ where
match newly_transitioning_nodes {
Some(ref mut nodes) => {
nodes.push(new_running_animation.node().to_untrusted_node_address());
}
},
None => {
warn!("New transition encountered from compositor-initiated layout.");
}
},
}
}
running_animations.entry(*new_running_animation.node())
.or_insert_with(Vec::new)
.push(new_running_animation)
running_animations
.entry(*new_running_animation.node())
.or_insert_with(Vec::new)
.push(new_running_animation)
}
let animation_state = if running_animations.is_empty() {
@ -143,9 +145,11 @@ where
AnimationState::AnimationsPresent
};
constellation_chan.send(ConstellationMsg::ChangeRunningAnimationsState(pipeline_id,
animation_state))
.unwrap();
constellation_chan
.send(ConstellationMsg::ChangeRunningAnimationsState(
pipeline_id,
animation_state,
)).unwrap();
}
/// Recalculates style for a set of animations. This does *not* run with the DOM
@ -154,8 +158,7 @@ pub fn recalc_style_for_animations<E>(
context: &LayoutContext,
flow: &mut Flow,
animations: &FxHashMap<OpaqueNode, Vec<Animation>>,
)
where
) where
E: TElement,
{
let mut damage = RestyleDamage::empty();
@ -170,10 +173,7 @@ where
&ServoMetricsProvider,
);
let difference =
RestyleDamage::compute_style_difference(
&old_style,
&fragment.style,
);
RestyleDamage::compute_style_difference(&old_style, &fragment.style);
damage |= difference.damage;
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -32,7 +32,8 @@ pub type LayoutFontContext = FontContext<FontCacheThread>;
thread_local!(static FONT_CONTEXT_KEY: RefCell<Option<LayoutFontContext>> = RefCell::new(None));
pub fn with_thread_local_font_context<F, R>(layout_context: &LayoutContext, f: F) -> R
where F: FnOnce(&mut LayoutFontContext) -> R
where
F: FnOnce(&mut LayoutFontContext) -> R,
{
FONT_CONTEXT_KEY.with(|k| {
let mut font_context = k.borrow_mut();
@ -69,9 +70,11 @@ pub struct LayoutContext<'a> {
pub font_cache_thread: Mutex<FontCacheThread>,
/// A cache of WebRender image info.
pub webrender_image_cache: Arc<RwLock<HashMap<(ServoUrl, UsePlaceholder),
WebRenderImageInfo,
BuildHasherDefault<FnvHasher>>>>,
pub webrender_image_cache: Arc<
RwLock<
HashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo, BuildHasherDefault<FnvHasher>>,
>,
>,
/// Paint worklets
pub registered_painters: &'a RegisteredPainters,
@ -101,11 +104,12 @@ impl<'a> LayoutContext<'a> {
&self.style_context
}
pub fn get_or_request_image_or_meta(&self,
node: OpaqueNode,
url: ServoUrl,
use_placeholder: UsePlaceholder)
-> Option<ImageOrMetadataAvailable> {
pub fn get_or_request_image_or_meta(
&self,
node: OpaqueNode,
url: ServoUrl,
use_placeholder: UsePlaceholder,
) -> Option<ImageOrMetadataAvailable> {
//XXXjdm For cases where we do not request an image, we still need to
// ensure the node gets another script-initiated reflow or it
// won't be requested at all.
@ -116,9 +120,9 @@ impl<'a> LayoutContext<'a> {
};
// See if the image is already available
let result = self.image_cache.find_image_or_metadata(url.clone(),
use_placeholder,
can_request);
let result =
self.image_cache
.find_image_or_metadata(url.clone(), use_placeholder, can_request);
match result {
Ok(image_or_metadata) => Some(image_or_metadata),
// Image failed to load, so just return nothing
@ -130,9 +134,14 @@ impl<'a> LayoutContext<'a> {
node: node.to_untrusted_node_address(),
id: id,
};
self.pending_images.as_ref().unwrap().lock().unwrap().push(image);
self.pending_images
.as_ref()
.unwrap()
.lock()
.unwrap()
.push(image);
None
}
},
// Image has been requested, is still pending. Return no image for this paint loop.
// When the image loads it will trigger a reflow and/or repaint.
Err(ImageState::Pending(id)) => {
@ -148,19 +157,22 @@ impl<'a> LayoutContext<'a> {
pending_images.lock().unwrap().push(image);
}
None
}
},
}
}
pub fn get_webrender_image_for_url(&self,
node: OpaqueNode,
url: ServoUrl,
use_placeholder: UsePlaceholder)
-> Option<WebRenderImageInfo> {
if let Some(existing_webrender_image) = self.webrender_image_cache
.read()
.get(&(url.clone(), use_placeholder)) {
return Some((*existing_webrender_image).clone())
pub fn get_webrender_image_for_url(
&self,
node: OpaqueNode,
url: ServoUrl,
use_placeholder: UsePlaceholder,
) -> Option<WebRenderImageInfo> {
if let Some(existing_webrender_image) = self
.webrender_image_cache
.read()
.get(&(url.clone(), use_placeholder))
{
return Some((*existing_webrender_image).clone());
}
match self.get_or_request_image_or_meta(node, url.clone(), use_placeholder) {
@ -170,11 +182,10 @@ impl<'a> LayoutContext<'a> {
Some(image_info)
} else {
let mut webrender_image_cache = self.webrender_image_cache.write();
webrender_image_cache.insert((url, use_placeholder),
image_info);
webrender_image_cache.insert((url, use_placeholder), image_info);
Some(image_info)
}
}
},
None | Some(ImageOrMetadataAvailable::MetadataAvailable(_)) => None,
}
}

View file

@ -24,7 +24,6 @@ impl StyleAndLayoutData {
}
}
/// Data that layout associates with a node.
#[repr(C)]
pub struct LayoutData {

View file

@ -444,8 +444,7 @@ fn convert_gradient_stops(
.filter_map(|item| match *item {
GenericGradientItem::ColorStop(ref stop) => Some(*stop),
_ => None,
})
.collect::<Vec<_>>();
}).collect::<Vec<_>>();
assert!(stop_items.len() >= 2);

View file

@ -1414,8 +1414,7 @@ impl FragmentDisplayListBuilding for Fragment {
url.clone(),
UsePlaceholder::No,
)
})
.and_then(|image| {
}).and_then(|image| {
build_image_border_details(image, border_style_struct, outset_layout)
}),
};
@ -1957,8 +1956,7 @@ impl FragmentDisplayListBuilding for Fragment {
.send(CanvasMsg::FromLayout(
FromLayoutMsg::SendData(sender),
canvas_fragment_info.canvas_id.clone(),
))
.unwrap();
)).unwrap();
receiver.recv().unwrap().image_key
},
None => return,
@ -2070,10 +2068,12 @@ impl FragmentDisplayListBuilding for Fragment {
// FIXME(pcwalton): Get the real container size.
let container_size = Size2D::zero();
let metrics = &text_fragment.run.font_metrics;
let baseline_origin = stacking_relative_content_box.origin +
LogicalPoint::new(self.style.writing_mode, Au(0), metrics.ascent)
.to_physical(self.style.writing_mode, container_size)
.to_vector();
let baseline_origin = stacking_relative_content_box.origin + LogicalPoint::new(
self.style.writing_mode,
Au(0),
metrics.ascent,
).to_physical(self.style.writing_mode, container_size)
.to_vector();
// Base item for all text/shadows
let base = state.create_base_display_item(

View file

@ -513,27 +513,29 @@ impl ClippingRegion {
/// This is a quick, not a precise, test; it can yield false positives.
#[inline]
pub fn might_intersect_point(&self, point: &LayoutPoint) -> bool {
self.main.contains(point) &&
self.complex
.iter()
.all(|complex| complex.rect.contains(point))
self.main.contains(point) && self
.complex
.iter()
.all(|complex| complex.rect.contains(point))
}
/// Returns true if this clipping region might intersect the given rectangle and false
/// otherwise. This is a quick, not a precise, test; it can yield false positives.
#[inline]
pub fn might_intersect_rect(&self, rect: &LayoutRect) -> bool {
self.main.intersects(rect) &&
self.complex
.iter()
.all(|complex| complex.rect.intersects(rect))
self.main.intersects(rect) && self
.complex
.iter()
.all(|complex| complex.rect.intersects(rect))
}
/// Returns true if this clipping region completely surrounds the given rect.
#[inline]
pub fn does_not_clip_rect(&self, rect: &LayoutRect) -> bool {
self.main.contains(&rect.origin) && self.main.contains(&rect.bottom_right()) &&
self.complex.iter().all(|complex| {
self.main.contains(&rect.origin) && self.main.contains(&rect.bottom_right()) && self
.complex
.iter()
.all(|complex| {
complex.rect.contains(&rect.origin) && complex.rect.contains(&rect.bottom_right())
})
}
@ -588,8 +590,7 @@ impl ClippingRegion {
rect: complex.rect.translate(delta),
radii: complex.radii,
mode: complex.mode,
})
.collect(),
}).collect(),
}
}

View file

@ -45,25 +45,25 @@ enum AxisSize {
impl AxisSize {
/// Generate a new available cross or main axis size from the specified size of the container,
/// containing block size, min constraint, and max constraint
pub fn new(size: LengthOrPercentageOrAuto, content_size: Option<Au>, min: LengthOrPercentage,
max: LengthOrPercentageOrNone) -> AxisSize {
pub fn new(
size: LengthOrPercentageOrAuto,
content_size: Option<Au>,
min: LengthOrPercentage,
max: LengthOrPercentageOrNone,
) -> AxisSize {
match size {
LengthOrPercentageOrAuto::Length(length) => AxisSize::Definite(Au::from(length)),
LengthOrPercentageOrAuto::Percentage(percent) => {
match content_size {
Some(size) => AxisSize::Definite(size.scale_by(percent.0)),
None => AxisSize::Infinite
}
}
LengthOrPercentageOrAuto::Calc(calc) => {
match calc.to_used_value(content_size) {
Some(length) => AxisSize::Definite(length),
None => AxisSize::Infinite,
}
}
LengthOrPercentageOrAuto::Percentage(percent) => match content_size {
Some(size) => AxisSize::Definite(size.scale_by(percent.0)),
None => AxisSize::Infinite,
},
LengthOrPercentageOrAuto::Calc(calc) => match calc.to_used_value(content_size) {
Some(length) => AxisSize::Definite(length),
None => AxisSize::Infinite,
},
LengthOrPercentageOrAuto::Auto => {
AxisSize::MinMax(SizeConstraint::new(content_size, min, max, None))
}
},
}
}
}
@ -112,7 +112,7 @@ struct FlexItem {
/// Whether the main size has met its constraint.
pub is_frozen: bool,
/// True if this flow has property 'visibility::collapse'.
pub is_strut: bool
pub is_strut: bool,
}
impl FlexItem {
@ -133,7 +133,7 @@ impl FlexItem {
flex_shrink: flex_shrink.into(),
order: order,
is_frozen: false,
is_strut: false
is_strut: false,
}
}
@ -147,41 +147,61 @@ impl FlexItem {
// should change to LengthOrPercentageOrAuto for automatic implied minimal size.
// https://drafts.csswg.org/css-flexbox-1/#min-size-auto
Direction::Inline => {
let basis = from_flex_basis(block.fragment.style.get_position().flex_basis,
block.fragment.style.content_inline_size(),
containing_length);
let basis = from_flex_basis(
block.fragment.style.get_position().flex_basis,
block.fragment.style.content_inline_size(),
containing_length,
);
// These methods compute auto margins to zero length, which is exactly what we want.
block.fragment.compute_border_and_padding(containing_length);
block.fragment.compute_inline_direction_margins(containing_length);
block.fragment.compute_block_direction_margins(containing_length);
block
.fragment
.compute_inline_direction_margins(containing_length);
block
.fragment
.compute_block_direction_margins(containing_length);
let (border_padding, margin) = block.fragment.surrounding_intrinsic_inline_size();
let content_size = block.base.intrinsic_inline_sizes.preferred_inline_size
- border_padding
- margin
+ block.fragment.box_sizing_boundary(direction);
let content_size = block.base.intrinsic_inline_sizes.preferred_inline_size -
border_padding -
margin +
block.fragment.box_sizing_boundary(direction);
self.base_size = basis.specified_or_default(content_size);
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);
}
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,
block.fragment.style.content_block_size(),
containing_length);
let content_size = block.fragment.border_box.size.block
- block.fragment.border_padding.block_start_end()
+ block.fragment.box_sizing_boundary(direction);
let basis = from_flex_basis(
block.fragment.style.get_position().flex_basis,
block.fragment.style.content_block_size(),
containing_length,
);
let content_size = block.fragment.border_box.size.block -
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 =
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);
}
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);
},
}
}
@ -192,13 +212,14 @@ impl FlexItem {
let outer_width = match direction {
Direction::Inline => {
fragment.border_padding.inline_start_end() + fragment.margin.inline_start_end()
}
},
Direction::Block => {
fragment.border_padding.block_start_end() + fragment.margin.block_start_end()
}
},
};
max(self.min_size, min(self.base_size, self.max_size))
- fragment.box_sizing_boundary(direction) + outer_width
max(self.min_size, min(self.base_size, self.max_size)) -
fragment.box_sizing_boundary(direction) +
outer_width
}
/// Returns the number of auto margins in given direction.
@ -213,7 +234,7 @@ impl FlexItem {
if margin.inline_end == LengthOrPercentageOrAuto::Auto {
margin_count += 1;
}
}
},
Direction::Block => {
if margin.block_start == LengthOrPercentageOrAuto::Auto {
margin_count += 1;
@ -221,7 +242,7 @@ impl FlexItem {
if margin.block_end == LengthOrPercentageOrAuto::Auto {
margin_count += 1;
}
}
},
}
margin_count
}
@ -247,7 +268,7 @@ impl FlexLine {
range: range,
auto_margin_count: auto_margin_count,
free_space: free_space,
cross_size: Au(0)
cross_size: Au(0),
}
}
@ -265,17 +286,20 @@ impl FlexLine {
// https://drafts.csswg.org/css-flexbox/#resolve-flexible-lengths
for item in items.iter_mut().filter(|i| !(i.is_strut && collapse)) {
item.main_size = max(item.min_size, min(item.base_size, item.max_size));
if (self.free_space > Au(0) && (item.flex_grow == 0.0 || item.base_size >= item.max_size)) ||
(self.free_space < Au(0) && (item.flex_shrink == 0.0 || item.base_size <= item.min_size)) {
item.is_frozen = true;
} else {
item.is_frozen = false;
total_grow += item.flex_grow;
total_shrink += item.flex_shrink;
// The scaled factor is used to calculate flex shrink
total_scaled += item.flex_shrink * item.base_size.0 as f32;
active_count += 1;
}
if (self.free_space > Au(0) &&
(item.flex_grow == 0.0 || item.base_size >= item.max_size)) ||
(self.free_space < Au(0) &&
(item.flex_shrink == 0.0 || item.base_size <= item.min_size))
{
item.is_frozen = true;
} else {
item.is_frozen = false;
total_grow += item.flex_grow;
total_shrink += item.flex_shrink;
// The scaled factor is used to calculate flex shrink
total_scaled += item.flex_shrink * item.base_size.0 as f32;
active_count += 1;
}
}
let initial_free_space = self.free_space;
@ -291,12 +315,19 @@ impl FlexLine {
};
total_variation = Au(0);
for item in items.iter_mut().filter(|i| !i.is_frozen).filter(|i| !(i.is_strut && collapse)) {
for item in items
.iter_mut()
.filter(|i| !i.is_frozen)
.filter(|i| !(i.is_strut && collapse))
{
// Use this and the 'abs()' below to make the code work in both grow and shrink scenarios.
let (factor, end_size) = if self.free_space > Au(0) {
(item.flex_grow / total_grow, item.max_size)
} else {
(item.flex_shrink * item.base_size.0 as f32 / total_scaled, item.min_size)
(
item.flex_shrink * item.base_size.0 as f32 / total_scaled,
item.min_size,
)
};
let variation = self.free_space.scale_by(factor);
if variation.0.abs() >= (end_size - item.main_size).0.abs() {
@ -343,13 +374,11 @@ pub struct FlexFlow {
/// True if this flex container can be multiline.
is_wrappable: bool,
/// True if the cross direction is reversed.
cross_reverse: bool
cross_reverse: bool,
}
impl FlexFlow {
pub fn from_fragment(fragment: Fragment,
flotation: Option<FloatKind>)
-> FlexFlow {
pub fn from_fragment(fragment: Fragment, flotation: Option<FloatKind>) -> FlexFlow {
let main_mode;
let main_reverse;
let is_wrappable;
@ -363,8 +392,7 @@ impl FlexFlow {
FlexDirection::ColumnReverse => (Direction::Block, true),
};
main_mode = mode;
main_reverse =
reverse == style.writing_mode.is_bidi_ltr();
main_reverse = reverse == style.writing_mode.is_bidi_ltr();
let (wrappable, reverse) = match fragment.style.get_position().flex_wrap {
FlexWrap::Nowrap => (false, false),
FlexWrap::Wrap => (true, false),
@ -384,7 +412,7 @@ impl FlexFlow {
items: Vec::new(),
main_reverse: main_reverse,
is_wrappable: is_wrappable,
cross_reverse: cross_reverse
cross_reverse: cross_reverse,
}
}
@ -414,7 +442,10 @@ impl FlexFlow {
let kid = children.get(item.index);
item.init_sizes(kid, container_size, self.main_mode);
let outer_main_size = item.outer_main_size(kid, self.main_mode);
if total_line_size + outer_main_size > container_size && end != start && self.is_wrappable {
if total_line_size + outer_main_size > container_size &&
end != start &&
self.is_wrappable
{
break;
}
margin_count += item.auto_margin_count(kid, self.main_mode);
@ -439,7 +470,8 @@ impl FlexFlow {
if !fixed_width {
for kid in self.block_flow.base.children.iter_mut() {
let base = kid.mut_base();
let is_absolutely_positioned = base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
let is_absolutely_positioned =
base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
if !is_absolutely_positioned {
let flex_item_inline_sizes = IntrinsicISizes {
minimum_inline_size: base.intrinsic_inline_sizes.minimum_inline_size,
@ -465,15 +497,18 @@ impl FlexFlow {
if !fixed_width {
for kid in self.block_flow.base.children.iter_mut() {
let base = kid.mut_base();
let is_absolutely_positioned = base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
let is_absolutely_positioned =
base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
if !is_absolutely_positioned {
computation.content_intrinsic_sizes.minimum_inline_size =
max(computation.content_intrinsic_sizes.minimum_inline_size,
base.intrinsic_inline_sizes.minimum_inline_size);
computation.content_intrinsic_sizes.minimum_inline_size = max(
computation.content_intrinsic_sizes.minimum_inline_size,
base.intrinsic_inline_sizes.minimum_inline_size,
);
computation.content_intrinsic_sizes.preferred_inline_size =
max(computation.content_intrinsic_sizes.preferred_inline_size,
base.intrinsic_inline_sizes.preferred_inline_size);
computation.content_intrinsic_sizes.preferred_inline_size = max(
computation.content_intrinsic_sizes.preferred_inline_size,
base.intrinsic_inline_sizes.preferred_inline_size,
);
}
}
}
@ -483,11 +518,13 @@ impl FlexFlow {
// TODO(zentner): This function needs to be radically different for multi-line flexbox.
// Currently, this is the core of BlockFlow::propagate_assigned_inline_size_to_children() with
// all float and table logic stripped out.
fn block_mode_assign_inline_sizes(&mut self,
_layout_context: &LayoutContext,
inline_start_content_edge: Au,
inline_end_content_edge: Au,
content_inline_size: Au) {
fn block_mode_assign_inline_sizes(
&mut self,
_layout_context: &LayoutContext,
inline_start_content_edge: Au,
inline_end_content_edge: Au,
content_inline_size: Au,
) {
let _scope = layout_debug_scope!("flex::block_mode_assign_inline_sizes");
debug!("flex::block_mode_assign_inline_sizes");
@ -496,19 +533,22 @@ impl FlexFlow {
let container_block_size = match self.available_main_size {
AxisSize::Definite(length) => Some(length),
_ => None
_ => None,
};
let container_inline_size = match self.available_cross_size {
AxisSize::Definite(length) => length,
AxisSize::MinMax(ref constraint) => constraint.clamp(content_inline_size),
AxisSize::Infinite => content_inline_size
AxisSize::Infinite => content_inline_size,
};
let mut children = self.block_flow.base.children.random_access_mut();
for kid in &mut self.items {
let kid_base = children.get(kid.index).mut_base();
kid_base.block_container_explicit_block_size = container_block_size;
if kid_base.flags.contains(FlowFlags::INLINE_POSITION_IS_STATIC) {
if kid_base
.flags
.contains(FlowFlags::INLINE_POSITION_IS_STATIC)
{
// The inline-start margin edge of the child flow is at our inline-start content
// edge, and its inline-size is our content inline-size.
kid_base.position.start.i =
@ -525,11 +565,13 @@ impl FlexFlow {
}
}
fn inline_mode_assign_inline_sizes(&mut self,
layout_context: &LayoutContext,
inline_start_content_edge: Au,
_inline_end_content_edge: Au,
content_inline_size: Au) {
fn inline_mode_assign_inline_sizes(
&mut self,
layout_context: &LayoutContext,
inline_start_content_edge: Au,
_inline_end_content_edge: Au,
content_inline_size: Au,
) {
let _scope = layout_debug_scope!("flex::inline_mode_assign_inline_sizes");
debug!("inline_mode_assign_inline_sizes");
@ -551,17 +593,25 @@ impl FlexFlow {
self.block_flow.base.position.size.inline = inline_size;
// Calculate non-auto block size to pass to children.
let box_border = self.block_flow.fragment.box_sizing_boundary(Direction::Block);
let box_border = self
.block_flow
.fragment
.box_sizing_boundary(Direction::Block);
let parent_container_size =
self.block_flow.explicit_block_containing_size(layout_context.shared_context());
let parent_container_size = self
.block_flow
.explicit_block_containing_size(layout_context.shared_context());
// https://drafts.csswg.org/css-ui-3/#box-sizing
let explicit_content_size = self
.block_flow
.explicit_block_size(parent_container_size)
.map(|x| max(x - box_border, Au(0)));
let containing_block_text_align =
self.block_flow.fragment.style().get_inherited_text().text_align;
.block_flow
.explicit_block_size(parent_container_size)
.map(|x| max(x - box_border, Au(0)));
let containing_block_text_align = self
.block_flow
.fragment
.style()
.get_inherited_text()
.text_align;
while let Some(mut line) = self.get_flex_line(inline_size) {
let items = &mut self.items[line.range.clone()];
@ -572,32 +622,42 @@ impl FlexFlow {
let item_count = items.len() as i32;
let mut cur_i = inline_start_content_edge;
let item_interval = if line.free_space >= Au(0) && line.auto_margin_count == 0 {
match self.block_flow.fragment.style().get_position().justify_content {
match self
.block_flow
.fragment
.style()
.get_position()
.justify_content
{
JustifyContent::SpaceBetween => {
if item_count == 1 {
Au(0)
} else {
line.free_space / (item_count - 1)
}
}
JustifyContent::SpaceAround => {
line.free_space / item_count
}
},
JustifyContent::SpaceAround => line.free_space / item_count,
_ => Au(0),
}
} else {
Au(0)
};
match self.block_flow.fragment.style().get_position().justify_content {
match self
.block_flow
.fragment
.style()
.get_position()
.justify_content
{
// Overflow equally in both ends of line.
JustifyContent::Center | JustifyContent::SpaceAround => {
cur_i += (line.free_space - item_interval * (item_count - 1)) / 2;
}
},
JustifyContent::FlexEnd => {
cur_i += line.free_space;
}
_ => {}
},
_ => {},
}
let mut children = self.block_flow.base.children.random_access_mut();
@ -613,19 +673,18 @@ impl FlexFlow {
block.base.flags.set_text_align(containing_block_text_align);
let margin = block.fragment.style().logical_margin();
let auto_len =
if line.auto_margin_count == 0 || line.free_space <= Au(0) {
Au(0)
} else {
line.free_space / line.auto_margin_count
};
let auto_len = if line.auto_margin_count == 0 || line.free_space <= Au(0) {
Au(0)
} else {
line.free_space / line.auto_margin_count
};
let margin_inline_start = MaybeAuto::from_style(margin.inline_start, inline_size)
.specified_or_default(auto_len);
let margin_inline_end = MaybeAuto::from_style(margin.inline_end, inline_size)
.specified_or_default(auto_len);
let item_inline_size = item.main_size
- block.fragment.box_sizing_boundary(self.main_mode)
+ block.fragment.border_padding.inline_start_end();
let item_inline_size = item.main_size -
block.fragment.box_sizing_boundary(self.main_mode) +
block.fragment.border_padding.inline_start_end();
let item_outer_size = item_inline_size + block.fragment.margin.inline_start_end();
block.fragment.margin.inline_start = margin_inline_start;
@ -635,7 +694,7 @@ impl FlexFlow {
block.base.position.start.i = if !self.main_reverse {
cur_i
} else {
inline_start_content_edge * 2 + content_inline_size - cur_i - item_outer_size
inline_start_content_edge * 2 + content_inline_size - cur_i - item_outer_size
};
block.base.position.size.inline = item_outer_size;
cur_i += item_outer_size + item_interval;
@ -669,7 +728,12 @@ impl FlexFlow {
let _scope = layout_debug_scope!("flex::inline_mode_assign_block_size");
let line_count = self.lines.len() as i32;
let line_align = self.block_flow.fragment.style().get_position().align_content;
let line_align = self
.block_flow
.fragment
.style()
.get_position()
.align_content;
let mut cur_b = self.block_flow.fragment.border_padding.block_start;
let mut total_cross_size = Au(0);
let mut line_interval = Au(0);
@ -679,22 +743,27 @@ impl FlexFlow {
for line in self.lines.iter_mut() {
for item in &self.items[line.range.clone()] {
let fragment = &children.get(item.index).as_block().fragment;
line.cross_size = max(line.cross_size,
fragment.border_box.size.block +
fragment.margin.block_start_end());
line.cross_size = max(
line.cross_size,
fragment.border_box.size.block + fragment.margin.block_start_end(),
);
}
total_cross_size += line.cross_size;
}
}
let box_border = self.block_flow.fragment.box_sizing_boundary(Direction::Block);
let parent_container_size =
self.block_flow.explicit_block_containing_size(layout_context.shared_context());
let box_border = self
.block_flow
.fragment
.box_sizing_boundary(Direction::Block);
let parent_container_size = self
.block_flow
.explicit_block_containing_size(layout_context.shared_context());
// https://drafts.csswg.org/css-ui-3/#box-sizing
let explicit_content_size = self
.block_flow
.explicit_block_size(parent_container_size)
.map(|x| max(x - box_border, Au(0)));
.block_flow
.explicit_block_size(parent_container_size)
.map(|x| max(x - box_border, Au(0)));
if let Some(container_block_size) = explicit_content_size {
let free_space = container_block_size - total_cross_size;
@ -713,25 +782,25 @@ impl FlexFlow {
} else {
free_space / (line_count - 1)
}
}
},
AlignContent::SpaceAround => {
if line_count == 0 {
Au(0)
} else {
free_space / line_count
}
}
},
_ => Au(0),
};
match line_align {
AlignContent::Center | AlignContent::SpaceAround => {
cur_b += (free_space - line_interval * (line_count - 1)) / 2;
}
},
AlignContent::FlexEnd => {
cur_b += free_space;
}
_ => {}
},
_ => {},
}
}
@ -744,8 +813,9 @@ impl FlexFlow {
let mut margin_block_start = block.fragment.margin.block_start;
let mut margin_block_end = block.fragment.margin.block_end;
let mut free_space = line.cross_size - block.base.position.size.block
- block.fragment.margin.block_start_end();
let mut free_space = line.cross_size -
block.base.position.size.block -
block.fragment.margin.block_start_end();
// The spec is a little vague here, but if I understand it correctly, the outer
// cross size of item should equal to the line size if any auto margin exists.
@ -758,29 +828,31 @@ impl FlexFlow {
free_space / auto_margin_count
};
}
margin_block_end = line.cross_size - margin_block_start - block.base.position.size.block;
margin_block_end =
line.cross_size - margin_block_start - block.base.position.size.block;
free_space = Au(0);
}
let self_align = block.fragment.style().get_position().align_self;
if self_align == AlignSelf::Stretch &&
block.fragment.style().content_block_size() == LengthOrPercentageOrAuto::Auto {
free_space = Au(0);
block.base.block_container_explicit_block_size = Some(line.cross_size);
block.base.position.size.block =
line.cross_size - margin_block_start - margin_block_end;
block.fragment.border_box.size.block = block.base.position.size.block;
// FIXME(stshine): item with 'align-self: stretch' and auto cross size should act
// as if it has a fixed cross size, all child blocks should resolve against it.
// block.assign_block_size(layout_context);
}
block.base.position.start.b = margin_block_start +
if !self.cross_reverse {
cur_b
} else {
self.block_flow.fragment.border_padding.block_start * 2
+ total_cross_size - cur_b - line.cross_size
};
block.fragment.style().content_block_size() == LengthOrPercentageOrAuto::Auto
{
free_space = Au(0);
block.base.block_container_explicit_block_size = Some(line.cross_size);
block.base.position.size.block =
line.cross_size - margin_block_start - margin_block_end;
block.fragment.border_box.size.block = block.base.position.size.block;
// FIXME(stshine): item with 'align-self: stretch' and auto cross size should act
// as if it has a fixed cross size, all child blocks should resolve against it.
// block.assign_block_size(layout_context);
}
block.base.position.start.b = margin_block_start + if !self.cross_reverse {
cur_b
} else {
self.block_flow.fragment.border_padding.block_start * 2 + total_cross_size -
cur_b -
line.cross_size
};
// TODO(stshine): support baseline alignment.
if free_space != Au(0) {
let flex_cross = match self_align {
@ -788,17 +860,17 @@ impl FlexFlow {
AlignSelf::Center => free_space / 2,
_ => Au(0),
};
block.base.position.start.b +=
if !self.cross_reverse {
flex_cross
} else {
free_space - flex_cross
};
block.base.position.start.b += if !self.cross_reverse {
flex_cross
} else {
free_space - flex_cross
};
}
}
cur_b += line_interval + line.cross_size;
}
let total_block_size = total_cross_size + self.block_flow.fragment.border_padding.block_start_end();
let total_block_size =
total_cross_size + self.block_flow.fragment.border_padding.block_start_end();
self.block_flow.fragment.border_box.size.block = total_block_size;
self.block_flow.base.position.size.block = total_block_size;
}
@ -830,72 +902,96 @@ impl Flow for FlexFlow {
}
fn bubble_inline_sizes(&mut self) {
let _scope = layout_debug_scope!("flex::bubble_inline_sizes {:x}",
self.block_flow.base.debug_id());
let _scope = layout_debug_scope!(
"flex::bubble_inline_sizes {:x}",
self.block_flow.base.debug_id()
);
// Flexbox Section 9.0: Generate anonymous flex items:
// This part was handled in the flow constructor.
// Flexbox Section 9.1: Re-order flex items according to their order.
// FIXME(stshine): This should be done during flow construction.
let mut items: Vec<FlexItem> =
self.block_flow
.base
.children
.iter()
.enumerate()
.filter(|&(_, flow)| {
!flow.as_block().base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
})
.map(|(index, flow)| FlexItem::new(index, flow))
.collect();
let mut items: Vec<FlexItem> = self
.block_flow
.base
.children
.iter()
.enumerate()
.filter(|&(_, flow)| {
!flow
.as_block()
.base
.flags
.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
}).map(|(index, flow)| FlexItem::new(index, flow))
.collect();
items.sort_by_key(|item| item.order);
self.items = items;
match self.main_mode {
Direction::Inline => self.inline_mode_bubble_inline_sizes(),
Direction::Block => self.block_mode_bubble_inline_sizes()
Direction::Block => self.block_mode_bubble_inline_sizes(),
}
}
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
let _scope = layout_debug_scope!("flex::assign_inline_sizes {:x}", self.block_flow.base.debug_id());
let _scope = layout_debug_scope!(
"flex::assign_inline_sizes {:x}",
self.block_flow.base.debug_id()
);
debug!("assign_inline_sizes");
if !self.block_flow.base.restyle_damage.intersects(ServoRestyleDamage::REFLOW_OUT_OF_FLOW |
ServoRestyleDamage::REFLOW) {
return
if !self
.block_flow
.base
.restyle_damage
.intersects(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW)
{
return;
}
self.block_flow.initialize_container_size_for_root(layout_context.shared_context());
self.block_flow
.initialize_container_size_for_root(layout_context.shared_context());
// Our inline-size was set to the inline-size of the containing block by the flow's parent.
// Now compute the real value.
let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
self.block_flow.compute_used_inline_size(layout_context.shared_context(),
containing_block_inline_size);
self.block_flow.compute_used_inline_size(
layout_context.shared_context(),
containing_block_inline_size,
);
if self.block_flow.base.flags.is_float() {
self.block_flow.float.as_mut().unwrap().containing_inline_size = containing_block_inline_size
self.block_flow
.float
.as_mut()
.unwrap()
.containing_inline_size = containing_block_inline_size
}
let (available_block_size, available_inline_size) = {
let style = &self.block_flow.fragment.style;
let (specified_block_size, specified_inline_size) = if style.writing_mode.is_vertical() {
let (specified_block_size, specified_inline_size) = if style.writing_mode.is_vertical()
{
(style.get_position().width, style.get_position().height)
} else {
(style.get_position().height, style.get_position().width)
};
let available_inline_size = AxisSize::new(specified_inline_size,
Some(self.block_flow.base.block_container_inline_size),
style.min_inline_size(),
style.max_inline_size());
let available_inline_size = AxisSize::new(
specified_inline_size,
Some(self.block_flow.base.block_container_inline_size),
style.min_inline_size(),
style.max_inline_size(),
);
let available_block_size = AxisSize::new(specified_block_size,
self.block_flow.base.block_container_explicit_block_size,
style.min_block_size(),
style.max_block_size());
let available_block_size = AxisSize::new(
specified_block_size,
self.block_flow.base.block_container_explicit_block_size,
style.min_block_size(),
style.max_block_size(),
);
(available_block_size, available_inline_size)
};
@ -903,37 +999,46 @@ impl Flow for FlexFlow {
let inline_start_content_edge = self.block_flow.fragment.border_box.start.i +
self.block_flow.fragment.border_padding.inline_start;
debug!("inline_start_content_edge = {:?}", inline_start_content_edge);
debug!(
"inline_start_content_edge = {:?}",
inline_start_content_edge
);
let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
// Distance from the inline-end margin edge to the inline-end content edge.
let inline_end_content_edge =
self.block_flow.fragment.margin.inline_end +
let inline_end_content_edge = self.block_flow.fragment.margin.inline_end +
self.block_flow.fragment.border_padding.inline_end;
debug!("padding_and_borders = {:?}", padding_and_borders);
debug!("self.block_flow.fragment.border_box.size.inline = {:?}",
self.block_flow.fragment.border_box.size.inline);
let content_inline_size = self.block_flow.fragment.border_box.size.inline - padding_and_borders;
debug!(
"self.block_flow.fragment.border_box.size.inline = {:?}",
self.block_flow.fragment.border_box.size.inline
);
let content_inline_size =
self.block_flow.fragment.border_box.size.inline - padding_and_borders;
match self.main_mode {
Direction::Inline => {
self.available_main_size = available_inline_size;
self.available_cross_size = available_block_size;
self.inline_mode_assign_inline_sizes(layout_context,
inline_start_content_edge,
inline_end_content_edge,
content_inline_size)
}
Direction::Block => {
self.inline_mode_assign_inline_sizes(
layout_context,
inline_start_content_edge,
inline_end_content_edge,
content_inline_size,
)
},
Direction::Block => {
self.available_main_size = available_block_size;
self.available_cross_size = available_inline_size;
self.block_mode_assign_inline_sizes(layout_context,
inline_start_content_edge,
inline_end_content_edge,
content_inline_size)
}
self.block_mode_assign_inline_sizes(
layout_context,
inline_start_content_edge,
inline_end_content_edge,
content_inline_size,
)
},
}
}
@ -941,31 +1046,37 @@ impl Flow for FlexFlow {
match self.main_mode {
Direction::Inline => {
self.inline_mode_assign_block_size(layout_context);
let block_start = AdjoiningMargins::from_margin(self.block_flow.fragment.margin.block_start);
let block_end = AdjoiningMargins::from_margin(self.block_flow.fragment.margin.block_end);
self.block_flow.base.collapsible_margins = CollapsibleMargins::Collapse(block_start, block_end);
let block_start =
AdjoiningMargins::from_margin(self.block_flow.fragment.margin.block_start);
let block_end =
AdjoiningMargins::from_margin(self.block_flow.fragment.margin.block_end);
self.block_flow.base.collapsible_margins =
CollapsibleMargins::Collapse(block_start, block_end);
// TODO(stshine): assign proper static position for absolute descendants.
if (&*self as &Flow).contains_roots_of_absolute_flow_tree() {
// Assign block-sizes for all flows in this absolute flow tree.
// This is preorder because the block-size of an absolute flow may depend on
// the block-size of its containing block, which may also be an absolute flow.
let assign_abs_b_sizes = AbsoluteAssignBSizesTraversal(layout_context.shared_context());
let assign_abs_b_sizes =
AbsoluteAssignBSizesTraversal(layout_context.shared_context());
assign_abs_b_sizes.traverse_absolute_flows(&mut *self);
}
}
Direction::Block =>{
self.block_flow
.assign_block_size_block_base(layout_context,
None,
MarginsMayCollapseFlag::MarginsMayNotCollapse);
},
Direction::Block => {
self.block_flow.assign_block_size_block_base(
layout_context,
None,
MarginsMayCollapseFlag::MarginsMayNotCollapse,
);
self.block_mode_assign_block_size();
}
},
}
}
fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
self.block_flow.compute_stacking_relative_position(layout_context)
self.block_flow
.compute_stacking_relative_position(layout_context)
}
fn place_float_if_applicable<'a>(&mut self) {
@ -973,11 +1084,13 @@ impl Flow for FlexFlow {
}
fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
self.block_flow.update_late_computed_inline_position_if_necessary(inline_position)
self.block_flow
.update_late_computed_inline_position_if_necessary(inline_position)
}
fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
self.block_flow.update_late_computed_block_position_if_necessary(block_position)
self.block_flow
.update_late_computed_block_position_if_necessary(block_position)
}
fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
@ -1008,11 +1121,17 @@ impl Flow for FlexFlow {
self.block_flow.generated_containing_block_size(flow)
}
fn iterate_through_fragment_border_boxes(&self,
iterator: &mut FragmentBorderBoxIterator,
level: i32,
stacking_context_position: &Point2D<Au>) {
self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position);
fn iterate_through_fragment_border_boxes(
&self,
iterator: &mut FragmentBorderBoxIterator,
level: i32,
stacking_context_position: &Point2D<Au>,
) {
self.block_flow.iterate_through_fragment_border_boxes(
iterator,
level,
stacking_context_position,
);
}
fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {

View file

@ -16,7 +16,7 @@ use style::values::computed::LengthOrPercentageOrAuto;
#[derive(Clone, Copy, Debug, Serialize)]
pub enum FloatKind {
Left,
Right
Right,
}
impl FloatKind {
@ -78,7 +78,12 @@ impl FloatList {
impl fmt::Debug for FloatList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "max_block_start={:?} floats={}", self.max_block_start, self.floats.len())?;
write!(
f,
"max_block_start={:?} floats={}",
self.max_block_start,
self.floats.len()
)?;
for float in self.floats.iter() {
write!(f, " {:?}", float)?;
}
@ -95,22 +100,29 @@ pub struct PlacementInfo {
/// The maximum inline-end position of the float, generally determined by the containing block.
pub max_inline_size: Au,
/// The kind of float.
pub kind: FloatKind
pub kind: FloatKind,
}
impl fmt::Debug for PlacementInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f,
"size={:?} ceiling={:?} max_inline_size={:?} kind={:?}",
self.size,
self.ceiling,
self.max_inline_size,
self.kind)
write!(
f,
"size={:?} ceiling={:?} max_inline_size={:?} kind={:?}",
self.size, self.ceiling, self.max_inline_size, self.kind
)
}
}
fn range_intersect(block_start_1: Au, block_end_1: Au, block_start_2: Au, block_end_2: Au) -> (Au, Au) {
(max(block_start_1, block_start_2), min(block_end_1, block_end_2))
fn range_intersect(
block_start_1: Au,
block_end_1: Au,
block_start_2: Au,
block_end_2: Au,
) -> (Au, Au) {
(
max(block_start_1, block_start_2),
min(block_end_1, block_end_2),
)
}
/// Encapsulates information about floats. This is optimized to avoid allocation if there are
@ -162,8 +174,12 @@ impl Floats {
/// with inline-size small enough that it doesn't collide with any floats. max_x is the
/// inline-size beyond which floats have no effect. (Generally this is the containing block
/// inline-size.)
pub fn available_rect(&self, block_start: Au, block_size: Au, max_x: Au)
-> Option<LogicalRect<Au>> {
pub fn available_rect(
&self,
block_start: Au,
block_size: Au,
max_x: Au,
) -> Option<LogicalRect<Au>> {
let list = &self.list;
let block_start = block_start - self.offset.block;
@ -186,30 +202,38 @@ impl Floats {
debug!("float_pos: {:?}, float_size: {:?}", float_pos, float_size);
match float.kind {
FloatKind::Left if float_pos.i + float_size.inline > max_inline_start &&
FloatKind::Left
if float_pos.i + float_size.inline > max_inline_start &&
float_pos.b + float_size.block > block_start &&
float_pos.b < block_start + block_size => {
float_pos.b < block_start + block_size =>
{
max_inline_start = float_pos.i + float_size.inline;
l_block_start = Some(float_pos.b);
l_block_end = Some(float_pos.b + float_size.block);
debug!("available_rect: collision with inline_start float: new \
max_inline_start is {:?}",
max_inline_start);
debug!(
"available_rect: collision with inline_start float: new \
max_inline_start is {:?}",
max_inline_start
);
}
FloatKind::Right if float_pos.i < min_inline_end &&
float_pos.b + float_size.block > block_start &&
float_pos.b < block_start + block_size => {
FloatKind::Right
if float_pos.i < min_inline_end &&
float_pos.b + float_size.block > block_start &&
float_pos.b < block_start + block_size =>
{
min_inline_end = float_pos.i;
r_block_start = Some(float_pos.b);
r_block_end = Some(float_pos.b + float_size.block);
debug!("available_rect: collision with inline_end float: new min_inline_end \
is {:?}",
min_inline_end);
debug!(
"available_rect: collision with inline_end float: new min_inline_end \
is {:?}",
min_inline_end
);
}
FloatKind::Left | FloatKind::Right => {}
FloatKind::Left | FloatKind::Right => {},
}
}
@ -217,25 +241,28 @@ impl Floats {
// If there are floats on both sides, take the intersection of the
// two areas. Also make sure we never return a block-start smaller than the
// given upper bound.
let (block_start, block_end) = match (r_block_start,
r_block_end,
l_block_start,
l_block_end) {
(Some(r_block_start), Some(r_block_end), Some(l_block_start), Some(l_block_end)) => {
range_intersect(max(block_start, r_block_start),
r_block_end,
max(block_start, l_block_start),
l_block_end)
}
(None, None, Some(l_block_start), Some(l_block_end)) => {
(max(block_start, l_block_start), l_block_end)
}
(Some(r_block_start), Some(r_block_end), None, None) => {
(max(block_start, r_block_start), r_block_end)
}
(None, None, None, None) => return None,
_ => panic!("Reached unreachable state when computing float area")
};
let (block_start, block_end) =
match (r_block_start, r_block_end, l_block_start, l_block_end) {
(
Some(r_block_start),
Some(r_block_end),
Some(l_block_start),
Some(l_block_end),
) => range_intersect(
max(block_start, r_block_start),
r_block_end,
max(block_start, l_block_start),
l_block_end,
),
(None, None, Some(l_block_start), Some(l_block_end)) => {
(max(block_start, l_block_start), l_block_end)
},
(Some(r_block_start), Some(r_block_end), None, None) => {
(max(block_start, r_block_start), r_block_end)
},
(None, None, None, None) => return None,
_ => panic!("Reached unreachable state when computing float area"),
};
// FIXME(eatkinson): This assertion is too strong and fails in some cases. It is OK to
// return negative inline-sizes since we check against that inline-end away, but we should
@ -244,11 +271,13 @@ impl Floats {
assert!(block_start <= block_end, "Float position error");
Some(LogicalRect::new(self.writing_mode,
max_inline_start + self.offset.inline,
block_start + self.offset.block,
min_inline_end - max_inline_start,
block_end - block_start))
Some(LogicalRect::new(
self.writing_mode,
max_inline_start + self.offset.inline,
block_start + self.offset.block,
min_inline_end - max_inline_start,
block_end - block_start,
))
}
/// Adds a new float to the list.
@ -260,7 +289,7 @@ impl Floats {
Some(max_block_start) => max(info.ceiling, max_block_start + self.offset.block),
},
max_inline_size: info.max_inline_size,
kind: info.kind
kind: info.kind,
};
debug!("add_float: added float with info {:?}", new_info);
@ -271,7 +300,7 @@ impl Floats {
self.place_between_floats(&new_info).start - self.offset,
info.size,
),
kind: info.kind
kind: info.kind,
};
self.list.floats = self.list.floats.prepend_elem(new_float);
@ -284,8 +313,12 @@ impl Floats {
/// Given the three sides of the bounding rectangle in the block-start direction, finds the
/// largest block-size that will result in the rectangle not colliding with any floats. Returns
/// `None` if that block-size is infinite.
fn max_block_size_for_bounds(&self, inline_start: Au, block_start: Au, inline_size: Au)
-> Option<Au> {
fn max_block_size_for_bounds(
&self,
inline_start: Au,
block_start: Au,
inline_size: Au,
) -> Option<Au> {
let list = &self.list;
let block_start = block_start - self.offset.block;
@ -294,10 +327,11 @@ impl Floats {
for float in list.floats.iter() {
if float.bounds.start.b + float.bounds.size.block > block_start &&
float.bounds.start.i + float.bounds.size.inline > inline_start &&
float.bounds.start.i < inline_start + inline_size {
let new_y = float.bounds.start.b;
max_block_size = Some(min(max_block_size.unwrap_or(new_y), new_y));
float.bounds.start.i + float.bounds.size.inline > inline_start &&
float.bounds.start.i < inline_start + inline_size
{
let new_y = float.bounds.start.b;
max_block_size = Some(min(max_block_size.unwrap_or(new_y), new_y));
}
}
@ -318,85 +352,88 @@ impl Floats {
Au(0),
info.ceiling,
info.max_inline_size,
MAX_AU)
}
MAX_AU,
)
},
FloatKind::Right => {
return LogicalRect::new(
self.writing_mode,
info.max_inline_size - info.size.inline,
info.ceiling,
info.max_inline_size,
MAX_AU)
}
MAX_AU,
)
},
}
}
// Can't go any higher than previous floats or previous elements in the document.
let mut float_b = info.ceiling;
loop {
let maybe_location = self.available_rect(float_b,
info.size.block,
info.max_inline_size);
debug!("place_float: got available rect: {:?} for block-pos: {:?}",
maybe_location,
float_b);
let maybe_location =
self.available_rect(float_b, info.size.block, info.max_inline_size);
debug!(
"place_float: got available rect: {:?} for block-pos: {:?}",
maybe_location, float_b
);
match maybe_location {
// If there are no floats blocking us, return the current location
// TODO(eatkinson): integrate with overflow
None => {
return match info.kind {
FloatKind::Left => {
LogicalRect::new(
self.writing_mode,
Au(0),
float_b,
info.max_inline_size,
MAX_AU)
}
FloatKind::Right => {
LogicalRect::new(
self.writing_mode,
info.max_inline_size - info.size.inline,
float_b,
info.max_inline_size,
MAX_AU)
}
FloatKind::Left => LogicalRect::new(
self.writing_mode,
Au(0),
float_b,
info.max_inline_size,
MAX_AU,
),
FloatKind::Right => LogicalRect::new(
self.writing_mode,
info.max_inline_size - info.size.inline,
float_b,
info.max_inline_size,
MAX_AU,
),
}
}
},
Some(rect) => {
assert_ne!(rect.start.b + rect.size.block, float_b,
"Non-terminating float placement");
assert_ne!(
rect.start.b + rect.size.block,
float_b,
"Non-terminating float placement"
);
// Place here if there is enough room
if rect.size.inline >= info.size.inline {
let block_size = self.max_block_size_for_bounds(rect.start.i,
rect.start.b,
rect.size.inline);
let block_size = self.max_block_size_for_bounds(
rect.start.i,
rect.start.b,
rect.size.inline,
);
let block_size = block_size.unwrap_or(MAX_AU);
return match info.kind {
FloatKind::Left => {
LogicalRect::new(
self.writing_mode,
rect.start.i,
float_b,
rect.size.inline,
block_size)
}
FloatKind::Right => {
LogicalRect::new(
self.writing_mode,
rect.start.i + rect.size.inline - info.size.inline,
float_b,
rect.size.inline,
block_size)
}
}
FloatKind::Left => LogicalRect::new(
self.writing_mode,
rect.start.i,
float_b,
rect.size.inline,
block_size,
),
FloatKind::Right => LogicalRect::new(
self.writing_mode,
rect.start.i + rect.size.inline - info.size.inline,
float_b,
rect.size.inline,
block_size,
),
};
}
// Try to place at the next-lowest location.
// Need to be careful of fencepost errors.
float_b = rect.start.b + rect.size.block;
}
},
}
}
}
@ -411,8 +448,8 @@ impl Floats {
(ClearType::Both, _) => {
let b = self.offset.block + float.bounds.start.b + float.bounds.size.block;
clearance = max(clearance, b);
}
_ => {}
},
_ => {},
}
}
clearance
@ -486,14 +523,20 @@ impl SpeculatedFloatPlacement {
}
}
self.left = max(self.left, block_flow.base.speculated_float_placement_in.left);
self.right = max(self.right, block_flow.base.speculated_float_placement_in.right);
self.left = max(
self.left,
block_flow.base.speculated_float_placement_in.left,
);
self.right = max(
self.right,
block_flow.base.speculated_float_placement_in.right,
);
}
}
let base_flow = flow.base();
if !base_flow.flags.is_float() {
return
return;
}
let mut float_inline_size = base_flow.intrinsic_inline_sizes.preferred_inline_size;
@ -504,7 +547,8 @@ impl SpeculatedFloatPlacement {
// that the layout traversal logic will know that objects later in the document
// might flow around this float.
if let LengthOrPercentageOrAuto::Percentage(percentage) =
flow.as_block().fragment.style.content_inline_size() {
flow.as_block().fragment.style.content_inline_size()
{
if percentage.0 > 0.0 {
float_inline_size = Au::from_px(1)
}
@ -513,7 +557,7 @@ impl SpeculatedFloatPlacement {
}
match base_flow.flags.float_kind() {
StyleFloat::None => {}
StyleFloat::None => {},
StyleFloat::Left => self.left = self.left + float_inline_size,
StyleFloat::Right => self.right = self.right + float_inline_size,
}
@ -522,17 +566,18 @@ impl SpeculatedFloatPlacement {
/// Given a flow, computes the speculated inline size of the floats in of its first child.
pub fn compute_floats_in_for_first_child(parent_flow: &mut Flow) -> SpeculatedFloatPlacement {
if !parent_flow.is_block_like() {
return parent_flow.base().speculated_float_placement_in
return parent_flow.base().speculated_float_placement_in;
}
let parent_block_flow = parent_flow.as_block();
if parent_block_flow.formatting_context_type() != FormattingContextType::None {
return SpeculatedFloatPlacement::zero()
return SpeculatedFloatPlacement::zero();
}
let mut placement = parent_block_flow.base.speculated_float_placement_in;
let speculated_inline_content_edge_offsets =
parent_block_flow.fragment.guess_inline_content_edge_offsets();
let speculated_inline_content_edge_offsets = parent_block_flow
.fragment
.guess_inline_content_edge_offsets();
if speculated_inline_content_edge_offsets.start > Au(0) {
placement.left = if placement.left > speculated_inline_content_edge_offsets.start {
@ -552,4 +597,3 @@ impl SpeculatedFloatPlacement {
placement
}
}

View file

@ -251,11 +251,15 @@ pub trait Flow: HasBaseFlow + fmt::Debug + Sync + Send + 'static {
/// and return a new flow similar to `self` with the rest of the content.
///
/// The default is to make a flow "atomic": it can not be fragmented.
fn fragment(&mut self,
layout_context: &LayoutContext,
_fragmentation_context: Option<FragmentationContext>)
-> Option<Arc<Flow>> {
fn recursive_assign_block_size<F: ?Sized + Flow + GetBaseFlow>(flow: &mut F, ctx: &LayoutContext) {
fn fragment(
&mut self,
layout_context: &LayoutContext,
_fragmentation_context: Option<FragmentationContext>,
) -> Option<Arc<Flow>> {
fn recursive_assign_block_size<F: ?Sized + Flow + GetBaseFlow>(
flow: &mut F,
ctx: &LayoutContext,
) {
for child in flow.mut_base().child_iter_mut() {
recursive_assign_block_size(child, ctx)
}
@ -277,17 +281,20 @@ pub trait Flow: HasBaseFlow + fmt::Debug + Sync + Send + 'static {
/// `parent_thread_id` is the thread ID of the parent. This is used for the layout tinting
/// debug mode; if the block size of this flow was determined by its parent, we should treat
/// it as laid out by its parent.
fn assign_block_size_for_inorder_child_if_necessary(&mut self,
layout_context: &LayoutContext,
parent_thread_id: u8,
_content_box: LogicalRect<Au>)
-> bool {
let might_have_floats_in_or_out = self.base().might_have_floats_in() ||
self.base().might_have_floats_out();
fn assign_block_size_for_inorder_child_if_necessary(
&mut self,
layout_context: &LayoutContext,
parent_thread_id: u8,
_content_box: LogicalRect<Au>,
) -> bool {
let might_have_floats_in_or_out =
self.base().might_have_floats_in() || self.base().might_have_floats_out();
if might_have_floats_in_or_out {
self.mut_base().thread_id = parent_thread_id;
self.assign_block_size(layout_context);
self.mut_base().restyle_damage.remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
self.mut_base()
.restyle_damage
.remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
}
might_have_floats_in_or_out
}
@ -295,23 +302,32 @@ pub trait Flow: HasBaseFlow + fmt::Debug + Sync + Send + 'static {
fn get_overflow_in_parent_coordinates(&self) -> Overflow {
// FIXME(#2795): Get the real container size.
let container_size = Size2D::zero();
let position = self.base().position.to_physical(self.base().writing_mode, container_size);
let position = self
.base()
.position
.to_physical(self.base().writing_mode, container_size);
let mut overflow = self.base().overflow;
match self.class() {
FlowClass::Block | FlowClass::TableCaption | FlowClass::TableCell => {}
FlowClass::Block | FlowClass::TableCaption | FlowClass::TableCell => {},
_ => {
overflow.translate(&position.origin.to_vector());
return overflow;
}
},
}
let border_box = self.as_block().fragment.stacking_relative_border_box(
&self.base().stacking_relative_position,
&self.base().early_absolute_position_info.relative_containing_block_size,
self.base().early_absolute_position_info.relative_containing_block_mode,
CoordinateSystem::Own);
&self
.base()
.early_absolute_position_info
.relative_containing_block_size,
self.base()
.early_absolute_position_info
.relative_containing_block_mode,
CoordinateSystem::Own,
);
if StyleOverflow::Visible != self.as_block().fragment.style.get_box().overflow_x {
overflow.paint.origin.x = Au(0);
overflow.paint.size.width = border_box.size.width;
@ -325,24 +341,35 @@ pub trait Flow: HasBaseFlow + fmt::Debug + Sync + Send + 'static {
overflow.scroll.size.height = border_box.size.height;
}
if !self.as_block().fragment.establishes_stacking_context() ||
self.as_block().fragment.style.get_box().transform.0.is_empty() {
if !self.as_block().fragment.establishes_stacking_context() || self
.as_block()
.fragment
.style
.get_box()
.transform
.0
.is_empty()
{
overflow.translate(&position.origin.to_vector());
return overflow;
}
// TODO: Take into account 3d transforms, even though it's a fairly
// uncommon case.
let transform_2d = self.as_block()
.fragment
.transform_matrix(&position)
.unwrap_or(LayoutTransform::identity())
.to_2d().to_untyped();
let transform_2d = self
.as_block()
.fragment
.transform_matrix(&position)
.unwrap_or(LayoutTransform::identity())
.to_2d()
.to_untyped();
let transformed_overflow = Overflow {
paint: f32_rect_to_au_rect(transform_2d.transform_rect(
&au_rect_to_f32_rect(overflow.paint))),
scroll: f32_rect_to_au_rect(transform_2d.transform_rect(
&au_rect_to_f32_rect(overflow.scroll))),
paint: f32_rect_to_au_rect(
transform_2d.transform_rect(&au_rect_to_f32_rect(overflow.paint)),
),
scroll: f32_rect_to_au_rect(
transform_2d.transform_rect(&au_rect_to_f32_rect(overflow.scroll)),
),
};
// TODO: We are taking the union of the overflow and transformed overflow here, which
@ -369,14 +396,12 @@ pub trait Flow: HasBaseFlow + fmt::Debug + Sync + Send + 'static {
// Calculate overflow on a per-fragment basis.
let mut overflow = self.compute_overflow();
match self.class() {
FlowClass::Block |
FlowClass::TableCaption |
FlowClass::TableCell => {
FlowClass::Block | FlowClass::TableCaption | FlowClass::TableCell => {
for kid in self.mut_base().children.iter_mut() {
overflow.union(&kid.get_overflow_in_parent_coordinates());
}
}
_ => {}
},
_ => {},
}
self.mut_base().overflow = overflow
}
@ -396,17 +421,21 @@ pub trait Flow: HasBaseFlow + fmt::Debug + Sync + Send + 'static {
/// Iterates through border boxes of all of this flow's fragments.
/// Level provides a zero based index indicating the current
/// depth of the flow tree during fragment iteration.
fn iterate_through_fragment_border_boxes(&self,
iterator: &mut FragmentBorderBoxIterator,
level: i32,
stacking_context_position: &Point2D<Au>);
fn iterate_through_fragment_border_boxes(
&self,
iterator: &mut FragmentBorderBoxIterator,
level: i32,
stacking_context_position: &Point2D<Au>,
);
/// Mutably iterates through fragments in this flow.
fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment));
fn compute_collapsible_block_start_margin(&mut self,
_layout_context: &mut LayoutContext,
_margin_collapse_info: &mut MarginCollapseInfo) {
fn compute_collapsible_block_start_margin(
&mut self,
_layout_context: &mut LayoutContext,
_margin_collapse_info: &mut MarginCollapseInfo,
) {
// The default implementation is a no-op.
}
@ -436,8 +465,10 @@ pub trait Flow: HasBaseFlow + fmt::Debug + Sync + Send + 'static {
}
fn contains_positioned_fragments(&self) -> bool {
self.contains_relatively_positioned_fragments() ||
self.base().flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
self.contains_relatively_positioned_fragments() || self
.base()
.flags
.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
}
fn contains_relatively_positioned_fragments(&self) -> bool {
@ -476,7 +507,7 @@ pub trait Flow: HasBaseFlow + fmt::Debug + Sync + Send + 'static {
/// Print any extra children (such as fragments) contained in this Flow
/// for debugging purposes. Any items inserted into the tree will become
/// children of this flow.
fn print_extra_flow_children(&self, _: &mut PrintTree) { }
fn print_extra_flow_children(&self, _: &mut PrintTree) {}
fn clipping_and_scrolling(&self) -> ClippingAndScrolling {
match self.base().clipping_and_scrolling {
@ -566,8 +597,10 @@ pub trait MutableOwnedFlowUtils {
/// </span>
/// </div>
/// ```
fn take_applicable_absolute_descendants(&mut self,
absolute_descendants: &mut AbsoluteDescendants);
fn take_applicable_absolute_descendants(
&mut self,
absolute_descendants: &mut AbsoluteDescendants,
);
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
@ -590,9 +623,15 @@ pub enum FlowClass {
impl FlowClass {
fn is_block_like(self) -> bool {
match self {
FlowClass::Block | FlowClass::ListItem | FlowClass::Table | FlowClass::TableRowGroup |
FlowClass::TableRow | FlowClass::TableCaption | FlowClass::TableCell |
FlowClass::TableWrapper | FlowClass::Flex => true,
FlowClass::Block |
FlowClass::ListItem |
FlowClass::Table |
FlowClass::TableRowGroup |
FlowClass::TableRow |
FlowClass::TableCaption |
FlowClass::TableCell |
FlowClass::TableWrapper |
FlowClass::Flex => true,
_ => false,
}
}
@ -663,7 +702,7 @@ impl FlowFlags {
#[inline]
pub fn set_text_align(&mut self, value: TextAlign) {
*self = (*self & !FlowFlags::TEXT_ALIGN) |
FlowFlags::from_bits((value as u32) << TEXT_ALIGN_SHIFT).unwrap();
FlowFlags::from_bits((value as u32) << TEXT_ALIGN_SHIFT).unwrap();
}
#[inline]
@ -776,7 +815,9 @@ pub struct AbsoluteDescendantIter<'a> {
impl<'a> Iterator for AbsoluteDescendantIter<'a> {
type Item = &'a mut Flow;
fn next(&mut self) -> Option<&'a mut Flow> {
self.iter.next().map(|info| FlowRef::deref_mut(&mut info.flow))
self.iter
.next()
.map(|info| FlowRef::deref_mut(&mut info.flow))
}
fn size_hint(&self) -> (usize, Option<usize>) {
@ -953,22 +994,32 @@ impl fmt::Debug for BaseFlow {
"".to_owned()
};
write!(f,
"\nsc={:?}\
\npos={:?}{}{}\
\nfloatspec-in={:?}\
\nfloatspec-out={:?}\
\noverflow={:?}{}{}{}",
self.stacking_context_id,
self.position,
if self.flags.contains(FlowFlags::FLOATS_LEFT) { "FL" } else { "" },
if self.flags.contains(FlowFlags::FLOATS_RIGHT) { "FR" } else { "" },
self.speculated_float_placement_in,
self.speculated_float_placement_out,
self.overflow,
child_count_string,
absolute_descendants_string,
damage_string)
write!(
f,
"\nsc={:?}\
\npos={:?}{}{}\
\nfloatspec-in={:?}\
\nfloatspec-out={:?}\
\noverflow={:?}{}{}{}",
self.stacking_context_id,
self.position,
if self.flags.contains(FlowFlags::FLOATS_LEFT) {
"FL"
} else {
""
},
if self.flags.contains(FlowFlags::FLOATS_RIGHT) {
"FR"
} else {
""
},
self.speculated_float_placement_in,
self.speculated_float_placement_out,
self.overflow,
child_count_string,
absolute_descendants_string,
damage_string
)
}
}
@ -976,7 +1027,10 @@ impl Serialize for BaseFlow {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut serializer = serializer.serialize_struct("base", 5)?;
serializer.serialize_field("id", &self.debug_id())?;
serializer.serialize_field("stacking_relative_position", &self.stacking_relative_position)?;
serializer.serialize_field(
"stacking_relative_position",
&self.stacking_relative_position,
)?;
serializer.serialize_field("intrinsic_inline_sizes", &self.intrinsic_inline_sizes)?;
serializer.serialize_field("position", &self.position)?;
serializer.serialize_field("children", &self.children)?;
@ -996,10 +1050,11 @@ pub enum ForceNonfloatedFlag {
impl BaseFlow {
#[inline]
pub fn new(style: Option<&ComputedValues>,
writing_mode: WritingMode,
force_nonfloated: ForceNonfloatedFlag)
-> BaseFlow {
pub fn new(
style: Option<&ComputedValues>,
writing_mode: WritingMode,
force_nonfloated: ForceNonfloatedFlag,
) -> BaseFlow {
let mut flags = FlowFlags::empty();
match style {
Some(style) => {
@ -1013,41 +1068,47 @@ impl BaseFlow {
let logical_position = style.logical_position();
if logical_position.inline_start == LengthOrPercentageOrAuto::Auto &&
logical_position.inline_end == LengthOrPercentageOrAuto::Auto {
logical_position.inline_end == LengthOrPercentageOrAuto::Auto
{
flags.insert(FlowFlags::INLINE_POSITION_IS_STATIC);
}
if logical_position.block_start == LengthOrPercentageOrAuto::Auto &&
logical_position.block_end == LengthOrPercentageOrAuto::Auto {
logical_position.block_end == LengthOrPercentageOrAuto::Auto
{
flags.insert(FlowFlags::BLOCK_POSITION_IS_STATIC);
}
}
_ => flags.insert(FlowFlags::BLOCK_POSITION_IS_STATIC | FlowFlags::INLINE_POSITION_IS_STATIC),
},
_ => flags.insert(
FlowFlags::BLOCK_POSITION_IS_STATIC | FlowFlags::INLINE_POSITION_IS_STATIC,
),
}
if force_nonfloated == ForceNonfloatedFlag::FloatIfNecessary {
match style.get_box().float {
Float::None => {}
Float::None => {},
Float::Left => flags.insert(FlowFlags::FLOATS_LEFT),
Float::Right => flags.insert(FlowFlags::FLOATS_RIGHT),
}
}
match style.get_box().clear {
Clear::None => {}
Clear::None => {},
Clear::Left => flags.insert(FlowFlags::CLEARS_LEFT),
Clear::Right => flags.insert(FlowFlags::CLEARS_RIGHT),
Clear::Both => {
flags.insert(FlowFlags::CLEARS_LEFT);
flags.insert(FlowFlags::CLEARS_RIGHT);
}
},
}
if !style.get_counters().counter_reset.is_empty() ||
!style.get_counters().counter_increment.is_empty() {
!style.get_counters().counter_increment.is_empty()
{
flags.insert(FlowFlags::AFFECTS_COUNTERS)
}
}
None => flags.insert(FlowFlags::BLOCK_POSITION_IS_STATIC | FlowFlags::INLINE_POSITION_IS_STATIC),
},
None => flags
.insert(FlowFlags::BLOCK_POSITION_IS_STATIC | FlowFlags::INLINE_POSITION_IS_STATIC),
}
// New flows start out as fully damaged.
@ -1089,17 +1150,24 @@ impl BaseFlow {
pub fn update_flags_if_needed(&mut self, style: &ComputedValues) {
// For absolutely-positioned flows, changes to top/bottom/left/right can cause these flags
// to get out of date:
if self.restyle_damage.contains(ServoRestyleDamage::REFLOW_OUT_OF_FLOW) {
if self
.restyle_damage
.contains(ServoRestyleDamage::REFLOW_OUT_OF_FLOW)
{
// Note: We don't need to check whether IS_ABSOLUTELY_POSITIONED has changed, because
// changes to the 'position' property trigger flow reconstruction.
if self.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
let logical_position = style.logical_position();
self.flags.set(FlowFlags::INLINE_POSITION_IS_STATIC,
self.flags.set(
FlowFlags::INLINE_POSITION_IS_STATIC,
logical_position.inline_start == LengthOrPercentageOrAuto::Auto &&
logical_position.inline_end == LengthOrPercentageOrAuto::Auto);
self.flags.set(FlowFlags::BLOCK_POSITION_IS_STATIC,
logical_position.inline_end == LengthOrPercentageOrAuto::Auto,
);
self.flags.set(
FlowFlags::BLOCK_POSITION_IS_STATIC,
logical_position.block_start == LengthOrPercentageOrAuto::Auto &&
logical_position.block_end == LengthOrPercentageOrAuto::Auto);
logical_position.block_end == LengthOrPercentageOrAuto::Auto,
);
}
}
}
@ -1108,8 +1176,10 @@ impl BaseFlow {
pub fn clone_with_children(&self, children: FlowList) -> BaseFlow {
BaseFlow {
children: children,
restyle_damage: self.restyle_damage | ServoRestyleDamage::REPAINT |
ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW,
restyle_damage: self.restyle_damage |
ServoRestyleDamage::REPAINT |
ServoRestyleDamage::REFLOW_OUT_OF_FLOW |
ServoRestyleDamage::REFLOW,
parallel: FlowParallelInfo::new(),
floats: self.floats.clone(),
abs_descendants: self.abs_descendants.clone(),
@ -1138,8 +1208,10 @@ impl BaseFlow {
return self as *const BaseFlow as usize;
}
pub fn collect_stacking_contexts_for_children(&mut self,
state: &mut StackingContextCollectionState) {
pub fn collect_stacking_contexts_for_children(
&mut self,
state: &mut StackingContextCollectionState,
) {
for kid in self.children.iter_mut() {
kid.collect_stacking_contexts(state);
}
@ -1157,15 +1229,17 @@ impl BaseFlow {
self.speculated_float_placement_out.right > Au(0)
}
/// Compute the fragment position relative to the parent stacking context. If the fragment
/// itself establishes a stacking context, then the origin of its position will be (0, 0)
/// for the purposes of this computation.
pub fn stacking_relative_border_box_for_display_list(&self, fragment: &Fragment) -> Rect<Au> {
fragment.stacking_relative_border_box(
&self.stacking_relative_position,
&self.early_absolute_position_info.relative_containing_block_size,
self.early_absolute_position_info.relative_containing_block_mode,
&self
.early_absolute_position_info
.relative_containing_block_size,
self.early_absolute_position_info
.relative_containing_block_mode,
CoordinateSystem::Own,
)
}
@ -1182,8 +1256,10 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
/// table-column-group flow, or table-caption flow.
fn is_proper_table_child(self) -> bool {
match self.class() {
FlowClass::TableRow | FlowClass::TableRowGroup |
FlowClass::TableColGroup | FlowClass::TableCaption => true,
FlowClass::TableRow |
FlowClass::TableRowGroup |
FlowClass::TableColGroup |
FlowClass::TableCaption => true,
_ => false,
}
}
@ -1239,9 +1315,13 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
/// Returns true if this flow is one of table-related flows.
fn is_table_kind(self) -> bool {
match self.class() {
FlowClass::TableWrapper | FlowClass::Table |
FlowClass::TableColGroup | FlowClass::TableRowGroup |
FlowClass::TableRow | FlowClass::TableCaption | FlowClass::TableCell => true,
FlowClass::TableWrapper |
FlowClass::Table |
FlowClass::TableColGroup |
FlowClass::TableRowGroup |
FlowClass::TableRow |
FlowClass::TableCaption |
FlowClass::TableCell => true,
_ => false,
}
}
@ -1268,7 +1348,7 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
FlowClass::Block | FlowClass::TableCaption | FlowClass::TableCell => {
// FIXME: Actually check the type of the node
self.child_count() != 0
}
},
_ => false,
}
}
@ -1307,13 +1387,13 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
fn floats_might_flow_through(self) -> bool {
if !self.base().might_have_floats_in() && !self.base().might_have_floats_out() {
return false
return false;
}
if self.is_root() {
return false
return false;
}
if !self.is_block_like() {
return true
return true;
}
self.as_block().formatting_context_type() == FormattingContextType::None
}
@ -1322,12 +1402,16 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
for kid in self.base().children.iter().rev() {
if kid.is_inline_flow() {
if let Some(baseline_offset) = kid.as_inline().baseline_offset_of_last_line() {
return Some(kid.base().position.start.b + baseline_offset)
return Some(kid.base().position.start.b + baseline_offset);
}
}
if kid.is_block_like() && !kid.base().flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
if kid.is_block_like() && !kid
.base()
.flags
.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
{
if let Some(baseline_offset) = kid.baseline_offset_of_last_line_box_in_flow() {
return Some(kid.base().position.start.b + baseline_offset)
return Some(kid.base().position.start.b + baseline_offset);
}
}
}
@ -1374,17 +1458,19 @@ impl MutableOwnedFlowUtils for FlowRef {
/// </span>
/// </div>
/// ```
fn take_applicable_absolute_descendants(&mut self,
absolute_descendants: &mut AbsoluteDescendants) {
fn take_applicable_absolute_descendants(
&mut self,
absolute_descendants: &mut AbsoluteDescendants,
) {
let mut applicable_absolute_descendants = AbsoluteDescendants::new();
for absolute_descendant in absolute_descendants.descendant_links.iter() {
if absolute_descendant.has_reached_containing_block {
applicable_absolute_descendants.push(absolute_descendant.flow.clone());
}
}
absolute_descendants.descendant_links.retain(|descendant| {
!descendant.has_reached_containing_block
});
absolute_descendants
.descendant_links
.retain(|descendant| !descendant.has_reached_containing_block);
let this = self.clone();
let base = FlowRef::deref_mut(self).mut_base();
@ -1412,9 +1498,7 @@ pub struct ContainingBlockLink {
impl ContainingBlockLink {
fn new() -> ContainingBlockLink {
ContainingBlockLink {
link: None,
}
ContainingBlockLink { link: None }
}
fn set(&mut self, link: FlowRef) {
@ -1424,34 +1508,38 @@ impl ContainingBlockLink {
#[inline]
pub fn generated_containing_block_size(&self, for_flow: OpaqueFlow) -> LogicalSize<Au> {
match self.link {
None => {
panic!("Link to containing block not established; perhaps you forgot to call \
`set_absolute_descendants`?")
}
None => panic!(
"Link to containing block not established; perhaps you forgot to call \
`set_absolute_descendants`?"
),
Some(ref link) => {
let flow = link.upgrade().unwrap();
flow.generated_containing_block_size(for_flow)
}
},
}
}
#[inline]
pub fn explicit_block_containing_size(&self, shared_context: &SharedStyleContext) -> Option<Au> {
pub fn explicit_block_containing_size(
&self,
shared_context: &SharedStyleContext,
) -> Option<Au> {
match self.link {
None => {
panic!("Link to containing block not established; perhaps you forgot to call \
`set_absolute_descendants`?")
}
None => panic!(
"Link to containing block not established; perhaps you forgot to call \
`set_absolute_descendants`?"
),
Some(ref link) => {
let flow = link.upgrade().unwrap();
if flow.is_block_like() {
flow.as_block().explicit_block_containing_size(shared_context)
flow.as_block()
.explicit_block_containing_size(shared_context)
} else if flow.is_inline_flow() {
Some(flow.as_inline().minimum_line_metrics.space_above_baseline)
} else {
None
}
}
},
}
}
}

View file

@ -38,10 +38,13 @@ impl Serialize for FlowList {
FlowClass::TableRow => to_value(f.as_table_row()).unwrap(),
FlowClass::TableCell => to_value(f.as_table_cell()).unwrap(),
FlowClass::Flex => to_value(f.as_flex()).unwrap(),
FlowClass::ListItem | FlowClass::TableColGroup | FlowClass::TableCaption |
FlowClass::Multicol | FlowClass::MulticolColumn => {
FlowClass::ListItem |
FlowClass::TableColGroup |
FlowClass::TableCaption |
FlowClass::Multicol |
FlowClass::MulticolColumn => {
Value::Null // Not implemented yet
}
},
};
flow_val.insert("data".to_owned(), data);
serializer.serialize_element(&flow_val)?;
@ -152,7 +155,7 @@ impl FlowList {
#[inline]
pub fn split_off(&mut self, i: usize) -> Self {
FlowList {
flows: self.flows.split_off(i)
flows: self.flows.split_off(i),
}
}
}

View file

@ -8,7 +8,6 @@
//! be superfluous. This design is largely duplicating logic of Arc<T> and
//! Weak<T>; please see comments there for details.
use flow::Flow;
use std::ops::Deref;
use std::sync::{Arc, Weak};
@ -63,4 +62,3 @@ impl WeakFlowRef {
self.0.upgrade().map(FlowRef)
}
}

File diff suppressed because it is too large Load diff

View file

@ -25,73 +25,103 @@ use text::TextRunScanner;
use traversal::InorderFlowTraversal;
// Decimal styles per CSS-COUNTER-STYLES § 6.1:
static DECIMAL: [char; 10] = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ];
static DECIMAL: [char; 10] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
// TODO(pcwalton): `decimal-leading-zero`
static ARABIC_INDIC: [char; 10] = [ '٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩' ];
static ARABIC_INDIC: [char; 10] = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
// TODO(pcwalton): `armenian`, `upper-armenian`, `lower-armenian`
static BENGALI: [char; 10] = [ '', '১', '২', '৩', '', '৫', '৬', '', '৮', '৯' ];
static CAMBODIAN: [char; 10] = [ '០', '១', '២', '៣', '៤', '៥', '៦', '៧', '៨', '៩' ];
static BENGALI: [char; 10] = [
'', '১', '২', '৩', '', '৫', '৬', '', '৮', '৯',
];
static CAMBODIAN: [char; 10] = [
'០', '១', '២', '៣', '៤', '៥', '៦', '៧', '៨', '៩',
];
// TODO(pcwalton): Suffix for CJK decimal.
static CJK_DECIMAL: [char; 10] = [ '', '一', '二', '三', '四', '五', '六', '七', '八', '九' ];
static DEVANAGARI: [char; 10] = [ '', '१', '२', '३', '४', '५', '६', '७', '८', '९' ];
static CJK_DECIMAL: [char; 10] = [
'', '一', '二', '三', '四', '五', '六', '七', '八', '九',
];
static DEVANAGARI: [char; 10] = [
'', '१', '२', '३', '४', '५', '६', '७', '८', '९',
];
// TODO(pcwalton): `georgian`
static GUJARATI: [char; 10] = ['', '૧', '૨', '૩', '૪', '૫', '૬', '૭', '૮', '૯'];
static GURMUKHI: [char; 10] = ['', '', '੨', '੩', '', '੫', '੬', '੭', '੮', '੯'];
static GUJARATI: [char; 10] = [
'', '૧', '૨', '૩', '૪', '૫', '૬', '૭', '૮', '૯',
];
static GURMUKHI: [char; 10] = [
'', '', '੨', '੩', '', '੫', '੬', '੭', '੮', '੯',
];
// TODO(pcwalton): `hebrew`
static KANNADA: [char; 10] = ['', '೧', '೨', '೩', '೪', '೫', '೬', '೭', '೮', '೯'];
static LAO: [char; 10] = ['', '໑', '໒', '໓', '໔', '໕', '໖', '໗', '໘', '໙'];
static MALAYALAM: [char; 10] = ['', '൧', '൨', '൩', '൪', '൫', '൬', '', '൮', '൯'];
static MONGOLIAN: [char; 10] = ['᠐', '᠑', '᠒', '᠓', '᠔', '᠕', '᠖', '᠗', '᠘', '᠙'];
static MYANMAR: [char; 10] = ['', '၁', '၂', '၃', '၄', '၅', '၆', '၇', '၈', '၉'];
static ORIYA: [char; 10] = ['', '୧', '', '୩', '୪', '୫', '୬', '୭', '୮', '୯'];
static KANNADA: [char; 10] = [
'', '೧', '೨', '೩', '೪', '೫', '೬', '೭', '೮', '೯',
];
static LAO: [char; 10] = [
'', '໑', '໒', '໓', '໔', '໕', '໖', '໗', '໘', '໙',
];
static MALAYALAM: [char; 10] = [
'', '൧', '൨', '൩', '൪', '൫', '൬', '', '൮', '൯',
];
static MONGOLIAN: [char; 10] = [
'᠐', '᠑', '᠒', '᠓', '᠔', '᠕', '᠖', '᠗', '᠘', '᠙',
];
static MYANMAR: [char; 10] = [
'', '၁', '၂', '၃', '၄', '၅', '၆', '၇', '၈', '၉',
];
static ORIYA: [char; 10] = [
'', '୧', '', '୩', '୪', '୫', '୬', '୭', '୮', '୯',
];
static PERSIAN: [char; 10] = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
// TODO(pcwalton): `lower-roman`, `upper-roman`
static TELUGU: [char; 10] = ['', '౧', '౨', '౩', '౪', '౫', '౬', '౭', '౮', '౯'];
static THAI: [char; 10] = ['', '๑', '๒', '๓', '๔', '๕', '๖', '๗', '๘', '๙'];
static TIBETAN: [char; 10] = ['༠', '༡', '༢', '༣', '༤', '༥', '༦', '༧', '༨', '༩'];
static TELUGU: [char; 10] = [
'', '౧', '౨', '౩', '౪', '౫', '౬', '౭', '౮', '౯',
];
static THAI: [char; 10] = [
'', '๑', '๒', '๓', '๔', '๕', '๖', '๗', '๘', '๙',
];
static TIBETAN: [char; 10] = [
'༠', '༡', '༢', '༣', '༤', '༥', '༦', '༧', '༨', '༩',
];
// Alphabetic styles per CSS-COUNTER-STYLES § 6.2:
static LOWER_ALPHA: [char; 26] = [
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
't', 'u', 'v', 'w', 'x', 'y', 'z'
't', 'u', 'v', 'w', 'x', 'y', 'z',
];
static UPPER_ALPHA: [char; 26] = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
];
static CJK_EARTHLY_BRANCH: [char; 12] = [
'子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'
'子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥',
];
static CJK_HEAVENLY_STEM: [char; 10] = [
'甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'
'甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸',
];
static LOWER_GREEK: [char; 24] = [
'α', 'β', 'γ', 'δ', 'ε', 'ζ', 'η', 'θ', 'ι', 'κ', 'λ', 'μ', 'ν', 'ξ', 'ο', 'π', 'ρ', 'σ', 'τ',
'υ', 'φ', 'χ', 'ψ', 'ω'
'α', 'β', 'γ', 'δ', 'ε', 'ζ', 'η', 'θ', 'ι', 'κ', 'λ', 'μ', 'ν', 'ξ', 'ο', 'π',
'ρ', 'σ', 'τ', 'υ', 'φ', 'χ', 'ψ', 'ω',
];
static HIRAGANA: [char; 48] = [
'あ', 'い', 'う', 'え', 'お', 'か', 'き', 'く', 'け', 'こ', 'さ', 'し', 'す', 'せ', 'そ',
'た', 'ち', 'つ', 'て', 'と', 'な', 'に', 'ぬ', 'ね', 'の', 'は', 'ひ', 'ふ', 'へ', 'ほ',
'ま', 'み', 'む', 'め', 'も', 'や', 'ゆ', 'よ', 'ら', 'り', 'る', 'れ', 'ろ',
'わ', 'ゐ', 'ゑ', 'を', 'ん'
'あ', 'い', 'う', 'え', 'お', 'か', 'き', 'く', 'け', 'こ', 'さ', 'し', 'す',
'せ', 'そ', 'た', 'ち', 'つ', 'て', 'と', 'な', 'に', 'ぬ', 'ね', 'の', 'は',
'ひ', 'ふ', 'へ', 'ほ', 'ま', 'み', 'む', 'め', 'も', 'や', 'ゆ', 'よ', 'ら',
'り', 'る', 'れ', 'ろ', 'わ', 'ゐ', 'ゑ', 'を', 'ん',
];
static HIRAGANA_IROHA: [char; 47] = [
'い', 'ろ', 'は', 'に', 'ほ', 'へ', 'と', 'ち', 'り', 'ぬ', 'る', 'を', 'わ', 'か', 'よ',
'た', 'れ', 'そ', 'つ', 'ね', 'な', 'ら', 'む', 'う', 'ゐ', 'の', 'お', 'く', 'や', 'ま',
'け', 'ふ', 'こ', 'え', 'て', 'あ', 'さ', 'き', 'ゆ', 'め', 'み', 'し', 'ゑ',
'ひ', 'も', 'せ', 'す'
'い', 'ろ', 'は', 'に', 'ほ', 'へ', 'と', 'ち', 'り', 'ぬ', 'る', 'を', 'わ',
'か', 'よ', 'た', 'れ', 'そ', 'つ', 'ね', 'な', 'ら', 'む', 'う', 'ゐ', 'の',
'お', 'く', 'や', 'ま', 'け', 'ふ', 'こ', 'え', 'て', 'あ', 'さ', 'き', 'ゆ',
'め', 'み', 'し', 'ゑ', 'ひ', 'も', 'せ', 'す',
];
static KATAKANA: [char; 48] = [
'ア', 'イ', 'ウ', 'エ', 'オ', 'カ', 'キ', 'ク', 'ケ', 'コ', 'サ', 'シ', 'ス', 'セ', 'ソ',
'タ', 'チ', 'ツ', 'テ', 'ト', 'ナ', 'ニ', 'ヌ', 'ネ', '', 'ハ', 'ヒ', 'フ', 'ヘ', 'ホ',
'マ', 'ミ', 'ム', 'メ', 'モ', 'ヤ', 'ユ', 'ヨ', 'ラ', 'リ', 'ル', 'レ', 'ロ',
'ワ', 'ヰ', 'ヱ', 'ヲ', 'ン'
'ア', 'イ', 'ウ', 'エ', 'オ', 'カ', 'キ', 'ク', 'ケ', 'コ', 'サ', 'シ', 'ス',
'セ', 'ソ', 'タ', 'チ', 'ツ', 'テ', 'ト', 'ナ', 'ニ', 'ヌ', 'ネ', '', 'ハ',
'ヒ', 'フ', 'ヘ', 'ホ', 'マ', 'ミ', 'ム', 'メ', 'モ', 'ヤ', 'ユ', 'ヨ', 'ラ',
'リ', 'ル', 'レ', 'ロ', 'ワ', 'ヰ', 'ヱ', 'ヲ', 'ン',
];
static KATAKANA_IROHA: [char; 47] = [
'イ', 'ロ', 'ハ', 'ニ', 'ホ', 'ヘ', 'ト', 'チ', 'リ', 'ヌ', 'ル', 'ヲ', 'ワ', 'カ', 'ヨ',
'タ', 'レ', 'ソ', 'ツ', 'ネ', 'ナ', 'ラ', 'ム', 'ウ', 'ヰ', '', 'オ', 'ク', 'ヤ', 'マ',
'ケ', 'フ', 'コ', 'エ', 'テ', 'ア', 'サ', 'キ', 'ユ', 'メ', 'ミ', 'シ', 'ヱ',
'ヒ', 'モ', 'セ', 'ス'
'イ', 'ロ', 'ハ', 'ニ', 'ホ', 'ヘ', 'ト', 'チ', 'リ', 'ヌ', 'ル', 'ヲ', 'ワ',
'カ', 'ヨ', 'タ', 'レ', 'ソ', 'ツ', 'ネ', 'ナ', 'ラ', 'ム', 'ウ', 'ヰ', '',
'オ', 'ク', 'ヤ', 'マ', 'ケ', 'フ', 'コ', 'エ', 'テ', 'ア', 'サ', 'キ', 'ユ',
'メ', 'ミ', 'シ', 'ヱ', 'ヒ', 'モ', 'セ', 'ス',
];
/// The generated content resolution traversal.
@ -132,8 +162,12 @@ impl<'a> InorderFlowTraversal for ResolveGeneratedContent<'a> {
#[inline]
fn should_process_subtree(&mut self, flow: &mut Flow) -> bool {
flow.base().restyle_damage.intersects(ServoRestyleDamage::RESOLVE_GENERATED_CONTENT) ||
flow.base().flags.intersects(FlowFlags::AFFECTS_COUNTERS | FlowFlags::HAS_COUNTER_AFFECTING_CHILDREN)
flow.base()
.restyle_damage
.intersects(ServoRestyleDamage::RESOLVE_GENERATED_CONTENT) ||
flow.base().flags.intersects(
FlowFlags::AFFECTS_COUNTERS | FlowFlags::HAS_COUNTER_AFFECTING_CHILDREN,
)
}
}
@ -168,81 +202,97 @@ impl<'a, 'b> ResolveGeneratedContentFragmentMutator<'a, 'b> {
if let SpecificFragmentInfo::GeneratedContent(ref mut info) = fragment.specific {
info
} else {
return
return;
};
match **info {
GeneratedContentInfo::ListItem => {
new_info = self.traversal.list_item.render(self.traversal.layout_context,
fragment.node,
fragment.pseudo.clone(),
fragment.style.clone(),
list_style_type,
RenderingMode::Suffix(".\u{00a0}"))
}
new_info = self.traversal.list_item.render(
self.traversal.layout_context,
fragment.node,
fragment.pseudo.clone(),
fragment.style.clone(),
list_style_type,
RenderingMode::Suffix(".\u{00a0}"),
)
},
GeneratedContentInfo::Empty |
GeneratedContentInfo::ContentItem(ContentItem::String(_)) => {
// Nothing to do here.
}
GeneratedContentInfo::ContentItem(ContentItem::Counter(ref counter_name,
counter_style)) => {
},
GeneratedContentInfo::ContentItem(ContentItem::Counter(
ref counter_name,
counter_style,
)) => {
let temporary_counter = Counter::new();
let counter = self.traversal
let counter = self
.traversal
.counters
.get(&*counter_name.0)
.unwrap_or(&temporary_counter);
new_info = counter.render(self.traversal.layout_context,
fragment.node,
fragment.pseudo.clone(),
fragment.style.clone(),
counter_style,
RenderingMode::Plain)
}
GeneratedContentInfo::ContentItem(ContentItem::Counters(ref counter_name,
ref separator,
counter_style)) => {
new_info = counter.render(
self.traversal.layout_context,
fragment.node,
fragment.pseudo.clone(),
fragment.style.clone(),
counter_style,
RenderingMode::Plain,
)
},
GeneratedContentInfo::ContentItem(ContentItem::Counters(
ref counter_name,
ref separator,
counter_style,
)) => {
let temporary_counter = Counter::new();
let counter = self.traversal
let counter = self
.traversal
.counters
.get(&*counter_name.0)
.unwrap_or(&temporary_counter);
new_info = counter.render(self.traversal.layout_context,
fragment.node,
fragment.pseudo,
fragment.style.clone(),
counter_style,
RenderingMode::All(&separator));
}
new_info = counter.render(
self.traversal.layout_context,
fragment.node,
fragment.pseudo,
fragment.style.clone(),
counter_style,
RenderingMode::All(&separator),
);
},
GeneratedContentInfo::ContentItem(ContentItem::OpenQuote) => {
new_info = render_text(self.traversal.layout_context,
fragment.node,
fragment.pseudo,
fragment.style.clone(),
self.quote(&*fragment.style, false));
new_info = render_text(
self.traversal.layout_context,
fragment.node,
fragment.pseudo,
fragment.style.clone(),
self.quote(&*fragment.style, false),
);
self.traversal.quote += 1
}
},
GeneratedContentInfo::ContentItem(ContentItem::CloseQuote) => {
if self.traversal.quote >= 1 {
self.traversal.quote -= 1
}
new_info = render_text(self.traversal.layout_context,
fragment.node,
fragment.pseudo,
fragment.style.clone(),
self.quote(&*fragment.style, true));
}
new_info = render_text(
self.traversal.layout_context,
fragment.node,
fragment.pseudo,
fragment.style.clone(),
self.quote(&*fragment.style, true),
);
},
GeneratedContentInfo::ContentItem(ContentItem::NoOpenQuote) => {
self.traversal.quote += 1
}
},
GeneratedContentInfo::ContentItem(ContentItem::NoCloseQuote) => {
if self.traversal.quote >= 1 {
self.traversal.quote -= 1
}
}
},
GeneratedContentInfo::ContentItem(ContentItem::Url(..)) => {
unreachable!("Servo doesn't parse content: url(..) yet")
}
},
}
};
@ -252,7 +302,7 @@ impl<'a, 'b> ResolveGeneratedContentFragmentMutator<'a, 'b> {
// so that it isn't processed again on the next layout. FIXME (mbrubeck): When
// processing an inline flow, this traversal should be allowed to insert or remove
// fragments. Then we can just remove these fragments rather than adding placeholders.
None => SpecificFragmentInfo::GeneratedContent(Box::new(GeneratedContentInfo::Empty))
None => SpecificFragmentInfo::GeneratedContent(Box::new(GeneratedContentInfo::Empty)),
};
}
@ -263,9 +313,12 @@ impl<'a, 'b> ResolveGeneratedContentFragmentMutator<'a, 'b> {
}
match list_style_type {
ListStyleType::Disc | ListStyleType::None | ListStyleType::Circle |
ListStyleType::Square | ListStyleType::DisclosureOpen |
ListStyleType::DisclosureClosed => {}
ListStyleType::Disc |
ListStyleType::None |
ListStyleType::Circle |
ListStyleType::Square |
ListStyleType::DisclosureOpen |
ListStyleType::DisclosureClosed => {},
_ => self.traversal.list_item.increment(self.level, 1),
}
@ -278,25 +331,29 @@ impl<'a, 'b> ResolveGeneratedContentFragmentMutator<'a, 'b> {
for pair in &*fragment.style().get_counters().counter_reset {
let counter_name = &*pair.name.0;
if let Some(ref mut counter) = self.traversal.counters.get_mut(counter_name) {
counter.reset(self.level, pair.value);
continue
counter.reset(self.level, pair.value);
continue;
}
let mut counter = Counter::new();
counter.reset(self.level, pair.value);
self.traversal.counters.insert(counter_name.to_owned(), counter);
self.traversal
.counters
.insert(counter_name.to_owned(), counter);
}
for pair in &*fragment.style().get_counters().counter_increment {
let counter_name = &*pair.name.0;
if let Some(ref mut counter) = self.traversal.counters.get_mut(counter_name) {
counter.increment(self.level, pair.value);
continue
continue;
}
let mut counter = Counter::new();
counter.increment(self.level, pair.value);
self.traversal.counters.insert(counter_name.to_owned(), counter);
self.traversal
.counters
.insert(counter_name.to_owned(), counter);
}
self.incremented = true
@ -305,14 +362,14 @@ impl<'a, 'b> ResolveGeneratedContentFragmentMutator<'a, 'b> {
fn quote(&self, style: &ComputedValues, close: bool) -> String {
let quotes = &style.get_list().quotes;
if quotes.0.is_empty() {
return String::new()
return String::new();
}
let &(ref open_quote, ref close_quote) =
if self.traversal.quote as usize >= quotes.0.len() {
quotes.0.last().unwrap()
} else {
&quotes.0[self.traversal.quote as usize]
};
let &(ref open_quote, ref close_quote) = if self.traversal.quote as usize >= quotes.0.len()
{
quotes.0.last().unwrap()
} else {
&quotes.0[self.traversal.quote as usize]
};
if close {
close_quote.to_string()
} else {
@ -329,9 +386,7 @@ struct Counter {
impl Counter {
fn new() -> Counter {
Counter {
values: Vec::new(),
}
Counter { values: Vec::new() }
}
fn reset(&mut self, level: u32, value: i32) {
@ -339,7 +394,7 @@ impl Counter {
if let Some(ref mut existing_value) = self.values.last_mut() {
if level == existing_value.level {
existing_value.value = value;
return
return;
}
}
@ -359,7 +414,7 @@ impl Counter {
fn increment(&mut self, level: u32, amount: i32) {
if let Some(ref mut value) = self.values.last_mut() {
value.value += amount;
return
return;
}
self.values.push(CounterValue {
@ -368,14 +423,15 @@ impl Counter {
})
}
fn render(&self,
layout_context: &LayoutContext,
node: OpaqueNode,
pseudo: PseudoElementType,
style: ::ServoArc<ComputedValues>,
list_style_type: ListStyleType,
mode: RenderingMode)
-> Option<SpecificFragmentInfo> {
fn render(
&self,
layout_context: &LayoutContext,
node: OpaqueNode,
pseudo: PseudoElementType,
style: ::ServoArc<ComputedValues>,
list_style_type: ListStyleType,
mode: RenderingMode,
) -> Option<SpecificFragmentInfo> {
let mut string = String::new();
match mode {
RenderingMode::Plain => {
@ -384,7 +440,7 @@ impl Counter {
None => 0,
};
push_representation(value, list_style_type, &mut string)
}
},
RenderingMode::Suffix(suffix) => {
let value = match self.values.last() {
Some(ref value) => value.value,
@ -392,7 +448,7 @@ impl Counter {
};
push_representation(value, list_style_type, &mut string);
string.push_str(suffix)
}
},
RenderingMode::All(separator) => {
let mut first = true;
for value in &self.values {
@ -402,7 +458,7 @@ impl Counter {
first = false;
push_representation(value.value, list_style_type, &mut string)
}
}
},
}
if string.is_empty() {
@ -432,22 +488,26 @@ struct CounterValue {
}
/// Creates fragment info for a literal string.
fn render_text(layout_context: &LayoutContext,
node: OpaqueNode,
pseudo: PseudoElementType,
style: ::ServoArc<ComputedValues>,
string: String)
-> Option<SpecificFragmentInfo> {
fn render_text(
layout_context: &LayoutContext,
node: OpaqueNode,
pseudo: PseudoElementType,
style: ::ServoArc<ComputedValues>,
string: String,
) -> Option<SpecificFragmentInfo> {
let mut fragments = LinkedList::new();
let info = SpecificFragmentInfo::UnscannedText(
Box::new(UnscannedTextFragmentInfo::new(string.into_boxed_str(), None))
);
fragments.push_back(Fragment::from_opaque_node_and_style(node,
pseudo,
style.clone(),
style,
RestyleDamage::rebuild_and_reflow(),
info));
let info = SpecificFragmentInfo::UnscannedText(Box::new(UnscannedTextFragmentInfo::new(
string.into_boxed_str(),
None,
)));
fragments.push_back(Fragment::from_opaque_node_and_style(
node,
pseudo,
style.clone(),
style,
RestyleDamage::rebuild_and_reflow(),
info,
));
// FIXME(pcwalton): This should properly handle multiple marker fragments. This could happen
// due to text run splitting.
let fragments = with_thread_local_font_context(layout_context, |font_context| {
@ -464,39 +524,28 @@ fn render_text(layout_context: &LayoutContext,
/// `list-style-type` onto the given string.
fn push_representation(value: i32, list_style_type: ListStyleType, accumulator: &mut String) {
match list_style_type {
ListStyleType::None => {}
ListStyleType::None => {},
ListStyleType::Disc |
ListStyleType::Circle |
ListStyleType::Square |
ListStyleType::DisclosureOpen |
ListStyleType::DisclosureClosed => {
accumulator.push(static_representation(list_style_type))
}
ListStyleType::DisclosureClosed => accumulator.push(static_representation(list_style_type)),
ListStyleType::Decimal => push_numeric_representation(value, &DECIMAL, accumulator),
ListStyleType::ArabicIndic => {
push_numeric_representation(value, &ARABIC_INDIC, accumulator)
}
},
ListStyleType::Bengali => push_numeric_representation(value, &BENGALI, accumulator),
ListStyleType::Cambodian |
ListStyleType::Khmer => {
ListStyleType::Cambodian | ListStyleType::Khmer => {
push_numeric_representation(value, &CAMBODIAN, accumulator)
}
ListStyleType::CjkDecimal => {
push_numeric_representation(value, &CJK_DECIMAL, accumulator)
}
ListStyleType::Devanagari => {
push_numeric_representation(value, &DEVANAGARI, accumulator)
}
},
ListStyleType::CjkDecimal => push_numeric_representation(value, &CJK_DECIMAL, accumulator),
ListStyleType::Devanagari => push_numeric_representation(value, &DEVANAGARI, accumulator),
ListStyleType::Gujarati => push_numeric_representation(value, &GUJARATI, accumulator),
ListStyleType::Gurmukhi => push_numeric_representation(value, &GURMUKHI, accumulator),
ListStyleType::Kannada => push_numeric_representation(value, &KANNADA, accumulator),
ListStyleType::Lao => push_numeric_representation(value, &LAO, accumulator),
ListStyleType::Malayalam => {
push_numeric_representation(value, &MALAYALAM, accumulator)
}
ListStyleType::Mongolian => {
push_numeric_representation(value, &MONGOLIAN, accumulator)
}
ListStyleType::Malayalam => push_numeric_representation(value, &MALAYALAM, accumulator),
ListStyleType::Mongolian => push_numeric_representation(value, &MONGOLIAN, accumulator),
ListStyleType::Myanmar => push_numeric_representation(value, &MYANMAR, accumulator),
ListStyleType::Oriya => push_numeric_representation(value, &ORIYA, accumulator),
ListStyleType::Persian => push_numeric_representation(value, &PERSIAN, accumulator),
@ -505,31 +554,27 @@ fn push_representation(value: i32, list_style_type: ListStyleType, accumulator:
ListStyleType::Tibetan => push_numeric_representation(value, &TIBETAN, accumulator),
ListStyleType::LowerAlpha => {
push_alphabetic_representation(value, &LOWER_ALPHA, accumulator)
}
},
ListStyleType::UpperAlpha => {
push_alphabetic_representation(value, &UPPER_ALPHA, accumulator)
}
},
ListStyleType::CjkEarthlyBranch => {
push_alphabetic_representation(value, &CJK_EARTHLY_BRANCH, accumulator)
}
},
ListStyleType::CjkHeavenlyStem => {
push_alphabetic_representation(value, &CJK_HEAVENLY_STEM, accumulator)
}
},
ListStyleType::LowerGreek => {
push_alphabetic_representation(value, &LOWER_GREEK, accumulator)
}
ListStyleType::Hiragana => {
push_alphabetic_representation(value, &HIRAGANA, accumulator)
}
},
ListStyleType::Hiragana => push_alphabetic_representation(value, &HIRAGANA, accumulator),
ListStyleType::HiraganaIroha => {
push_alphabetic_representation(value, &HIRAGANA_IROHA, accumulator)
}
ListStyleType::Katakana => {
push_alphabetic_representation(value, &KATAKANA, accumulator)
}
},
ListStyleType::Katakana => push_alphabetic_representation(value, &KATAKANA, accumulator),
ListStyleType::KatakanaIroha => {
push_alphabetic_representation(value, &KATAKANA_IROHA, accumulator)
}
},
}
}
@ -572,7 +617,7 @@ fn push_numeric_representation(value: i32, system: &[char], accumulator: &mut St
// Step 1.
if abs_value == 0 {
accumulator.push(system[0]);
return
return;
}
// Step 2.

View file

@ -11,7 +11,7 @@ use style::servo::restyle_damage::ServoRestyleDamage;
#[derive(Clone, Copy, PartialEq)]
pub enum RelayoutMode {
Incremental,
Force
Force,
}
bitflags! {
@ -30,7 +30,10 @@ pub trait LayoutDamageComputation {
impl<'a> LayoutDamageComputation for &'a mut Flow {
fn compute_layout_damage(self) -> SpecialRestyleDamage {
let mut special_damage = SpecialRestyleDamage::empty();
let is_absolutely_positioned = self.base().flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
let is_absolutely_positioned = self
.base()
.flags
.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
// In addition to damage, we use this phase to compute whether nodes affect CSS counters.
let mut has_counter_affecting_children = false;
@ -41,35 +44,47 @@ impl<'a> LayoutDamageComputation for &'a mut Flow {
let parent_damage = self_base.restyle_damage;
for kid in self_base.children.iter_mut() {
let child_is_absolutely_positioned =
kid.base().flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
let child_is_absolutely_positioned = kid
.base()
.flags
.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
kid.mut_base().restyle_damage.insert(
parent_damage.damage_for_child(is_absolutely_positioned,
child_is_absolutely_positioned));
parent_damage
.damage_for_child(is_absolutely_positioned, child_is_absolutely_positioned),
);
{
let kid: &mut Flow = kid;
special_damage.insert(kid.compute_layout_damage());
}
self_base.restyle_damage
.insert(kid.base().restyle_damage.damage_for_parent(
child_is_absolutely_positioned));
self_base.restyle_damage.insert(
kid.base()
.restyle_damage
.damage_for_parent(child_is_absolutely_positioned),
);
has_counter_affecting_children = has_counter_affecting_children ||
kid.base().flags.intersects(FlowFlags::AFFECTS_COUNTERS |
FlowFlags::HAS_COUNTER_AFFECTING_CHILDREN);
has_counter_affecting_children =
has_counter_affecting_children || kid.base().flags.intersects(
FlowFlags::AFFECTS_COUNTERS | FlowFlags::HAS_COUNTER_AFFECTING_CHILDREN,
);
}
}
let self_base = self.mut_base();
if self_base.flags.float_kind() != Float::None &&
self_base.restyle_damage.intersects(ServoRestyleDamage::REFLOW) {
if self_base.flags.float_kind() != Float::None && self_base
.restyle_damage
.intersects(ServoRestyleDamage::REFLOW)
{
special_damage.insert(SpecialRestyleDamage::REFLOW_ENTIRE_DOCUMENT);
}
if has_counter_affecting_children {
self_base.flags.insert(FlowFlags::HAS_COUNTER_AFFECTING_CHILDREN)
self_base
.flags
.insert(FlowFlags::HAS_COUNTER_AFFECTING_CHILDREN)
} else {
self_base.flags.remove(FlowFlags::HAS_COUNTER_AFFECTING_CHILDREN)
self_base
.flags
.remove(FlowFlags::HAS_COUNTER_AFFECTING_CHILDREN)
}
special_damage
@ -77,8 +92,12 @@ impl<'a> LayoutDamageComputation for &'a mut Flow {
fn reflow_entire_document(self) {
let self_base = self.mut_base();
self_base.restyle_damage.insert(RestyleDamage::rebuild_and_reflow());
self_base.restyle_damage.remove(ServoRestyleDamage::RECONSTRUCT_FLOW);
self_base
.restyle_damage
.insert(RestyleDamage::rebuild_and_reflow());
self_base
.restyle_damage
.remove(ServoRestyleDamage::RECONSTRUCT_FLOW);
for kid in self_base.children.iter_mut() {
kid.reflow_entire_document();
}

File diff suppressed because it is too large Load diff

View file

@ -47,7 +47,7 @@ impl ScopeData {
name: name,
pre: pre,
post: Value::Null,
children: vec!(),
children: vec![],
}
}
}

Some files were not shown because too many files have changed in this diff Show more