tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 93090c66ae0be7aeb42653ab809bdb5291eb7fa4
parent a768b8ec470f48159b3b84d287fc82bbb6ae5c03
Author: Glenn Watson <git@chillybin.org>
Date:   Mon,  6 Oct 2025 19:31:55 +0000

Bug 1992049 - Add initial wrshell command to convert a WR capture to a wrench scene r=gfx-reviewers,lsalzman

Differential Revision: https://phabricator.services.mozilla.com/D267153

Diffstat:
Mgfx/wr/webrender/src/lib.rs | 2+-
Mgfx/wr/wrshell/Cargo.lock | 481+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgfx/wr/wrshell/Cargo.toml | 2++
Mgfx/wr/wrshell/src/main.rs | 3+++
Agfx/wr/wrshell/src/script_commands.rs | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agfx/wr/wrshell/src/wrench.rs | 701+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 1266 insertions(+), 1 deletion(-)

diff --git a/gfx/wr/webrender/src/lib.rs b/gfx/wr/webrender/src/lib.rs @@ -121,7 +121,7 @@ mod render_task_cache; mod render_task; mod renderer; mod resource_cache; -mod scene; +pub mod scene; mod scene_builder_thread; mod scene_building; mod screen_capture; diff --git a/gfx/wr/wrshell/Cargo.lock b/gfx/wr/wrshell/Cargo.lock @@ -27,6 +27,15 @@ dependencies = [ ] [[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +dependencies = [ + "serde", +] + +[[package]] name = "anstream" version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -119,6 +128,12 @@ dependencies = [ ] [[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + +[[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -152,6 +167,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -176,6 +200,17 @@ dependencies = [ ] [[package]] +name = "build-parallel" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e3ff9db740167616e528c509b3618046fc05d337f8f3182d300f4aa977d2bb" +dependencies = [ + "crossbeam-utils", + "jobserver", + "num_cpus", +] + +[[package]] name = "bumpalo" version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -209,6 +244,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] +name = "chunked_transfer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" + +[[package]] name = "clap" version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -262,6 +303,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.2.1", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.2.1", + "core-foundation", + "libc", +] + +[[package]] +name = "core-text" +version = "20.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + +[[package]] name = "cpufeatures" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -280,6 +373,25 @@ dependencies = [ ] [[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -302,6 +414,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -344,6 +476,26 @@ dependencies = [ ] [[package]] +name = "dwrote" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c93d234bac0cdd0e2ac08bc8a5133f8df2169e95b262dfcea5e5cb7855672f" +dependencies = [ + "lazy_static", + "libc", + "serde", + "serde_derive", + "winapi", + "wio", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] name = "endian-type" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -360,6 +512,17 @@ dependencies = [ ] [[package]] +name = "etagere" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" +dependencies = [ + "euclid", + "serde", + "svg_fmt", +] + +[[package]] name = "euclid" version = "0.22.11" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -386,6 +549,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] name = "form_urlencoded" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -395,6 +585,27 @@ dependencies = [ ] [[package]] +name = "freetype" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a440748e063798e4893ceb877151e84acef9bea9a8c6800645cf3f1b3a7806e" +dependencies = [ + "freetype-sys", + "libc", +] + +[[package]] +name = "freetype-sys" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] name = "futures-channel" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -484,6 +695,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "gleam" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8647cc2e2ffde598ce5ca2809452e722dd8dc127885ab8aba2fa8b469cd3ed94" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "glslopt" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913662ae8335df058d56e00f11340b20fa82e03e0276587797ef325ab01e50d4" +dependencies = [ + "cc", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] name = "http" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -524,6 +770,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] name = "hyper" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -748,6 +1000,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + +[[package]] name = "js-sys" version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -758,6 +1020,12 @@ dependencies = [ ] [[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -811,6 +1079,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] name = "malloc_size_of_derive" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -894,6 +1171,25 @@ dependencies = [ ] [[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] name = "object" version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -980,6 +1276,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] +name = "plane-split" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f7d82649829ecdef8e258790b0587acf0a8403f0ce963473d8e918acc1643" +dependencies = [ + "euclid", + "log", + "smallvec", +] + +[[package]] name = "potential_utf" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1061,6 +1368,26 @@ dependencies = [ ] [[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] name = "redox_syscall" version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1157,6 +1484,32 @@ dependencies = [ ] [[package]] +name = "ron" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beceb6f7bf81c73e73aeef6dd1356d9a1b2b4909e1f0fc3e59b034f9572d7b7f" +dependencies = [ + "base64", + "bitflags 2.9.2", + "serde", + "serde_derive", + "unicode-ident", +] + +[[package]] +name = "ron" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db09040cc89e461f1a265139777a2bde7f8d8c67c4936f700c63ce3e2904d468" +dependencies = [ + "base64", + "bitflags 2.9.2", + "serde", + "serde_derive", + "unicode-ident", +] + +[[package]] name = "roxmltree" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1175,6 +1528,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] name = "rustversion" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1348,6 +1707,9 @@ name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] [[package]] name = "socket2" @@ -1384,6 +1746,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] +name = "svg_fmt" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" + +[[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1466,6 +1834,18 @@ dependencies = [ ] [[package]] +name = "tiny_http" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" +dependencies = [ + "ascii", + "chunked_transfer", + "httpdate", + "log", +] + +[[package]] name = "tinystr" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1492,6 +1872,12 @@ dependencies = [ ] [[package]] +name = "topological-sort" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa7c7f42dea4b1b99439786f5633aeb9c14c1b53f75e282803c2ec2ad545873c" + +[[package]] name = "tower" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1556,6 +1942,12 @@ dependencies = [ ] [[package]] +name = "tracy-rs" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce607aae8ab0ab3abf3a2723a9ab6f09bb8639ed83fdd888d857b8e556c868d8" + +[[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1743,6 +2135,46 @@ dependencies = [ ] [[package]] +name = "webrender" +version = "0.62.0" +dependencies = [ + "allocator-api2", + "base64", + "bincode", + "bitflags 2.9.2", + "build-parallel", + "byteorder", + "derive_more", + "etagere", + "euclid", + "gleam", + "glslopt", + "lazy_static", + "log", + "malloc_size_of_derive", + "num-traits", + "peek-poke", + "plane-split", + "rayon", + "ron 0.10.1", + "rustc-hash", + "serde", + "serde_json", + "sha1", + "smallvec", + "svg_fmt", + "tiny_http", + "topological-sort", + "tracy-rs", + "url", + "webrender_api", + "webrender_build", + "wr_glyph_rasterizer", + "wr_malloc_size_of", + "zeitstempel", +] + +[[package]] name = "webrender_api" version = "0.62.0" dependencies = [ @@ -1761,6 +2193,14 @@ dependencies = [ ] [[package]] +name = "webrender_build" +version = "0.0.2" +dependencies = [ + "bitflags 2.9.2", + "lazy_static", +] + +[[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1936,6 +2376,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + +[[package]] name = "wit-bindgen-rt" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1945,6 +2394,30 @@ dependencies = [ ] [[package]] +name = "wr_glyph_rasterizer" +version = "0.1.0" +dependencies = [ + "core-foundation", + "core-graphics", + "core-text", + "dwrote", + "euclid", + "freetype", + "lazy_static", + "libc", + "log", + "malloc_size_of_derive", + "objc", + "rayon", + "rustc-hash", + "serde", + "smallvec", + "tracy-rs", + "webrender_api", + "wr_malloc_size_of", +] + +[[package]] name = "wr_malloc_size_of" version = "0.2.2" dependencies = [ @@ -1967,15 +2440,23 @@ dependencies = [ "imgui-sdl3", "repl-ng", "reqwest", + "ron 0.11.0", "sdl3", "serde", "serde_json", "strprox", "tungstenite", + "webrender", "webrender_api", ] [[package]] +name = "xml-rs" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" + +[[package]] name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/gfx/wr/wrshell/Cargo.toml b/gfx/wr/wrshell/Cargo.toml @@ -8,6 +8,7 @@ license = "MPL-2.0" [dependencies] reqwest = { version = "0.12", default-features = false, features = [ "blocking" ] } +webrender = { path = "../webrender", features = [ "debugger", "replay" ] } webrender_api = { path = "../webrender_api", features = [ "debugger" ] } tungstenite = "0.27.0" argh = "0.1" @@ -15,6 +16,7 @@ serde = { version="1", features = ["derive"] } serde_json = "1" strprox = "0.3.3" repl-ng = "0.3" +ron = "0.11.0" imgui-sdl3 = { git = "https://github.com/florianvazelle/imgui-sdl3" } imgui = { version="0.12", features = [ "docking" ] } diff --git a/gfx/wr/wrshell/src/main.rs b/gfx/wr/wrshell/src/main.rs @@ -7,6 +7,8 @@ mod command; mod debug_commands; mod gui; mod net; +mod script_commands; +mod wrench; use argh::FromArgs; use std::str; @@ -50,6 +52,7 @@ fn main() { let mut cmd_list = command::CommandList::new(); debug_commands::register(&mut cmd_list); + script_commands::register(&mut cmd_list); match args.mode { Mode::Repl => { diff --git a/gfx/wr/wrshell/src/script_commands.rs b/gfx/wr/wrshell/src/script_commands.rs @@ -0,0 +1,78 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::command::{Command, CommandDescriptor, CommandList, ParamDescriptor}; +use crate::command::{CommandContext, CommandOutput}; +use crate::wrench; + +use webrender::scene::Scene; + +// Implementation of a basic set of script commands + +// Register the script commands in this source file +pub fn register(cmd_list: &mut CommandList) { + cmd_list.register_command(Box::new(ProcessCaptureCommand)); +} + +pub struct ProcessCaptureCommand; + +impl Command for ProcessCaptureCommand { + fn descriptor(&self) -> CommandDescriptor { + CommandDescriptor { + name: "process-capture", + help: r#" +Process a WR capture directory. + USAGE: process-capture [scene file] [output file] +"#, + params: &[ + ParamDescriptor { + name: "scene", + is_required: true, + }, + ParamDescriptor { + name: "out", + is_required: true, + }, + ], + ..Default::default() + } + } + + fn run( + &mut self, + ctx: &mut CommandContext, + ) -> CommandOutput { + use std::io::Write; + + let source = ctx.arg_string("scene"); + let target = ctx.arg_string("out"); + + println!("Loading scene file '{}'", source); + let scene_file = match std::fs::read_to_string(source) { + Ok(f) => f, + Err(..) => return CommandOutput::Err("\tUnable to read scene".into()), + }; + + println!("Deserialize scene file '{}'", source); + let scene: Scene = match ron::de::from_str(&scene_file) { + Ok(out) => out, + Err(..) => return CommandOutput::Err("\tDeserialization failed".into()), + }; + + let yaml = match wrench::scene_to_yaml(&scene) { + Ok(yaml) => yaml, + Err(err) => return CommandOutput::Err( + format!("\tFailed to convert - {}", err) + ), + }; + + let mut output = match std::fs::File::create(target) { + Ok(f) => f, + Err(..) => return CommandOutput::Err("\tUnable to open output file".into()), + }; + write!(output, "{}", yaml).expect("failed to write yaml"); + + CommandOutput::Log(yaml) + } +} diff --git a/gfx/wr/wrshell/src/wrench.rs b/gfx/wr/wrshell/src/wrench.rs @@ -0,0 +1,701 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use std::collections::HashMap; + +use webrender::scene::Scene; +use webrender_api::{units::LayoutRect, BorderDetails, BorderStyle, BuiltDisplayList}; +use webrender_api::{ColorF, DisplayItem, PipelineId, PropertyBinding, SpatialId}; +use webrender_api::{ClipId, SpatialTreeItem, ClipChainId}; + +fn color_to_string( + color: ColorF, +) -> String { + match (color.r, color.g, color.b, color.a) { + (1.0, 0.0, 0.0, 1.0) => "red".into(), + _ => { + format!("{} {} {} {}", + color.r * 255.0, + color.g * 255.0, + color.b * 255.0, + color.a, + ) + } + } +} + +fn color_to_string_array( + color: ColorF, +) -> String { + match (color.r, color.g, color.b, color.a) { + (1.0, 0.0, 0.0, 1.0) => "red".into(), + _ => { + format!("{}, {}, {}, {}", + color.r * 255.0, + color.g * 255.0, + color.b * 255.0, + color.a, + ) + } + } +} + +fn style_to_string( + style: BorderStyle, +) -> String { + match style { + BorderStyle::None => "none", + BorderStyle::Solid => "solid", + BorderStyle::Double => "double", + BorderStyle::Dotted => "dotted", + BorderStyle::Dashed => "dashed", + BorderStyle::Hidden => "hidden", + BorderStyle::Ridge => "ridge", + BorderStyle::Inset => "inset", + BorderStyle::Outset => "outset", + BorderStyle::Groove => "groove", + }.into() +} + +#[derive(Debug)] +enum SpatialNodeKind { + Reference { + }, + Scroll { + }, + Sticky { + }, +} + +#[derive(Debug)] +struct SpatialNode { + wrench_id: u64, +} + +struct YamlWriter { + out: String, + indent: String, + spatial_nodes: HashMap<SpatialId, SpatialNode>, + clip_id_map: HashMap<ClipId, u64>, + clipchain_id_map: HashMap<ClipChainId, u64>, + next_wrench_id: u64, +} + +impl YamlWriter { + fn new() -> Self { + YamlWriter { + out: String::new(), + indent: String::new(), + spatial_nodes: HashMap::new(), + next_wrench_id: 2, + clip_id_map: HashMap::new(), + clipchain_id_map: HashMap::new(), + } + } + + fn push_level(&mut self) { + self.indent.push_str(" "); + } + + fn pop_level(&mut self) { + self.indent.truncate(self.indent.len() - 2); + } + + fn add_clip_id( + &mut self, + clip_id: ClipId + ) -> u64 { + let id = self.next_wrench_id; + self.next_wrench_id += 1; + + let _prev = self.clip_id_map.insert(clip_id, id); + assert!(_prev.is_none()); + + id + } + + fn add_clipchain_id( + &mut self, + clipchain_id: ClipChainId + ) -> u64 { + let id = self.next_wrench_id; + self.next_wrench_id += 1; + + let _prev = self.clipchain_id_map.insert(clipchain_id, id); + assert!(_prev.is_none()); + + id + } + + fn add_and_write_spatial_node( + &mut self, + spatial_id: SpatialId, + parent: SpatialId, + kind: SpatialNodeKind, + ) { + match kind { + SpatialNodeKind::Reference {} => { + self.write_line("- type: reference-frame"); + self.push_level(); + self.write_line(&format!("id: {}", self.next_wrench_id)); + if let Some(parent) = self.spatial_nodes.get(&parent) { + self.write_line(&format!("spatial-id: {}", parent.wrench_id)); + } + self.pop_level(); + } + SpatialNodeKind::Scroll {} => { + let parent_id = self.spatial_nodes[&parent].wrench_id; + + self.write_line("- type: scroll-frame"); + self.push_level(); + self.write_line(&format!("id: {}", self.next_wrench_id)); + self.write_bounds(LayoutRect::zero()); + self.write_line(&format!("spatial-id: {}", parent_id)); + self.pop_level(); + } + SpatialNodeKind::Sticky {} => { + let parent_id = self.spatial_nodes[&parent].wrench_id; + + self.write_line("- type: sticky-frame"); + self.push_level(); + self.write_line(&format!("id: {}", self.next_wrench_id)); + self.write_line(&format!("spatial-id: {}", parent_id)); + self.write_bounds(LayoutRect::zero()); + self.pop_level(); + } + } + + let _prev = self.spatial_nodes.insert( + spatial_id, + SpatialNode { + wrench_id: self.next_wrench_id, + }, + ); + assert!(_prev.is_none()); + self.next_wrench_id += 1; + } + + fn write_color( + &mut self, + color: ColorF, + ) { + self.write_line( + &format!("color: {}", color_to_string(color)) + ); + } + + fn write_rect( + &mut self, + tag: &str, + rect: LayoutRect, + ) { + self.write_line( + &format!("{}: {} {} {} {}", + tag, + rect.min.x, + rect.min.y, + rect.width(), + rect.height(), + ) + ); + } + + fn write_bounds( + &mut self, + bounds: LayoutRect, + ) { + self.write_rect("bounds", bounds); + } + + fn maybe_write_clip_rect( + &mut self, + bounds: LayoutRect, + clip_rect: LayoutRect, + ) { + if bounds != clip_rect { + self.write_rect("clip-rect", clip_rect); + } + } + + fn create_savepoint( + &mut self, + ) -> (usize, usize) { + (self.out.len(), self.indent.len()) + } + + fn restore_savepoint( + &mut self, + p: (usize, usize), + ) { + self.out.truncate(p.0); + self.indent.truncate(p.1); + } + + fn write_line( + &mut self, + s: &str, + ) { + self.out.push_str(&self.indent); + self.out.push_str(s); + self.out.push_str("\n"); + } + + fn write_spatial_id( + &mut self, + id: SpatialId, + ) { + let spatial_node = self.spatial_nodes + .get(&id) + .expect(&format!("unknown spatial node {:?}", id)); + + self.write_line(&format!("spatial-id: {}", spatial_node.wrench_id)); + } + + fn write_clip_chain_id( + &mut self, + id: ClipChainId, + ) { + if id != ClipChainId::INVALID { + let clip_chain_id = self.clipchain_id_map[&id]; + self.write_line(&format!("clip-chain: {}", clip_chain_id)); + } + } + + fn build_spatial_tree( + &mut self, + dl: &BuiltDisplayList, + pipeline_id: PipelineId, + ) { + // Insert root ref + scroll frames + self.add_and_write_spatial_node( + SpatialId::root_reference_frame(pipeline_id), + SpatialId::root_reference_frame(pipeline_id), + SpatialNodeKind::Reference { }, + ); + self.add_and_write_spatial_node( + SpatialId::root_scroll_node(pipeline_id), + SpatialId::root_reference_frame(pipeline_id), + SpatialNodeKind::Scroll { }, + ); + + dl.iter_spatial_tree(|item| { + match item { + SpatialTreeItem::ScrollFrame(descriptor) => { + self.add_and_write_spatial_node( + descriptor.scroll_frame_id, + descriptor.parent_space, + SpatialNodeKind::Scroll { + }, + ); + } + SpatialTreeItem::ReferenceFrame(descriptor) => { + self.add_and_write_spatial_node( + descriptor.reference_frame.id, + descriptor.parent_spatial_id, + SpatialNodeKind::Reference { + }, + ); + } + SpatialTreeItem::StickyFrame(descriptor) => { + self.add_and_write_spatial_node( + descriptor.id, + descriptor.parent_spatial_id, + SpatialNodeKind::Sticky { + }, + ); + } + SpatialTreeItem::Invalid => { + unreachable!(); + } + } + }); + } + + fn write_pipeline( + &mut self, + scene: &Scene, + pipeline_id: PipelineId, + ) -> Result<(), String> { + enum ContextKind { + Root, + StackingContext { + // sc_info: StackingContextInfo, + }, + } + struct BuildContext { + kind: ContextKind, + } + + let pipeline = &scene.pipelines[&pipeline_id]; + + self.build_spatial_tree( + &pipeline.display_list.display_list, + pipeline_id, + ); + + let mut stack = vec![BuildContext { + kind: ContextKind::Root, + }]; + let mut traversal = pipeline.display_list.iter(); + + 'outer: while let Some(bc) = stack.pop() { + loop { + let item = match traversal.next() { + Some(item) => item, + None => break, + }; + + match item.item() { + DisplayItem::PushStackingContext(info) => { + self.write_line("- type: stacking-context"); + self.push_level(); + self.write_spatial_id(info.spatial_id); + if let Some(clip_chain_id) = info.stacking_context.clip_chain_id { + self.write_clip_chain_id(clip_chain_id); + } + self.write_line( + &format!("bounds: {} {} 0 0", + 0.0, //info.origin.x + info.ref_frame_offset.x, + 0.0, //info.origin.y + info.ref_frame_offset.y, + ) + ); + self.write_line("items:"); + self.push_level(); + + let new_context = BuildContext { + kind: ContextKind::StackingContext { + // sc_info, + }, + }; + stack.push(bc); + stack.push(new_context); + + traversal = item.sub_iter(); + continue 'outer; + } + DisplayItem::PopStackingContext => { + self.pop_level(); + self.pop_level(); + } + DisplayItem::Iframe(info) => { + self.write_line("- type: iframe"); + self.push_level(); + self.write_spatial_id(info.space_and_clip.spatial_id); + self.write_clip_chain_id(info.space_and_clip.clip_chain_id); + self.write_bounds(info.bounds); + self.maybe_write_clip_rect(info.bounds, info.clip_rect); + self.write_line(&format!("id: [{}, {}]", + info.pipeline_id.0, + info.pipeline_id.1, + )); + self.pop_level(); + } + DisplayItem::Rectangle(info) => { + self.write_line("- type: rect"); + self.push_level(); + self.write_spatial_id(info.common.spatial_id); + self.write_clip_chain_id(info.common.clip_chain_id); + self.write_bounds(info.bounds); + self.maybe_write_clip_rect(info.bounds, info.common.clip_rect); + let color = match info.color { + PropertyBinding::Binding(..) => { + println!("WARN: Property color bindings are unsupported"); + ColorF::new(1.0, 0.0, 1.0, 0.5) + } + PropertyBinding::Value(color) => { + color + } + }; + if color.a > 0.0 { + self.write_color(color); + } + self.pop_level(); + } + DisplayItem::Text(info) => { + self.write_line("- type: rect"); + self.push_level(); + self.write_spatial_id(info.common.spatial_id); + self.write_clip_chain_id(info.common.clip_chain_id); + self.write_bounds(info.bounds); + self.maybe_write_clip_rect(info.bounds, info.common.clip_rect); + self.write_color(ColorF::new(1.0, 0.0, 0.0, 0.5)); + self.pop_level(); + } + DisplayItem::Border(info) => { + let sp = self.create_savepoint(); + + self.write_line("- type: border"); + self.push_level(); + self.write_spatial_id(info.common.spatial_id); + self.write_clip_chain_id(info.common.clip_chain_id); + self.maybe_write_clip_rect(info.bounds, info.common.clip_rect); + self.write_bounds(info.bounds); + + match info.details { + BorderDetails::Normal(border) => { + self.write_line("border-type: normal"); + + let colors = [ + border.top.color, + border.right.color, + border.bottom.color, + border.left.color, + ]; + + if colors.iter().all(|c| c.a == 0.0) { + self.restore_savepoint(sp); + continue; + } + + if colors.iter().all(|c| *c == border.top.color) { + self.write_color(border.top.color); + } else { + self.write_line(&format!( + "color: [ [{}], [{}], [{}], [{}] ]", + color_to_string_array(colors[0]), + color_to_string_array(colors[1]), + color_to_string_array(colors[2]), + color_to_string_array(colors[3]), + ) + ); + } + + let styles = [ + border.top.style, + border.right.style, + border.bottom.style, + border.left.style, + ]; + + if styles.iter().all(|s| *s == border.top.style) { + self.write_line(&format!( + "style: {}", style_to_string(styles[0]), + ) + ); + } else { + self.write_line(&format!( + "style: [ {}, {}, {}, {} ]", + style_to_string(styles[0]), + style_to_string(styles[1]), + style_to_string(styles[2]), + style_to_string(styles[3]), + ) + ); + } + + self.write_line("width: [1, 1, 1, 1]"); + + if !border.radius.is_zero() { + self.write_line("radius: {"); + self.push_level(); + self.write_line( + &format!("top-left: [{}, {}],", + border.radius.top_left.width, + border.radius.top_left.height, + ) + ); + self.write_line( + &format!("top-right: [{}, {}],", + border.radius.top_right.width, + border.radius.top_right.height, + ) + ); + self.write_line( + &format!("bottom-left: [{}, {}],", + border.radius.bottom_left.width, + border.radius.bottom_left.height, + ) + ); + self.write_line( + &format!("bottom-right: [{}, {}],", + border.radius.bottom_right.width, + border.radius.bottom_right.height, + ) + ); + self.pop_level(); + self.write_line("}"); + } + } + BorderDetails::NinePatch(..) => { + todo!(); + } + } + + self.pop_level(); + } + DisplayItem::Image(info) => { + self.write_line("- type: image"); + self.push_level(); + self.write_spatial_id(info.common.spatial_id); + self.write_clip_chain_id(info.common.clip_chain_id); + self.write_bounds(info.bounds); + self.maybe_write_clip_rect(info.bounds, info.common.clip_rect); + self.write_line( + &format!("src: checkerboard(2,8,8,{},{})", + ((info.bounds.width() - 2.0) / 8.0).ceil() as i32, + ((info.bounds.height() - 2.0) / 8.0).ceil() as i32, + ), + ); + self.pop_level(); + } + DisplayItem::RectClip(info) => { + let clip_id = self.add_clip_id(info.id); + self.write_line("- type: clip"); + self.push_level(); + self.write_line(&format!("id: {}", clip_id)); + self.write_spatial_id(info.spatial_id); + self.write_rect("bounds", info.clip_rect); + self.pop_level(); + } + DisplayItem::ImageMaskClip(info) => { + let clip_id = self.add_clip_id(info.id); + self.write_line("- type: clip"); + self.push_level(); + self.write_line(&format!("id: {}", clip_id)); + self.write_spatial_id(info.spatial_id); + self.write_rect("bounds", info.image_mask.rect); + self.pop_level(); + } + DisplayItem::RoundedRectClip(info) => { + let clip_id = self.add_clip_id(info.id); + self.write_line("- type: clip"); + self.push_level(); + self.write_line(&format!("id: {}", clip_id)); + self.write_spatial_id(info.spatial_id); + self.write_line("complex:"); + self.push_level(); + self.write_rect("- rect", info.clip.rect); + self.push_level(); + self.write_line("radius: {"); + self.push_level(); + self.write_line( + &format!("top-left: [{}, {}],", + info.clip.radii.top_left.width, + info.clip.radii.top_left.height, + )); + self.write_line( + &format!("top-right: [{}, {}],", + info.clip.radii.top_right.width, + info.clip.radii.top_right.height, + )); + self.write_line( + &format!("bottom-right: [{}, {}],", + info.clip.radii.bottom_right.width, + info.clip.radii.bottom_right.height, + )); + self.write_line( + &format!("bottom-left: [{}, {}],", + info.clip.radii.bottom_left.width, + info.clip.radii.bottom_left.height, + )); + self.pop_level(); + self.write_line("}"); + self.pop_level(); + self.pop_level(); + self.pop_level(); + } + DisplayItem::ClipChain(info) => { + let clipchain_id = self.add_clipchain_id(info.id); + self.write_line("- type: clip-chain"); + self.push_level(); + self.write_line(&format!("id: {}", clipchain_id)); + let mut clips = String::new(); + clips.push_str("clips: ["); + for id in item.clip_chain_items().iter() { + clips.push_str(&format!("{}, ", self.clip_id_map[&id])) + } + clips.push_str("]"); + self.write_line(&clips); + self.pop_level(); + } + + // TODO(gw): Ignored elements - we should as support for + // these as needed. + DisplayItem::SetGradientStops => {} + DisplayItem::SetFilterOps => {} + DisplayItem::SetFilterData => {} + DisplayItem::SetFilterPrimitives => {} + DisplayItem::SetPoints => {} + DisplayItem::PopAllShadows => {} + DisplayItem::ReuseItems(..) => {} + DisplayItem::RetainedItems(..) => {} + DisplayItem::RepeatingImage(..) => {} + DisplayItem::YuvImage(..) => {} + DisplayItem::BackdropFilter(..) => {} + DisplayItem::PushShadow(..) => {} + DisplayItem::Gradient(..) => {} + DisplayItem::RadialGradient(..) => {} + DisplayItem::ConicGradient(..) => {} + DisplayItem::ClearRectangle(..) => {} + DisplayItem::Line(..) => {} + DisplayItem::HitTest(..) => {} + DisplayItem::PushReferenceFrame(..) => {} + DisplayItem::PopReferenceFrame => {} + DisplayItem::DebugMarker(..) => {} + DisplayItem::BoxShadow(..) => {} + }; + } + + match bc.kind { + ContextKind::Root => {} + ContextKind::StackingContext { } => { + // self.pop_stacking_context(sc_info); + } + } + } + + assert!(stack.is_empty()); + + Ok(()) + } + + fn write_scene( + mut self, + scene: &Scene + ) -> Result<String, String> { + self.write_line(&format!("# process-capture")); + self.write_line("---"); + self.write_line("root:"); + self.push_level(); + self.write_line("items:"); + self.push_level(); + + if let Some(root_pipeline_id) = scene.root_pipeline_id { + self.write_pipeline(scene, root_pipeline_id)?; + } + + self.pop_level(); + self.pop_level(); + assert!(self.indent.is_empty()); + + if scene.pipelines.len() > 1 { + self.write_line("pipelines:"); + self.push_level(); + for (id, _) in &scene.pipelines { + if Some(*id) == scene.root_pipeline_id { + continue; + } + + self.write_line(&format!("- id: [{}, {}]", id.0, id.1)); + self.push_level(); + self.write_line("items:"); + self.push_level(); + self.write_pipeline(scene, *id)?; + self.pop_level(); + self.pop_level(); + } + self.pop_level(); + } + + Ok(self.out) + } +} + +pub fn scene_to_yaml( + scene: &Scene, +) -> Result<String, String> { + let writer = YamlWriter::new(); + + writer.write_scene(scene) +}