Update On Wed May 22 20:46:05 CEST 2024

This commit is contained in:
github-action[bot] 2024-05-22 20:46:06 +02:00
parent c488624fcc
commit 221b7a721e
881 changed files with 83685 additions and 109693 deletions

247
Cargo.lock generated
View file

@ -22,7 +22,7 @@ version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
@ -97,7 +97,7 @@ name = "any_all_workaround"
version = "0.1.0"
source = "git+https://github.com/hsivonen/any_all_workaround?rev=7fb1b7034c9f172aade21ee1c8554e8d8a48af80#7fb1b7034c9f172aade21ee1c8554e8d8a48af80"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"version_check",
]
@ -156,11 +156,11 @@ dependencies = [
[[package]]
name = "ash"
version = "0.37.3+1.3.251"
version = "0.38.0+1.3.281"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a"
checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f"
dependencies = [
"libloading 0.7.999",
"libloading 0.8.3",
]
[[package]]
@ -249,7 +249,7 @@ version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3632611da7e79f8fc8fd75840f1ccfa7792dbf1e25d00791344a4450dd8834f"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"dbus",
"libc",
"log",
@ -276,7 +276,7 @@ dependencies = [
"libc",
"log",
"memmap2 0.9.3",
"mio 0.8.8",
"mio",
"scopeguard",
"serde",
"serde_bytes",
@ -318,7 +318,7 @@ checksum = "be346361f2602704c3a48d71530df852a59558b9774a144432d91fdfe775f298"
dependencies = [
"base64 0.21.3",
"bitflags 1.999.999",
"cfg-if 1.0.0",
"cfg-if",
"core-foundation",
"devd-rs",
"libc",
@ -486,7 +486,7 @@ version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d84ea71c85d1fe98fe67a9b9988b1695bc24c0b0d3bfb18d4c510f44b4b09941"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
]
[[package]]
@ -714,13 +714,6 @@ dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "0.1.999"
dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -792,6 +785,16 @@ dependencies = [
"clap_derive",
]
[[package]]
name = "clap-verbosity-flag"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb9b20c0dd58e4c2e991c8d203bbeb76c11304d1011659686b5b644bc29aa478"
dependencies = [
"clap",
"log",
]
[[package]]
name = "clap_builder"
version = "4.4.5"
@ -995,7 +998,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b85cef661eeca0c6675116310936972c520ebb0a33ddef16fd7efc957f4c1288"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"libc",
"mach2",
]
@ -1007,7 +1010,7 @@ dependencies = [
"anyhow",
"block",
"bytes",
"cfg-if 1.0.0",
"cfg-if",
"cocoabind",
"embed-manifest",
"env_logger",
@ -1041,7 +1044,7 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
]
[[package]]
@ -1050,7 +1053,7 @@ version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"crossbeam-utils",
]
@ -1060,7 +1063,7 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
@ -1072,7 +1075,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
dependencies = [
"autocfg",
"cfg-if 1.0.0",
"cfg-if",
"crossbeam-utils",
"memoffset 0.8.999",
"scopeguard",
@ -1084,7 +1087,7 @@ version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"crossbeam-utils",
]
@ -1094,7 +1097,7 @@ version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
]
[[package]]
@ -1227,7 +1230,7 @@ dependencies = [
[[package]]
name = "d3d12"
version = "0.20.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=2f4522714c4037a1842d27bb448b634f089664ab#2f4522714c4037a1842d27bb448b634f089664ab"
source = "git+https://github.com/gfx-rs/wgpu?rev=18b758e3889bdd6ffa769085de15e2b96a0c1eb5#18b758e3889bdd6ffa769085de15e2b96a0c1eb5"
dependencies = [
"bitflags 2.5.0",
"libloading 0.8.3",
@ -1611,7 +1614,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
dependencies = [
"any_all_workaround",
"cfg-if 1.0.0",
"cfg-if",
]
[[package]]
@ -2020,22 +2023,6 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541"
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
dependencies = [
"bitflags 1.999.999",
"fuchsia-zircon-sys",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]]
name = "futures"
version = "0.3.28"
@ -2239,7 +2226,7 @@ version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
]
@ -2720,13 +2707,12 @@ version = "0.1.1"
dependencies = [
"base64 0.21.3",
"bindgen 0.69.4",
"cfg-if 1.0.0",
"cfg-if",
"http",
"hyper",
"log",
"mio 0.6.23",
"mio-extras",
"mozilla-central-workspace-hack",
"neqo-bin",
"neqo-common",
"neqo-crypto",
"neqo-http3",
@ -2775,7 +2761,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
"socket2",
"socket2 0.4.999",
"tokio",
"tower-service",
"tracing",
@ -3313,7 +3299,7 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"windows-targets 0.52.999",
]
@ -3539,7 +3525,7 @@ dependencies = [
"dns-parser",
"gecko-profiler",
"log",
"socket2",
"socket2 0.4.999",
"uuid",
]
@ -3682,7 +3668,7 @@ checksum = "e2abcd9c8a1e6e1e9d56ce3627851f39a17ea83e17c96bc510f29d7e43d78a7d"
dependencies = [
"bitflags 2.5.0",
"byteorder",
"cfg-if 1.0.0",
"cfg-if",
"crash-context",
"goblin 0.8.0",
"libc",
@ -3713,22 +3699,6 @@ dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.6.23"
dependencies = [
"cfg-if 0.1.999",
"fuchsia-zircon",
"fuchsia-zircon-sys",
"iovec",
"libc",
"log",
"miow",
"net2",
"slab",
"winapi",
]
[[package]]
name = "mio"
version = "0.8.8"
@ -3740,27 +3710,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "mio-extras"
version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
dependencies = [
"lazycell",
"log",
"mio 0.6.23",
"slab",
]
[[package]]
name = "miow"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
dependencies = [
"winapi",
]
[[package]]
name = "moz_asserts"
version = "0.1.0"
@ -3869,7 +3818,7 @@ dependencies = [
"libc",
"log",
"memchr",
"mio 0.8.8",
"mio",
"nom",
"num-integer",
"num-traits",
@ -3998,7 +3947,7 @@ checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664"
[[package]]
name = "naga"
version = "0.20.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=2f4522714c4037a1842d27bb448b634f089664ab#2f4522714c4037a1842d27bb448b634f089664ab"
source = "git+https://github.com/gfx-rs/wgpu?rev=18b758e3889bdd6ffa769085de15e2b96a0c1eb5#18b758e3889bdd6ffa769085de15e2b96a0c1eb5"
dependencies = [
"arrayvec",
"bit-set",
@ -4007,7 +3956,6 @@ dependencies = [
"hexf-parse",
"indexmap 2.2.6",
"log",
"num-traits",
"rustc-hash",
"serde",
"spirv",
@ -4016,10 +3964,32 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "neqo-bin"
version = "0.7.8"
source = "git+https://github.com/mozilla/neqo?tag=v0.7.8#a71e43dacf8fae41e5aa30cf95b2e826f63a7466"
dependencies = [
"clap",
"clap-verbosity-flag",
"futures",
"hex",
"log",
"neqo-common",
"neqo-crypto",
"neqo-http3",
"neqo-qpack",
"neqo-transport",
"qlog",
"quinn-udp",
"regex",
"tokio",
"url",
]
[[package]]
name = "neqo-common"
version = "0.7.7"
source = "git+https://github.com/mozilla/neqo?tag=v0.7.7#343df5cc0d02e0b0953de4a0a390ae8980d89081"
version = "0.7.8"
source = "git+https://github.com/mozilla/neqo?tag=v0.7.8#a71e43dacf8fae41e5aa30cf95b2e826f63a7466"
dependencies = [
"enum-map",
"env_logger",
@ -4031,8 +4001,8 @@ dependencies = [
[[package]]
name = "neqo-crypto"
version = "0.7.7"
source = "git+https://github.com/mozilla/neqo?tag=v0.7.7#343df5cc0d02e0b0953de4a0a390ae8980d89081"
version = "0.7.8"
source = "git+https://github.com/mozilla/neqo?tag=v0.7.8#a71e43dacf8fae41e5aa30cf95b2e826f63a7466"
dependencies = [
"bindgen 0.69.4",
"log",
@ -4046,8 +4016,8 @@ dependencies = [
[[package]]
name = "neqo-http3"
version = "0.7.7"
source = "git+https://github.com/mozilla/neqo?tag=v0.7.7#343df5cc0d02e0b0953de4a0a390ae8980d89081"
version = "0.7.8"
source = "git+https://github.com/mozilla/neqo?tag=v0.7.8#a71e43dacf8fae41e5aa30cf95b2e826f63a7466"
dependencies = [
"enumset",
"log",
@ -4063,8 +4033,8 @@ dependencies = [
[[package]]
name = "neqo-qpack"
version = "0.7.7"
source = "git+https://github.com/mozilla/neqo?tag=v0.7.7#343df5cc0d02e0b0953de4a0a390ae8980d89081"
version = "0.7.8"
source = "git+https://github.com/mozilla/neqo?tag=v0.7.8#a71e43dacf8fae41e5aa30cf95b2e826f63a7466"
dependencies = [
"log",
"neqo-common",
@ -4076,8 +4046,8 @@ dependencies = [
[[package]]
name = "neqo-transport"
version = "0.7.7"
source = "git+https://github.com/mozilla/neqo?tag=v0.7.7#343df5cc0d02e0b0953de4a0a390ae8980d89081"
version = "0.7.8"
source = "git+https://github.com/mozilla/neqo?tag=v0.7.8#a71e43dacf8fae41e5aa30cf95b2e826f63a7466"
dependencies = [
"enum-map",
"indexmap 2.2.6",
@ -4109,17 +4079,6 @@ dependencies = [
"xpcom",
]
[[package]]
name = "net2"
version = "0.2.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631"
dependencies = [
"cfg-if 0.1.999",
"libc",
"winapi",
]
[[package]]
name = "netwerk_helper"
version = "0.0.1"
@ -4149,7 +4108,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [
"bitflags 2.5.0",
"cfg-if 1.0.0",
"cfg-if",
"cfg_aliases",
"libc",
]
@ -4400,7 +4359,7 @@ version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
@ -4621,6 +4580,7 @@ dependencies = [
"mach2",
"memoffset 0.9.0",
"mozilla-central-workspace-hack",
"scroll",
"thiserror",
"windows-sys 0.52.0",
]
@ -4737,6 +4697,19 @@ version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quinn-udp"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7ad7bc932e4968523fa7d9c320ee135ff779de720e9350fee8728838551764"
dependencies = [
"libc",
"once_cell",
"socket2 0.5.7",
"tracing",
"windows-sys 0.52.0",
]
[[package]]
name = "quote"
version = "1.0.35"
@ -5296,7 +5269,7 @@ version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"cpufeatures",
"digest",
]
@ -5307,7 +5280,7 @@ version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"cpufeatures",
"digest",
]
@ -5387,12 +5360,19 @@ dependencies = [
[[package]]
name = "socket2"
version = "0.4.9"
version = "0.4.999"
dependencies = [
"socket2 0.5.7",
]
[[package]]
name = "socket2"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
dependencies = [
"libc",
"winapi",
"windows-sys 0.52.0",
]
[[package]]
@ -5705,7 +5685,7 @@ version = "3.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"fastrand",
"redox_syscall",
"rustix",
@ -5856,13 +5836,25 @@ dependencies = [
"backtrace",
"bytes",
"libc",
"mio 0.8.8",
"mio",
"num_cpus",
"pin-project-lite",
"socket2",
"socket2 0.4.999",
"tokio-macros",
"windows-sys 0.48.999",
]
[[package]]
name = "tokio-macros"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-stream"
version = "0.1.12"
@ -5915,7 +5907,7 @@ version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"log",
"pin-project-lite",
"tracing-attributes",
@ -6663,7 +6655,7 @@ dependencies = [
[[package]]
name = "wgpu-core"
version = "0.20.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=2f4522714c4037a1842d27bb448b634f089664ab#2f4522714c4037a1842d27bb448b634f089664ab"
source = "git+https://github.com/gfx-rs/wgpu?rev=18b758e3889bdd6ffa769085de15e2b96a0c1eb5#18b758e3889bdd6ffa769085de15e2b96a0c1eb5"
dependencies = [
"arrayvec",
"bit-vec",
@ -6681,7 +6673,6 @@ dependencies = [
"serde",
"smallvec",
"thiserror",
"web-sys",
"wgpu-hal",
"wgpu-types",
]
@ -6689,7 +6680,7 @@ dependencies = [
[[package]]
name = "wgpu-hal"
version = "0.20.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=2f4522714c4037a1842d27bb448b634f089664ab#2f4522714c4037a1842d27bb448b634f089664ab"
source = "git+https://github.com/gfx-rs/wgpu?rev=18b758e3889bdd6ffa769085de15e2b96a0c1eb5#18b758e3889bdd6ffa769085de15e2b96a0c1eb5"
dependencies = [
"android_system_properties",
"arrayvec",
@ -6728,7 +6719,7 @@ dependencies = [
[[package]]
name = "wgpu-types"
version = "0.20.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=2f4522714c4037a1842d27bb448b634f089664ab#2f4522714c4037a1842d27bb448b634f089664ab"
source = "git+https://github.com/gfx-rs/wgpu?rev=18b758e3889bdd6ffa769085de15e2b96a0c1eb5#18b758e3889bdd6ffa769085de15e2b96a0c1eb5"
dependencies = [
"bitflags 2.5.0",
"js-sys",
@ -6761,7 +6752,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb632c0076024630111a08ca9fcbd34736c80d10b9ae517077487b0c82f46a36"
dependencies = [
"cc",
"cfg-if 1.0.0",
"cfg-if",
"libc",
]
@ -6999,7 +6990,7 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eeea3eb6a30ed24e374f59368d3917c5180a845fdd4ed6f1b2278811a9e826f8"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"libc",
"once_cell",
]

View file

@ -127,9 +127,6 @@ bitflags = { path = "build/rust/bitflags" }
memmap2_0_5 = { package = "memmap2", path = "build/rust/memmap2-0.5" }
memmap2 = { path = "build/rust/memmap2" }
# Patch cfg-if 0.1 to 1.0
cfg-if = { path = "build/rust/cfg-if" }
# Patch redox_users to an empty crate
redox_users = { path = "build/rust/redox_users" }
@ -179,6 +176,9 @@ hashbrown = { path = "build/rust/hashbrown" }
# Patch `libloading` 0.7 to depend on 0.8, which moves from `winapi` to `windows-sys`.
libloading = { path = "build/rust/libloading" }
# Patch `socket2` 0.4 to 0.5
socket2 = { path = "build/rust/socket2" }
# The following overrides point to dummy projects, as a temporary measure until this is resolved:
# https://github.com/rust-lang/cargo/issues/6179
js-sys = { path = "build/rust/dummy-web/js-sys" }
@ -238,8 +238,3 @@ mio_0_8 = { package = "mio", git = "https://github.com/glandium/mio", rev = "9a2
# Patch `gpu-descriptor` 0.3.0 to remove unnecessary `allocator-api2` dep.:
# Still waiting for the now-merged <https://github.com/zakarumych/gpu-descriptor/pull/40> to be released.
gpu-descriptor = { git = "https://github.com/zakarumych/gpu-descriptor", rev = "7b71a4e47c81903ad75e2c53deb5ab1310f6ff4d" }
# Patch mio 0.6 to use winapi 0.3 and miow 0.3, getting rid of winapi 0.2.
# There is not going to be new version of mio 0.6, mio now being >= 0.7.11.
[patch.crates-io.mio]
path = "third_party/rust/mio-0.6.23"

View file

@ -243,6 +243,8 @@ class CacheKey {
static constexpr nsStaticAtom* TextValue = nsGkAtoms::aria_valuetext;
// gfx::Matrix4x4, CacheDomain::TransformMatrix
static constexpr nsStaticAtom* TransformMatrix = nsGkAtoms::transform;
// int32_t, CacheDomain::Value
static constexpr nsStaticAtom* ValueRegion = nsGkAtoms::valuetype;
// nsTArray<uint64_t>, CacheDomain::Viewport
// The list of Accessibles in the viewport used for hit testing and on-screen
// determination.

View file

@ -3452,6 +3452,14 @@ already_AddRefed<AccAttributes> LocalAccessible::BundleFieldsForCache(
fields->SetAttribute(CacheKey::SrcURL, DeleteEntry());
}
}
if (TagName() == nsGkAtoms::meter) {
// We should only cache value region for HTML meter elements. A meter
// should always have a value region, so this attribute should never
// be empty (i.e. there is no DeleteEntry() clause here).
HTMLMeterAccessible* meter = static_cast<HTMLMeterAccessible*>(this);
fields->SetAttribute(CacheKey::ValueRegion, meter->ValueRegion());
}
}
if (aCacheDomain & CacheDomain::Viewport && IsDoc()) {

View file

@ -18,6 +18,7 @@
#include "nsContentList.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/HTMLMeterElement.h"
#include "mozilla/dom/HTMLTextAreaElement.h"
#include "mozilla/dom/HTMLFormControlsCollection.h"
#include "nsIFormControl.h"
@ -965,6 +966,39 @@ bool HTMLMeterAccessible::SetCurValue(double aValue) {
return false; // meters are readonly.
}
int32_t HTMLMeterAccessible::ValueRegion() const {
dom::HTMLMeterElement* elm = dom::HTMLMeterElement::FromNode(mContent);
if (!elm) {
return -1;
}
double high = elm->High();
double low = elm->Low();
double optimum = elm->Optimum();
double value = elm->Value();
// For more information on how these regions are defined, see
// "UA requirements for regions of the gauge"
// https://html.spec.whatwg.org/multipage/form-elements.html#the-meter-element
if (optimum > high) {
if (value > high) {
return 1;
}
return value > low ? 0 : -1;
}
if (optimum < low) {
if (value < low) {
return 1;
}
return value < high ? 0 : -1;
}
// optimum is between low and high, inclusive
if (value >= low && value <= high) {
return 1;
}
// Both upper and lower regions are considered equally
// non-optimal.
return 0;
}
void HTMLMeterAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
nsAtom* aAttribute,
int32_t aModType,
@ -976,4 +1010,12 @@ void HTMLMeterAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
if (aAttribute == nsGkAtoms::value) {
mDoc->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, this);
}
if (aAttribute == nsGkAtoms::high || aAttribute == nsGkAtoms::low ||
aAttribute == nsGkAtoms::optimum) {
// Our meter's value region may have changed, queue an update for
// the value domain.
mDoc->QueueCacheUpdate(this, CacheDomain::Value);
return;
}
}

View file

@ -335,6 +335,17 @@ class HTMLMeterAccessible : public LeafAccessible {
// Widgets
virtual bool IsWidget() const override;
// HTMLMeterAccessible
/**
* Given the low, high, and optimum attrs from DOM, return an int
* that indicates which region the current value falls in:
* - Optimal (1)
* - Suboptimal (0)
* - Critical, or "even less good" by the spec (-1)
*/
int32_t ValueRegion() const;
protected:
virtual ~HTMLMeterAccessible() {}

View file

@ -1348,6 +1348,19 @@ void RemoteAccessible::Announce(const nsString& aAnnouncement,
}
#endif // !defined(XP_WIN)
int32_t RemoteAccessible::ValueRegion() const {
MOZ_ASSERT(TagName() == nsGkAtoms::meter,
"Accessing value region on non-meter element?");
if (mCachedFields) {
if (auto region =
mCachedFields->GetAttribute<int32_t>(CacheKey::ValueRegion)) {
return *region;
}
}
// Expose sub-optimal (but not critical) as the value region, as a fallback.
return 0;
}
void RemoteAccessible::ScrollSubstringToPoint(int32_t aStartOffset,
int32_t aEndOffset,
uint32_t aCoordinateType,

View file

@ -374,6 +374,9 @@ class RemoteAccessible : public Accessible, public HyperTextAccessibleBase {
void Announce(const nsString& aAnnouncement, uint16_t aPriority);
#endif // !defined(XP_WIN)
// HTMLMeterAccessible
int32_t ValueRegion() const;
// HyperTextAccessibleBase
virtual already_AddRefed<AccAttributes> DefaultTextAttributes() override;

View file

@ -195,6 +195,9 @@ Class a11y::GetTypeFromRole(roles::Role aRole) {
case roles::PROGRESSBAR:
return [mozRangeAccessible class];
case roles::METER:
return [mozMeterAccessible class];
case roles::SPINBUTTON:
case roles::SLIDER:
return [mozIncrementableAccessible class];

View file

@ -88,6 +88,13 @@
@end
@interface mozMeterAccessible : mozRangeAccessible
// override
- (NSString*)moxValueDescription;
@end
/**
* Base accessible for an incrementable, a settable range
*/

View file

@ -177,6 +177,48 @@ using namespace mozilla::a11y;
@end
@implementation mozMeterAccessible
- (NSString*)moxValueDescription {
nsAutoString valueDesc;
mGeckoAccessible->Value(valueDesc);
if (mGeckoAccessible->TagName() != nsGkAtoms::meter) {
// We're dealing with an aria meter, which shouldn't get
// a value region.
return nsCocoaUtils::ToNSString(valueDesc);
}
if (!valueDesc.IsEmpty()) {
// Append a comma to separate the existing value description
// from the value region.
valueDesc.Append(u", "_ns);
}
// We need to concat the given value description
// with a description of the value as either optimal,
// suboptimal, or critical.
int32_t region;
if (mGeckoAccessible->IsRemote()) {
region = mGeckoAccessible->AsRemote()->ValueRegion();
} else {
HTMLMeterAccessible* localMeter =
static_cast<HTMLMeterAccessible*>(mGeckoAccessible->AsLocal());
region = localMeter->ValueRegion();
}
if (region == 1) {
valueDesc.Append(u"Optimal value"_ns);
} else if (region == 0) {
valueDesc.Append(u"Suboptimal value"_ns);
} else {
MOZ_ASSERT(region == -1);
valueDesc.Append(u"Critical value"_ns);
}
return nsCocoaUtils::ToNSString(valueDesc);
}
@end
@implementation mozIncrementableAccessible
- (NSString*)moxValueDescription {

View file

@ -242,3 +242,54 @@ addAccessibleTask(
is(progress.getAttributeValue("AXValue"), 90, "Correct updated value");
}
);
/**
* Verify meter HTML elements expose the value region as part of their value
* description.
*/
addAccessibleTask(
`<label for="fuel">Fuel level:</label><meter id="fuel" min="0" max="100" low="33" high="66" optimum="80" value="50"></meter>`,
async (browser, accDoc) => {
const meter = getNativeInterface(accDoc, "fuel");
is(meter.getAttributeValue("AXValue"), 50, "Correct value");
is(
meter.getAttributeValue("AXValueDescription"),
"50, Suboptimal value",
"Value description contains appropriate value region"
);
let evt = waitForMacEvent("AXValueChanged");
await invokeContentTask(browser, [], () => {
const f = content.document.getElementById("fuel");
f.setAttribute("value", "90");
});
await evt;
is(
meter.getAttributeValue("AXValueDescription"),
"90, Optimal value",
"Value description updated to optimal"
);
await invokeContentTask(browser, [], () => {
const f = content.document.getElementById("fuel");
f.setAttribute("optimum", "20");
});
await untilCacheIs(
() => meter.getAttributeValue("AXValueDescription"),
"90, Critical value",
"Value description updated to critical."
);
// XXX bug 1895627:
// await invokeContentTask(browser, [], () => {
// const f = content.document.getElementById("fuel");
// f.textContent = "at 90/100";
// });
// await untilCacheIs(
// () => meter.getAttributeValue("AXValueDescription"),
// "at 90/100, Critical value",
// "Value description updated to include inner text."
// );
}
);

View file

@ -134,91 +134,6 @@ panelview:not([visible]) {
transition: height var(--panelui-subview-transition-duration);
}
/* stylelint-disable-next-line media-query-no-invalid */
@media not (-moz-bool-pref: "browser.tabs.tabmanager.enabled") {
#tabbrowser-tabs:not([overflow], [hashiddentabs]) ~ #alltabs-button {
display: none;
}
#tabbrowser-tabs:not([overflow])[using-closing-tabs-spacer] ~ #alltabs-button {
/* temporary space to keep a tab's close button under the cursor */
display: flex;
visibility: hidden;
}
}
#tabbrowser-tabs[hasadjacentnewtabbutton]:not([overflow]) ~ #new-tab-button,
#tabbrowser-tabs[overflow] > #tabbrowser-arrowscrollbox > #tabbrowser-arrowscrollbox-periphery > #tabs-newtab-button,
#tabbrowser-tabs:not([hasadjacentnewtabbutton]) > #tabbrowser-arrowscrollbox > #tabbrowser-arrowscrollbox-periphery > #tabs-newtab-button,
#TabsToolbar[customizing="true"] #tabs-newtab-button {
display: none;
}
.tabbrowser-tab:not([pinned]) {
flex: 100 100;
max-width: 225px;
min-width: var(--tab-min-width);
transition: min-width 100ms ease-out,
max-width 100ms ease-out;
}
:root[uidensity=touch] .tabbrowser-tab:not([pinned]) {
/* Touch mode needs additional space for the close button. */
min-width: calc(var(--tab-min-width) + 10px);
}
.tabbrowser-tab:not([pinned], [fadein]) {
max-width: 0.1px;
min-width: 0.1px;
visibility: hidden;
}
.tab-icon-pending:not([fadein]),
.tab-icon-image:not([fadein]),
.tab-close-button:not([fadein]),
.tabbrowser-tab:not([fadein])::after,
.tab-background:not([fadein]) {
visibility: hidden;
}
.tab-label:not([fadein]),
.tab-throbber:not([fadein]) {
display: none;
}
#tabbrowser-tabs[positionpinnedtabs] > #tabbrowser-arrowscrollbox > .tabbrowser-tab[pinned] {
position: absolute !important;
display: block;
}
#tabbrowser-tabs[movingtab] > #tabbrowser-arrowscrollbox > .tabbrowser-tab[selected],
#tabbrowser-tabs[movingtab] > #tabbrowser-arrowscrollbox > .tabbrowser-tab[multiselected] {
position: relative;
z-index: 2;
pointer-events: none; /* avoid blocking dragover events on scroll buttons */
}
@media (prefers-reduced-motion: no-preference) {
#tabbrowser-tabs[movingtab] > #tabbrowser-arrowscrollbox > .tabbrowser-tab[fadein]:not([selected]):not([multiselected]),
.tabbrowser-tab[tab-grouping],
.tabbrowser-tab[tabdrop-samewindow] {
transition: transform 200ms var(--animation-easing-function);
}
}
.tabbrowser-tab[tab-grouping][multiselected]:not([selected]) {
z-index: 2;
}
/* Make it easier to drag tabs by expanding the drag area downwards. */
#tabbrowser-tabs[movingtab] {
padding-bottom: 15px;
margin-bottom: -15px;
}
#navigator-toolbox[movingtab] > #nav-bar {
pointer-events: none;
}
#nav-bar-customization-target {
/* Don't grow if potentially-user-sized elements (like the searchbar or the
* bookmarks toolbar item list) are too wide. This forces them to flex to the
@ -226,14 +141,6 @@ panelview:not([visible]) {
min-width: 0;
}
/* Allow dropping a tab on buttons with associated drop actions. */
#navigator-toolbox[movingtab] > #nav-bar > #nav-bar-customization-target > #personal-bookmarks,
#navigator-toolbox[movingtab] > #nav-bar > #nav-bar-customization-target > #home-button,
#navigator-toolbox[movingtab] > #nav-bar > #nav-bar-customization-target > #downloads-button,
#navigator-toolbox[movingtab] > #nav-bar > #nav-bar-customization-target > #bookmarks-menu-button {
pointer-events: auto;
}
/* The address bar needs to be able to render outside of the toolbar, but as
* long as it's within the toolbar's bounds we can clip the toolbar so that the
* rendering pipeline doesn't reserve an enormous texture for it. */
@ -434,27 +341,6 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-b
position: absolute;
}
#allTabsMenu-dropIndicatorHolder {
display: block;
position: relative;
}
#allTabsMenu-dropIndicator {
background: url(chrome://browser/skin/tabbrowser/tab-drag-indicator.svg) no-repeat center;
display: block;
position: absolute;
transform: rotate(-90deg);
width: 12px;
height: 29px;
inset-inline-start: 8px;
top: 0;
pointer-events: none;
}
#allTabsMenu-dropIndicator:-moz-locale-dir(rtl) {
transform: rotate(90deg);
}
#nav-bar-customization-target > #personal-bookmarks,
toolbar:not(#TabsToolbar) > #wrapper-personal-bookmarks,
toolbar:not(#TabsToolbar) > #personal-bookmarks {

View file

@ -533,7 +533,7 @@
#include ../../components/downloads/content/downloadsPanel.inc.xhtml
#include ../../components/translations/content/selectTranslationsPanel.inc.xhtml
#include ../../components/translations/content/fullPageTranslationsPanel.inc.xhtml
#include browser-allTabsMenu.inc.xhtml
#include ../../components/tabbrowser/content/browser-allTabsMenu.inc.xhtml
<tooltip id="dynamic-shortcut-tooltip"
onpopupshowing="UpdateDynamicShortcutTooltipText(this);"/>

View file

@ -8,24 +8,30 @@ const { sinon } = ChromeUtils.importESModule(
"resource://testing-common/Sinon.sys.mjs"
);
async function openPreview(tab) {
async function openPreview(tab, win = window) {
const previewShown = BrowserTestUtils.waitForPopupEvent(
document.getElementById("tab-preview-panel"),
win.document.getElementById("tab-preview-panel"),
"shown"
);
EventUtils.synthesizeMouseAtCenter(tab, { type: "mouseover" });
EventUtils.synthesizeMouseAtCenter(tab, { type: "mouseover" }, win);
return previewShown;
}
async function closePreviews() {
const tabs = document.getElementById("tabbrowser-tabs");
async function closePreviews(win = window) {
const tabs = win.document.getElementById("tabbrowser-tabs");
const previewHidden = BrowserTestUtils.waitForPopupEvent(
document.getElementById("tab-preview-panel"),
win.document.getElementById("tab-preview-panel"),
"hidden"
);
EventUtils.synthesizeMouse(tabs, 0, tabs.outerHeight + 1, {
type: "mouseout",
});
EventUtils.synthesizeMouse(
tabs,
0,
tabs.outerHeight + 1,
{
type: "mouseout",
},
win
);
return previewHidden;
}
@ -73,6 +79,36 @@ add_task(async function hoverTests() {
await closePreviews();
// Bug 1897475 - don't show tab previews in background windows
let fgWindow = await BrowserTestUtils.openNewBrowserWindow();
let fgTab = fgWindow.gBrowser.tabs[0];
let fgWindowPreviewContainer =
fgWindow.document.getElementById("tab-preview-panel");
await openPreview(fgTab, fgWindow);
Assert.equal(
fgWindowPreviewContainer.querySelector(".tab-preview-title").innerText,
"New Tab",
"Preview of foreground tab shows correct title"
);
await closePreviews(fgWindow);
// ensure tab1 preview doesn't open, as it's now in a background window
let resolved = false;
let openPreviewPromise = openPreview(tab1).then(() => {
resolved = true;
});
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
let timeoutPromise = new Promise(resolve => setTimeout(resolve, 100));
await Promise.race([openPreviewPromise, timeoutPromise]);
Assert.ok(!resolved, "preview does not open from background window");
Assert.ok(
BrowserTestUtils.isHidden(previewContainer),
"Background window tab preview hidden"
);
await BrowserTestUtils.closeWindow(fgWindow);
BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2);

View file

@ -4477,12 +4477,12 @@ BrowserGlue.prototype = {
);
lazy.FormAutofillUtils.setOSAuthEnabled(
"extensions.formautofill.creditcards.reauth.optout",
lazy.FormAutofillUtils.AUTOFILL_CREDITCARDS_REAUTH_PREF,
ccReauthPrefValue
);
lazy.LoginHelper.setOSAuthEnabled(
"signon.management.page.os-auth.optout",
lazy.LoginHelper.OS_AUTH_FOR_PASSWORDS_PREF,
pwdReauthPrefValue
);
}

View file

@ -33,6 +33,7 @@ let proxyPreferences = [
"network.proxy.type",
"network.proxy.autoconfig_url",
"network.proxy.socks_remote_dns",
"network.proxy.socks5_remote_dns",
"signon.autologin.proxy",
"network.proxy.socks_version",
"network.proxy.no_proxies_on",
@ -57,6 +58,7 @@ export var ProxyPolicies = {
if (param.UseProxyForDNS !== undefined) {
setPref("network.proxy.socks_remote_dns", param.UseProxyForDNS);
setPref("network.proxy.socks5_remote_dns", param.UseProxyForDNS);
}
if (param.AutoLogin !== undefined) {

View file

@ -38,7 +38,7 @@ add_task(async function test_proxy_boolean_settings() {
},
});
checkUnlockedPref("network.proxy.socks_remote_dns", false);
checkUnlockedPref("network.proxy.socks5_remote_dns", false);
checkUnlockedPref("signon.autologin.proxy", false);
await setupPolicyEngineWithJson({
@ -50,7 +50,7 @@ add_task(async function test_proxy_boolean_settings() {
},
});
checkUnlockedPref("network.proxy.socks_remote_dns", true);
checkUnlockedPref("network.proxy.socks5_remote_dns", true);
checkUnlockedPref("signon.autologin.proxy", true);
});

View file

@ -88,8 +88,8 @@
.close-button-wrapper {
position: sticky;
top: var(--space-large);
margin-block-end: var(--space-large);
top: 0;
padding-block-start: var(--space-large);
background-color: var(--newtab-background-color-secondary);
z-index: 1;
}

View file

@ -1734,8 +1734,8 @@ main section {
}
.customize-menu .close-button-wrapper {
position: sticky;
top: var(--space-large);
margin-block-end: var(--space-large);
top: 0;
padding-block-start: var(--space-large);
background-color: var(--newtab-background-color-secondary);
z-index: 1;
}

View file

@ -1738,8 +1738,8 @@ main section {
}
.customize-menu .close-button-wrapper {
position: sticky;
top: var(--space-large);
margin-block-end: var(--space-large);
top: 0;
padding-block-start: var(--space-large);
background-color: var(--newtab-background-color-secondary);
z-index: 1;
}

View file

@ -1734,8 +1734,8 @@ main section {
}
.customize-menu .close-button-wrapper {
position: sticky;
top: var(--space-large);
margin-block-end: var(--space-large);
top: 0;
padding-block-start: var(--space-large);
background-color: var(--newtab-background-color-secondary);
z-index: 1;
}

View file

@ -7,6 +7,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
MerinoClient: "resource:///modules/MerinoClient.sys.mjs",
clearTimeout: "resource://gre/modules/Timer.sys.mjs",
setTimeout: "resource://gre/modules/Timer.sys.mjs",
PersistentCache: "resource://activity-stream/lib/PersistentCache.sys.mjs",
});
import {
@ -14,12 +15,13 @@ import {
actionCreators as ac,
} from "resource://activity-stream/common/Actions.mjs";
const CACHE_KEY = "weather_feed";
const WEATHER_UPDATE_TIME = 10 * 60 * 1000; // 10 minutes
const MERINO_PROVIDER = "accuweather";
const WEATHER_ENABLED = "browser.newtabpage.activity-stream.showWeather";
const WEATHER_ENABLED_SYS =
"browser.newtabpage.activity-stream.system.showWeather";
const WEATHER_QUERY = "browser.newtabpage.activity-stream.weather.query";
const PREF_WEATHER_QUERY = "weather.query";
const PREF_SHOW_WEATHER = "showWeather";
const PREF_SYSTEM_SHOW_WEATHER = "system.showWeather";
/**
* A feature that periodically fetches weather suggestions from Merino for HNT.
@ -35,17 +37,30 @@ export class WeatherFeed {
this.timeoutMS = 5000;
this.lastFetchTimeMs = 0;
this.fetchDelayAfterComingOnlineMs = 3000; // 3s
this.cache = this.PersistentCache(CACHE_KEY, true);
}
async resetCache() {
if (this.cache) {
await this.cache.set("weather", {});
}
}
async resetWeather() {
await this.resetCache();
this.suggestions = [];
this.lastUpdated = null;
}
isEnabled() {
return (
Services.prefs.getBoolPref(WEATHER_ENABLED) &&
Services.prefs.getBoolPref(WEATHER_ENABLED_SYS)
this.store.getState().Prefs.values[PREF_SHOW_WEATHER] &&
this.store.getState().Prefs.values[PREF_SYSTEM_SHOW_WEATHER]
);
}
init() {
this.fetch(true /* isStartup */);
async init() {
await this.loadWeather(true /* isStartup */);
}
stopFetching() {
@ -59,21 +74,13 @@ export class WeatherFeed {
this.fetchTimer = 0;
}
/**
* This thin wrapper around lazy.MerinoClient makes it easier for us to write
* automated tests that simulate responses.
*/
MerinoClient(...args) {
return new lazy.MerinoClient(...args);
}
/**
* This thin wrapper around the fetch call makes it easier for us to write
* automated tests that simulate responses.
*/
async fetchHelper() {
this.restartFetchTimer();
this.lastUpdated = Date.now();
const weatherQuery = Services.prefs.getStringPref(WEATHER_QUERY);
const weatherQuery = this.store.getState().Prefs.values[PREF_WEATHER_QUERY];
let suggestions = await this.merino.fetch({
query: weatherQuery || "",
providers: [MERINO_PROVIDER],
@ -84,7 +91,7 @@ export class WeatherFeed {
this.suggestions = suggestions ?? [];
}
async fetch(isStartup = false) {
async fetch(isStartup) {
// Keep a handle on the `MerinoClient` instance that exists at the start of
// this fetch. If fetching stops or this `Weather` instance is uninitialized
// during the fetch, `#merino` will be nulled, and the fetch should stop. We
@ -93,6 +100,28 @@ export class WeatherFeed {
await this.fetchHelper();
if (this.suggestions.length) {
this.lastUpdated = this.Date().now();
await this.cache.set("weather", {
suggestions: this.suggestions,
lastUpdated: this.lastUpdated,
});
this.update(isStartup);
}
}
async loadWeather(isStartup = false) {
const cachedData = (await this.cache.get()) || {};
const { weather } = cachedData;
// If we have nothing in cache, or cache has expired, we can make a fresh fetch.
if (
!weather?.lastUpdated ||
!(this.Date().now() - weather.lastUpdated < WEATHER_UPDATE_TIME)
) {
await this.fetch(isStartup);
} else if (!this.lastUpdated) {
this.suggestions = weather.suggestions;
this.lastUpdated = weather.lastUpdated;
this.update(isStartup);
}
}
@ -119,33 +148,55 @@ export class WeatherFeed {
}, ms);
}
async onAction(action) {
switch (action.type) {
case at.INIT:
if (this.isEnabled()) {
this.init();
}
async onPrefChangedAction(action) {
switch (action.data.name) {
case PREF_WEATHER_QUERY:
await this.loadWeather();
break;
case at.UNINIT:
break;
case at.DISCOVERY_STREAM_DEV_SYSTEM_TICK:
case at.SYSTEM_TICK:
if (this.isEnabled()) {
await this.fetch();
}
break;
case at.PREF_CHANGED:
if (action.data.name === "weather.query") {
await this.fetch();
} else if (
(action.data.name === "showWeather" ||
action.data.name === "system.showWeather") &&
action.data.value &&
this.isEnabled()
) {
await this.fetch();
case PREF_SHOW_WEATHER:
case PREF_SYSTEM_SHOW_WEATHER:
if (this.isEnabled() && action.data.value) {
await this.loadWeather();
} else {
await this.resetWeather();
}
break;
}
}
async onAction(action) {
switch (action.type) {
case at.INIT:
if (this.isEnabled()) {
await this.init();
}
break;
case at.UNINIT:
await this.resetWeather();
break;
case at.DISCOVERY_STREAM_DEV_SYSTEM_TICK:
case at.SYSTEM_TICK:
if (this.isEnabled()) {
await this.loadWeather();
}
break;
case at.PREF_CHANGED:
await this.onPrefChangedAction(action);
break;
}
}
}
/**
* Creating a thin wrapper around MerinoClient, PersistentCache, and Date.
* This makes it easier for us to write automated tests that simulate responses.
*/
WeatherFeed.prototype.MerinoClient = (...args) => {
return new lazy.MerinoClient(...args);
};
WeatherFeed.prototype.PersistentCache = (...args) => {
return new lazy.PersistentCache(...args);
};
WeatherFeed.prototype.Date = () => {
return Date;
};

View file

@ -23,6 +23,12 @@ const SYS_WEATHER_ENABLED =
"browser.newtabpage.activity-stream.system.showWeather";
add_task(async function test_construction() {
let sandbox = sinon.createSandbox();
sandbox.stub(WeatherFeed.prototype, "PersistentCache").returns({
set: () => {},
get: () => {},
});
let feed = new WeatherFeed();
info("WeatherFeed constructor should create initial values");
@ -35,18 +41,28 @@ add_task(async function test_construction() {
"suggestions is initialized as a array with length of 0"
);
Assert.ok(feed.fetchTimer === null, "fetchTimer is initialized as null");
sandbox.restore();
});
add_task(async function test_onAction_INIT() {
let sandbox = sinon.createSandbox();
let feed = new WeatherFeed();
Services.prefs.setBoolPref(WEATHER_ENABLED, true);
Services.prefs.setBoolPref(SYS_WEATHER_ENABLED, true);
sandbox.stub(feed, "MerinoClient").returns({
sandbox.stub(WeatherFeed.prototype, "MerinoClient").returns({
get: () => [WEATHER_SUGGESTION],
on: () => {},
});
sandbox.stub(WeatherFeed.prototype, "PersistentCache").returns({
set: () => {},
get: () => {},
});
const dateNowTestValue = 1;
sandbox.stub(WeatherFeed.prototype, "Date").returns({
now: () => dateNowTestValue,
});
let feed = new WeatherFeed();
Services.prefs.setBoolPref(WEATHER_ENABLED, true);
Services.prefs.setBoolPref(SYS_WEATHER_ENABLED, true);
sandbox.stub(feed, "isEnabled").returns(true);
@ -70,7 +86,7 @@ add_task(async function test_onAction_INIT() {
type: at.WEATHER_UPDATE,
data: {
suggestions: [WEATHER_SUGGESTION],
lastUpdated: null,
lastUpdated: dateNowTestValue,
},
meta: {
isStartup: true,

View file

@ -25,7 +25,7 @@ Preferences.addAll([
{ id: "network.proxy.socks", type: "string" },
{ id: "network.proxy.socks_port", type: "int" },
{ id: "network.proxy.socks_version", type: "int" },
{ id: "network.proxy.socks_remote_dns", type: "bool" },
{ id: "network.proxy.socks5_remote_dns", type: "bool" },
{ id: "network.proxy.no_proxies_on", type: "string" },
{ id: "network.proxy.share_proxy_settings", type: "bool" },
{ id: "signon.autologin.proxy", type: "bool" },
@ -179,7 +179,7 @@ var gConnectionsDialog = {
updateDNSPref() {
var socksVersionPref = Preferences.get("network.proxy.socks_version");
var socksDNSPref = Preferences.get("network.proxy.socks_remote_dns");
var socksDNSPref = Preferences.get("network.proxy.socks5_remote_dns");
var proxyTypePref = Preferences.get("network.proxy.type");
var isDefinitelySocks4 =
proxyTypePref.value == 1 && socksVersionPref.value == 4;

View file

@ -252,7 +252,7 @@
/>
<checkbox
id="networkProxySOCKSRemoteDNS"
preference="network.proxy.socks_remote_dns"
preference="network.proxy.socks5_remote_dns"
data-l10n-id="connection-proxy-socks-remote-dns"
/>
</dialog>

View file

@ -39,6 +39,7 @@ const API_PROXY_PREFS = [
"network.proxy.socks_port",
"network.proxy.socks_version",
"network.proxy.socks_remote_dns",
"network.proxy.socks5_remote_dns",
"network.proxy.no_proxies_on",
"network.proxy.autoconfig_url",
"signon.autologin.proxy",

View file

@ -202,6 +202,8 @@ async function reformatExpectedWebCompatInfo(tab, overrides) {
}
}
extra_labels.sort();
return reformatted;
}

View file

@ -327,9 +327,7 @@ let ShellServiceInternal = {
// On Windows, our best chance is to set UserChoice, so try that first.
if (
AppConstants.platform == "win" &&
lazy.NimbusFeatures.shellService.getVariable(
"setDefaultBrowserUserChoice"
)
Services.prefs.getBoolPref("browser.shell.setDefaultBrowserUserChoice")
) {
try {
await this.setAsDefaultUserChoice();

View file

@ -107,21 +107,10 @@ var SidebarController = {
this._toolsAndExtensions = new Map();
this.getTools().forEach(tool => {
this._toolsAndExtensions.set(tool.commandID, {
view: tool.commandID,
icon: tool.icon,
l10nId: tool.revampL10nId,
disabled: tool.disabled ?? false,
});
this._toolsAndExtensions.set(tool.commandID, tool);
});
this.getExtensions().forEach(extension => {
this._toolsAndExtensions.set(extension.commandID, {
view: extension.commandID,
extensionId: extension.extensionId,
icon: extension.icon,
tooltiptext: extension.label,
disabled: false,
});
this._toolsAndExtensions.set(extension.commandID, extension);
});
return this._toolsAndExtensions;
},
@ -755,7 +744,13 @@ var SidebarController = {
const extensions = [];
for (const [commandID, sidebar] of this.sidebars.entries()) {
if (Object.hasOwn(sidebar, "extensionId")) {
extensions.push({ commandID, ...sidebar });
extensions.push({
commandID,
extensionId: sidebar.extensionId,
icon: sidebar.icon,
tooltiptext: sidebar.label,
disabled: false,
});
}
}
return extensions;
@ -775,7 +770,13 @@ var SidebarController = {
];
for (const [commandID, sidebar] of this.sidebars.entries()) {
if (toolIds.includes(commandID)) {
tools.push({ commandID, ...sidebar });
tools.push({
commandID,
view: commandID,
icon: sidebar.icon,
l10nId: sidebar.revampL10nId,
disabled: sidebar.disabled ?? false,
});
}
}
return tools;

View file

@ -44,14 +44,36 @@
}
.icon {
-moz-context-properties: fill;
fill: currentColor;
background-image: var(--tool-icon);
background-size: var(--icon-size-default);
width: var(--icon-size-default);
height: var(--icon-size-default);
background-position: center;
background-repeat: no-repeat;
}
}
}
.icon {
-moz-context-properties: fill;
fill: currentColor;
background-size: var(--icon-size-default);
width: var(--icon-size-default);
height: var(--icon-size-default);
background-position: center;
background-repeat: no-repeat;
}
.customize-extensions {
.extensions {
display: flex;
flex-direction: column;
gap: var(--space-medium);
}
.extension-item {
display: flex;
gap: var(--space-medium);
align-items: center;
font-size: 0.9em;
.icon {
background-image: var(--extension-icon);
}
}
}

View file

@ -2,7 +2,11 @@
* 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/. */
import { html, styleMap } from "chrome://global/content/vendor/lit.all.mjs";
import {
html,
styleMap,
when,
} from "chrome://global/content/vendor/lit.all.mjs";
import { SidebarPage } from "./sidebar-page.mjs";
// eslint-disable-next-line import/no-unassigned-import
@ -15,17 +19,31 @@ const l10nMap = new Map([
]);
export class SidebarCustomize extends SidebarPage {
constructor() {
super();
this.activeExtIndex = 0;
}
static properties = {
activeExtIndex: { type: Number },
};
static queries = {
toolInputs: { all: ".customize-firefox-tools input" },
extensionLinks: { all: ".extension-link" },
};
connectedCallback() {
super.connectedCallback();
window.addEventListener("SidebarItemChanged", this);
this.getWindow().addEventListener("SidebarItemAdded", this);
this.getWindow().addEventListener("SidebarItemChanged", this);
this.getWindow().addEventListener("SidebarItemRemoved", this);
}
disconnectedCallback() {
super.disconnectedCallback();
window.removeEventListener("SidebarItemChanged", this);
this.getWindow().removeEventListener("SidebarItemAdded", this);
this.getWindow().removeEventListener("SidebarItemChanged", this);
this.getWindow().removeEventListener("SidebarItemRemoved", this);
}
get sidebarLauncher() {
@ -42,18 +60,11 @@ export class SidebarCustomize extends SidebarPage {
this.getWindow().SidebarController.toggle(view);
}
getTools() {
const toolsMap = new Map(
[...this.getWindow().SidebarController.toolsAndExtensions]
// eslint-disable-next-line no-unused-vars
.filter(([key, val]) => !val.extensionId)
);
return toolsMap;
}
handleEvent(e) {
switch (e.type) {
case "SidebarItemAdded":
case "SidebarItemChanged":
case "SidebarItemRemoved":
this.requestUpdate();
break;
}
@ -87,7 +98,59 @@ export class SidebarCustomize extends SidebarPage {
</div>`;
}
async manageAddon(extensionId) {
await this.getWindow().BrowserAddonUI.manageAddon(
extensionId,
"unifiedExtensions"
);
}
handleKeydown(e) {
if (e.code == "ArrowUp") {
if (this.activeExtIndex >= 0) {
this.focusIndex(this.activeExtIndex - 1);
}
} else if (e.code == "ArrowDown") {
if (this.activeExtIndex < this.extensionLinks.length) {
this.focusIndex(this.activeExtIndex + 1);
}
} else if (
(e.type == "keydown" && e.code == "Enter") ||
(e.type == "keydown" && e.code == "Space")
) {
this.manageAddon(e.target.getAttribute("extensionId"));
}
}
focusIndex(index) {
let extLinkList = Array.from(
this.shadowRoot.querySelectorAll(".extension-link")
);
extLinkList[index].focus();
this.activeExtIndex = index;
}
extensionTemplate(extension, index) {
return html`
<div class="extension-item">
<span class="icon ghost-icon" style=${styleMap({
"--extension-icon": extension.icon,
})} role="presentation"/>
</span>
<div class="extension-link" extensionId=${
extension.extensionId
} tabindex=${
index === this.activeExtIndex ? 0 : -1
} role="list-item" @click=${() =>
this.manageAddon(extension.extensionId)} @keydown=${this.handleKeydown}>
<a href="about:addons" tabindex="-1" target="_blank" @click=${e =>
e.preventDefault()}>${extension.tooltiptext}</a>
</div>
</div>`;
}
render() {
let extensions = this.getWindow().SidebarController.getExtensions();
return html`
${this.stylesheet()}
<link rel="stylesheet" href="chrome://browser/content/sidebar/sidebar-customize.css"></link>
@ -106,9 +169,22 @@ export class SidebarCustomize extends SidebarPage {
<div class="customize-firefox-tools">
<h5 data-l10n-id="sidebar-customize-firefox-tools"></h5>
<div class="inputs">
${[...this.getTools().values()].map(tool => this.inputTemplate(tool))}
${this.getWindow()
.SidebarController.getTools()
.map(tool => this.inputTemplate(tool))}
</div>
</div>
${when(
extensions.length,
() => html`<div class="customize-extensions">
<h5 data-l10n-id="sidebar-customize-extensions"></h5>
<div role="list" class="extensions">
${extensions.map((extension, index) =>
this.extensionTemplate(extension, index)
)}
</div>
</div>`
)}
</div>
`;
}

View file

@ -41,3 +41,6 @@ sidebar-context-menu-remove-extension =
.label = Remove extension
sidebar-context-menu-report-extension =
.label = Report extension
# A header for a list of sidebar-specific extensions in the sidebar
sidebar-customize-extensions = Sidebar extensions

View file

@ -6,6 +6,17 @@
add_setup(() => SpecialPowers.pushPrefEnv({ set: [["sidebar.revamp", true]] }));
registerCleanupFunction(() => SpecialPowers.popPrefEnv());
const extData2 = {
manifest: {
sidebar_action: {
default_icon: "default.png",
default_panel: "default.html",
default_title: "Another Title",
},
},
...extData,
};
async function sendMessage(extension, msg, data) {
extension.sendMessage({ msg, data });
await extension.awaitMessage("done");
@ -72,15 +83,16 @@ add_task(async function test_extension_sidebar_actions() {
});
add_task(async function test_open_new_window_after_install() {
const extension = ExtensionTestUtils.loadExtension({ ...extData });
await extension.startup();
await extension.awaitMessage("sidebar");
const win = await BrowserTestUtils.openNewBrowserWindow();
const { document } = win;
const sidebar = document.querySelector("sidebar-main");
ok(sidebar, "Sidebar is shown.");
const extension = ExtensionTestUtils.loadExtension({ ...extData });
await extension.startup();
await extension.awaitMessage("sidebar");
await extension.awaitMessage("sidebar");
is(
sidebar.extensionButtons.length,
1,
@ -154,3 +166,134 @@ add_task(async function test_open_new_private_window_after_install() {
await extension.unload();
await BrowserTestUtils.closeWindow(privateWin);
});
add_task(async function test_customize_sidebar_extensions() {
const win = await BrowserTestUtils.openNewBrowserWindow();
const { document } = win;
const sidebar = document.querySelector("sidebar-main");
ok(sidebar, "Sidebar is shown.");
const extension = ExtensionTestUtils.loadExtension({ ...extData });
await extension.startup();
await extension.awaitMessage("sidebar");
await extension.awaitMessage("sidebar");
is(sidebar.extensionButtons.length, 1, "Extension is shown in the sidebar.");
const button = sidebar.customizeButton;
const promiseFocused = BrowserTestUtils.waitForEvent(win, "SidebarFocused");
button.click();
await promiseFocused;
let customizeDocument = win.SidebarController.browser.contentDocument;
const customizeComponent =
customizeDocument.querySelector("sidebar-customize");
let extensionEntrypointsCount = sidebar.extensionButtons.length;
is(
customizeComponent.extensionLinks.length,
extensionEntrypointsCount,
`${extensionEntrypointsCount} links to manage sidebar extensions are shown in the Customize Menu.`
);
// Default icon and title matches.
const extensionLink = customizeComponent.extensionLinks[0];
let iconUrl = `moz-extension://${extension.uuid}/default.png`;
let iconEl = extensionLink.closest(".extension-item").querySelector(".icon");
is(
iconEl.style.getPropertyValue("--extension-icon"),
`image-set(url("${iconUrl}"), url("${iconUrl}") 2x)`,
"Extension has the correct icon."
);
is(
extensionLink.textContent.trim(),
"Default Title",
"Extension has the correct title."
);
// Test manage extension
extensionLink.click();
await TestUtils.waitForCondition(() => {
let spec = win.gBrowser.selectedTab.linkedBrowser.documentURI.spec;
return spec.startsWith("about:addons");
}, "about:addons is the new opened tab");
await extension.unload();
await sidebar.updateComplete;
is(
sidebar.extensionButtons.length,
0,
"Extension is removed from the sidebar."
);
await BrowserTestUtils.closeWindow(win);
});
add_task(async function test_extensions_keyboard_navigation() {
const win = await BrowserTestUtils.openNewBrowserWindow();
const { document } = win;
const sidebar = document.querySelector("sidebar-main");
ok(sidebar, "Sidebar is shown.");
const extension = ExtensionTestUtils.loadExtension({ ...extData });
await extension.startup();
await extension.awaitMessage("sidebar");
await extension.awaitMessage("sidebar");
is(sidebar.extensionButtons.length, 1, "Extension is shown in the sidebar.");
const extension2 = ExtensionTestUtils.loadExtension({ ...extData2 });
await extension2.startup();
await extension2.awaitMessage("sidebar");
await extension2.awaitMessage("sidebar");
is(
sidebar.extensionButtons.length,
2,
"Two extensions are shown in the sidebar."
);
const button = sidebar.customizeButton;
const promiseFocused = BrowserTestUtils.waitForEvent(win, "SidebarFocused");
button.click();
await promiseFocused;
let customizeDocument = win.SidebarController.browser.contentDocument;
const customizeComponent =
customizeDocument.querySelector("sidebar-customize");
let extensionEntrypointsCount = sidebar.extensionButtons.length;
is(
customizeComponent.extensionLinks.length,
extensionEntrypointsCount,
`${extensionEntrypointsCount} links to manage sidebar extensions are shown in the Customize Menu.`
);
customizeComponent.extensionLinks[0].focus();
ok(
isActiveElement(customizeComponent.extensionLinks[0]),
"First extension link is focused."
);
info("Press Arrow Down key.");
EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
ok(
isActiveElement(customizeComponent.extensionLinks[1]),
"Second extension link is focused."
);
info("Press Arrow Up key.");
EventUtils.synthesizeKey("KEY_ArrowUp", {}, win);
ok(
isActiveElement(customizeComponent.extensionLinks[0]),
"First extension link is focused."
);
info("Press Enter key.");
EventUtils.synthesizeKey("KEY_Enter", {}, win);
await TestUtils.waitForCondition(() => {
let spec = win.gBrowser.selectedTab.linkedBrowser.documentURI.spec;
return spec.startsWith("about:addons");
}, "about:addons is the new opened tab");
await extension.unload();
await extension2.unload();
await sidebar.updateComplete;
is(
sidebar.extensionButtons.length,
0,
"Extensions are removed from the sidebar."
);
await BrowserTestUtils.closeWindow(win);
});

View file

@ -119,3 +119,7 @@ function openAndWaitForContextMenu(popup, button, onShown, onHidden) {
);
});
}
function isActiveElement(el) {
return el.getRootNode().activeElement == el;
}

View file

@ -575,7 +575,12 @@
// Prepare connection to host beforehand.
SessionStore.speculativeConnectOnTabHover(this);
this.dispatchEvent(new CustomEvent("TabHoverStart", { bubbles: true }));
const isForegroundWindow =
this.ownerGlobal ==
BrowserWindowTracker.getTopWindow({ allowPopups: true });
if (isForegroundWindow) {
this.dispatchEvent(new CustomEvent("TabHoverStart", { bubbles: true }));
}
}
_mouseleave() {

View file

@ -91,6 +91,7 @@ export default class TabPreviewPanel {
this._panel.openPopup(this._tab, POPUP_OPTIONS);
}, this._prefPreviewDelay);
this._win.addEventListener("TabSelect", this);
this._win.addEventListener("blur", this);
this._panel.addEventListener("popupshowing", this);
}
@ -130,6 +131,9 @@ export default class TabPreviewPanel {
this._thumbnailElement = null;
}
break;
case "blur":
this.deactivate();
break;
}
}

View file

@ -5,6 +5,9 @@
"use strict";
// A test blob that should be unique in the entire browser.
const TEST_ICON_BLOB = new Blob([new Uint8Array([5, 11, 2013])]);
// `URL.createObjectURL()` should be called the first time a blob icon is shown
// while the view is open, and `revokeObjectURL()` should be called when the
// view is closed.
@ -25,11 +28,10 @@ add_task(async function test() {
});
await UrlbarTestUtils.promisePopupClose(window);
// No blob URLs should have been created or revoked since no results that have
// blob icons were matched.
checkCallCounts(spies, {
// No blob URLs should have been created since there were no results with blob
// icons.
await checkCallCounts(spies, null, {
createObjectURL: 0,
revokeObjectURL: 0,
});
// Create a test provider that returns a result with a blob icon.
@ -40,7 +42,7 @@ add_task(async function test() {
UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
{
url: "https://example.com/",
iconBlob: new Blob([new Uint8Array([])]),
iconBlob: TEST_ICON_BLOB,
}
),
],
@ -48,30 +50,24 @@ add_task(async function test() {
UrlbarProvidersManager.registerProvider(provider);
// Do some searches.
await doSearches(provider, spies, {
createObjectURL: 1,
revokeObjectURL: 0,
});
let blobUrl = await doSearches(provider, spies);
// Closing the view should cause `revokeObjectURL()` to be called.
await UrlbarTestUtils.promisePopupClose(window);
checkCallCounts(spies, {
createObjectURL: 1,
await checkCallCounts(spies, blobUrl, {
createObjectURL: 0,
revokeObjectURL: 1,
});
resetSpies(spies);
// Do some more searches.
await doSearches(provider, spies, {
createObjectURL: 2,
revokeObjectURL: 1,
});
// Close the view.
// Do some more searches and close the view again.
blobUrl = await doSearches(provider, spies);
await UrlbarTestUtils.promisePopupClose(window);
checkCallCounts(spies, {
createObjectURL: 2,
revokeObjectURL: 2,
await checkCallCounts(spies, blobUrl, {
createObjectURL: 0,
revokeObjectURL: 1,
});
resetSpies(spies);
// Remove the provider, do another search, and close the view. Since no
// results with blob icons are matched, the call counts should not change.
@ -81,16 +77,16 @@ add_task(async function test() {
value: "test",
});
await UrlbarTestUtils.promisePopupClose(window);
checkCallCounts(spies, {
createObjectURL: 2,
revokeObjectURL: 2,
await checkCallCounts(spies, blobUrl, {
createObjectURL: 0,
revokeObjectURL: 0,
});
sandbox.restore();
});
async function doSearches(provider, spies, expectedCountsByName) {
let previousImage;
async function doSearches(provider, spies) {
let previousBlobUrl;
for (let i = 0; i < 3; i++) {
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
@ -104,15 +100,31 @@ async function doSearches(provider, spies, expectedCountsByName) {
if (i > 0) {
Assert.equal(
result.image,
previousImage,
previousBlobUrl,
"Blob URL should be the same as in previous searches"
);
}
previousImage = result.image;
previousBlobUrl = result.image;
// `createObjectURL()` should be called only once across all searches since
// the view remains open the whole time.
checkCallCounts(spies, expectedCountsByName);
// `createObjectURL()` should be called only once across all searches
// performed in this function since there's only one result with a blob and
// the view remains open the whole time. The URL should be created and
// cached on the first search, and the cached URL should be used for the
// later searches.
await checkCallCounts(spies, result.image, {
createObjectURL: 1,
revokeObjectURL: 0,
});
}
resetSpies(spies);
return previousBlobUrl;
}
function resetSpies(spies) {
for (let spy of Object.values(spies)) {
spy.resetHistory();
}
}
@ -126,8 +138,53 @@ async function getTestResult(provider) {
return null;
}
function checkCallCounts(spies, expectedCountsByName) {
for (let [name, count] of Object.entries(expectedCountsByName)) {
Assert.strictEqual(spies[name].callCount, count, "Spy call count: " + name);
/**
* Checks calls to `createObjectURL()` and `revokeObjectURL()`.
*
* @param {object} spies
* An object that maps spy names to Sinon spies, one entry per function being
* spied on: `{ createObjectURL, revokeObjectURL }`
* @param {Array} knownBlobUrl
* The blob URL that has been created and not yet verified as being revoked,
* or null if no blob URL has been created.
* @param {object} expectedCountsByName
* An object that maps function names to the expected number of times they
* should have been called since spies were last reset:
* `{ createObjectURL, revokeObjectURL }`
*/
async function checkCallCounts(spies, knownBlobUrl, expectedCountsByName) {
// Other parts of the browser may also create and revoke blob URLs, so first
// we filter `createObjectURL()` calls that were passed our test blob.
let createCalls = [];
for (let call of spies.createObjectURL.getCalls()) {
if (await areBlobsEqual(call.args[0], TEST_ICON_BLOB)) {
createCalls.push(call);
}
}
Assert.strictEqual(
createCalls.length,
expectedCountsByName.createObjectURL,
"createObjectURL spy call count"
);
// Similarly there may be other callers of `revokeObjectURL()`, so first we
// filter calls that were passed the known blob URL.
if (knownBlobUrl) {
let calls = spies.revokeObjectURL
.getCalls()
.filter(call => call.args[0] == knownBlobUrl);
Assert.strictEqual(
calls.length,
expectedCountsByName.revokeObjectURL,
"revokeObjectURL spy call count"
);
}
}
async function areBlobsEqual(blob1, blob2) {
let buf1 = new Uint8Array(await blob1.arrayBuffer());
let buf2 = new Uint8Array(await blob2.arrayBuffer());
return (
buf1.length == buf2.length && buf1.every((element, i) => element == buf2[i])
);
}

View file

@ -13,14 +13,13 @@ add_task(async function testSwitchToTabTextDisplay() {
omnibox: {
keyword: "omniboxtest",
},
background() {
/* global browser */
browser.omnibox.setDefaultSuggestion({
description: "doit",
});
// Just do nothing for this test.
},
},
background() {
/* global browser */
browser.omnibox.setDefaultSuggestion({
description: "doit",
});
// Just do nothing for this test.
},
});

View file

@ -110,18 +110,17 @@ add_task(async function test_extension() {
omnibox: {
keyword: "omniboxtest",
},
background() {
/* global browser */
browser.omnibox.setDefaultSuggestion({
description: "doit",
});
// Just do nothing for this test.
browser.omnibox.onInputEntered.addListener(() => {});
browser.omnibox.onInputChanged.addListener((text, suggest) => {
suggest([]);
});
},
},
background() {
/* global browser */
browser.omnibox.setDefaultSuggestion({
description: "doit",
});
// Just do nothing for this test.
browser.omnibox.onInputEntered.addListener(() => {});
browser.omnibox.onInputChanged.addListener((text, suggest) => {
suggest([]);
});
},
});

View file

@ -195,18 +195,18 @@ add_task(async function test_omnibox_result() {
omnibox: {
keyword: "omniboxtest",
},
},
background() {
/* global browser */
browser.omnibox.setDefaultSuggestion({
description: "doit",
});
// Just do nothing for this test.
browser.omnibox.onInputEntered.addListener(() => {});
browser.omnibox.onInputChanged.addListener((text, suggest) => {
suggest([]);
});
},
background() {
/* global browser */
browser.omnibox.setDefaultSuggestion({
description: "doit",
});
// Just do nothing for this test.
browser.omnibox.onInputEntered.addListener(() => {});
browser.omnibox.onInputChanged.addListener((text, suggest) => {
suggest([]);
});
},
});
@ -223,7 +223,7 @@ add_task(async function test_omnibox_result() {
assertElementsDisplayed(details, {
separator: true,
title: "Generated extension",
title: "doit",
type: UrlbarUtils.RESULT_TYPE.OMNIBOX,
});
});

View file

@ -303,14 +303,15 @@ function checkMenuEntriesComment(expectedValues, extraRows = 1) {
is(actualValues.length, expectedLength, " Checking length of expected menu");
for (let i = 0; i < expectedValues.length; i++) {
let val = actualValues[i];
if (val) {
val = JSON.parse(val);
delete val.profile;
val = JSON.stringify(val);
const expectedValue = JSON.parse(expectedValues[i]);
const actualValue = JSON.parse(actualValues[i]);
for (const [key, value] of Object.entries(expectedValue)) {
is(
actualValue[key],
value,
` Checking menu entry #${i}, ${key} should be ${value}`
);
}
is(val, expectedValues[i], " Checking menu entry #" + i);
}
}

View file

@ -57,7 +57,25 @@ function makeAddressComment({ primary, secondary, status, profile }) {
secondary,
status,
ariaLabel: primary + " " + secondary + " " + status,
profile,
fillMessageName: "FormAutofill:FillForm",
fillMessageData: profile,
});
}
function makeCreditCardComment({
primary,
secondary,
ariaLabel,
image,
profile,
}) {
return JSON.stringify({
primary,
secondary,
ariaLabel,
image,
fillMessageName: "FormAutofill:FillForm",
fillMessageData: profile,
});
}
@ -321,7 +339,7 @@ let creditCardTestCases = [
value: "",
style: "autofill",
label: "Timothy Berners-Lee",
comment: JSON.stringify({
comment: makeCreditCardComment({
primary: "Timothy Berners-Lee",
secondary: "••••6785",
ariaLabel: "Visa Timothy Berners-Lee ****6785",
@ -334,7 +352,7 @@ let creditCardTestCases = [
value: "",
style: "autofill",
label: "John Doe",
comment: JSON.stringify({
comment: makeCreditCardComment({
primary: "John Doe",
secondary: "••••1234",
ariaLabel: "American Express John Doe ****1234",
@ -361,7 +379,7 @@ let creditCardTestCases = [
value: "",
style: "autofill",
label: "••••6785",
comment: JSON.stringify({
comment: makeCreditCardComment({
primary: "••••6785",
secondary: "Timothy Berners-Lee",
ariaLabel: "Visa 6785 Timothy Berners-Lee",
@ -374,7 +392,7 @@ let creditCardTestCases = [
value: "",
style: "autofill",
label: "••••1234",
comment: JSON.stringify({
comment: makeCreditCardComment({
primary: "••••1234",
secondary: "John Doe",
ariaLabel: "American Express 1234 John Doe",
@ -387,7 +405,7 @@ let creditCardTestCases = [
value: "",
style: "autofill",
label: "••••5678",
comment: JSON.stringify({
comment: makeCreditCardComment({
primary: "••••5678",
secondary: "",
ariaLabel: "5678",

View file

@ -377,7 +377,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "bf32b15ffce8f9d2723fcc9db8f5f4133b3ac928"
"revision": "cdddd31437dda4530c9f6720c07da2481b70ac95"
},
"de": {
"pin": false,
@ -510,7 +510,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "091c1ed46e048367b1327c5ef1ef1652ced24733"
"revision": "65a26c12e6e9d1ad77f8948e52d989ff770e427a"
},
"es-CL": {
"pin": false,
@ -795,7 +795,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "4716771935f03ddd347193a0db88e1b3c41a87e9"
"revision": "4bf9466a168c4696997b5039ee9c0796aaaa39a7"
},
"gu-IN": {
"pin": false,
@ -909,7 +909,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "e22ac04a215bf56e0a5272dd242aaf0184a5ea28"
"revision": "43352212205f762577018f1b5fc8c796d77f82c7"
},
"hy-AM": {
"pin": false,
@ -966,7 +966,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "ba5cb082496a8b92809ee5d3f34b9501eaf063cd"
"revision": "27c230254b78685d7c4ba71263fd42d0f65b649b"
},
"id": {
"pin": false,
@ -1004,7 +1004,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "349b8a5ac5346f1918444431e5955b0f8b0d241b"
"revision": "6b7afd502c98ded5f24659502a5539da0ce04be9"
},
"it": {
"pin": false,
@ -1162,7 +1162,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "349327c9329d46acdfeb82777bfa60361d138d4f"
"revision": "1d6a0804a979fbb33c62e277f17c47f147c02d3f"
},
"lij": {
"pin": false,
@ -1770,7 +1770,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "a9aa736e05f49b9305fb007571810b77c0edf784"
"revision": "0aba5067b13bcb73ca84410e2bac4ab8ad779188"
},
"sr": {
"pin": false,
@ -2036,7 +2036,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "9c862065927e718924fecace4f4c594a62a3f361"
"revision": "444ece2df819ca058b9c3571631854dc3f39f99f"
},
"wo": {
"pin": false,

View file

@ -1153,6 +1153,11 @@ panelview .toolbarbutton-1,
stroke: var(--panel-item-hover-bgcolor);
}
#appMenu-zoomReset-button2:not([disabled]):hover > .toolbarbutton-text,
#appMenu-fullscreen-button2:not([disabled]):hover > .toolbarbutton-icon {
background-color: var(--panel-item-hover-bgcolor);
}
#appMenu-zoomReset-button2:not([disabled]):active:hover > .toolbarbutton-text,
#appMenu-fullscreen-button2:not([disabled]):active:hover > .toolbarbutton-icon {
background-color: var(--panel-item-active-bgcolor);

View file

@ -85,6 +85,26 @@
last tab is selected */
padding-inline: var(--tab-shadow-max-size);
}
/* Make it easier to drag tabs by expanding the drag area downwards. */
&[movingtab] {
padding-bottom: 15px;
margin-bottom: -15px;
}
}
#navigator-toolbox[movingtab] > #nav-bar {
pointer-events: none;
/* Allow dropping a tab on buttons with associated drop actions. */
> #nav-bar-customization-target {
> #personal-bookmarks,
> #home-button,
> #downloads-button,
> #bookmarks-menu-button {
pointer-events: auto;
}
}
}
.closing-tabs-spacer {
@ -116,8 +136,32 @@
* (we only have 2px padding in the inline direction) */
overflow-clip-margin: 2px;
&:not([pinned]) {
flex: 100 100;
max-width: 225px;
min-width: var(--tab-min-width);
transition: min-width 100ms ease-out,
max-width 100ms ease-out;
:root[uidensity=touch] & {
/* Touch mode needs additional space for the close button. */
min-width: calc(var(--tab-min-width) + 10px);
}
}
&:not([pinned], [fadein]) {
max-width: 0.1px;
min-width: 0.1px;
visibility: hidden;
}
&[pinned] {
flex-shrink: 0;
#tabbrowser-tabs[positionpinnedtabs] > #tabbrowser-arrowscrollbox > & {
position: absolute !important;
display: block;
}
}
/* tabbrowser-tab keyboard focus */
@ -126,6 +170,24 @@
outline: var(--focus-outline);
outline-offset: var(--focus-outline-inset);
}
#tabbrowser-tabs[movingtab] > #tabbrowser-arrowscrollbox > &:is([selected], [multiselected]) {
position: relative;
z-index: 2;
pointer-events: none; /* avoid blocking dragover events on scroll buttons */
}
@media (prefers-reduced-motion: no-preference) {
#tabbrowser-tabs[movingtab] > #tabbrowser-arrowscrollbox > &[fadein]:not([selected]):not([multiselected]),
&[tab-grouping],
&[tabdrop-samewindow] {
transition: transform 200ms var(--animation-easing-function);
}
}
&[tab-grouping][multiselected]:not([selected]) {
z-index: 2;
}
}
.tab-content {
@ -201,6 +263,18 @@
}
}
.tab-icon-pending:not([fadein]),
.tab-icon-image:not([fadein]),
.tab-close-button:not([fadein]),
.tab-background:not([fadein]) {
visibility: hidden;
}
.tab-label:not([fadein]),
.tab-throbber:not([fadein]) {
display: none;
}
/* Width/height & margins apply on tab icon stack children */
.tab-throbber,
.tab-icon-pending,
@ -751,26 +825,46 @@ toolbar:not(#TabsToolbar) #firefox-view-button {
list-style-image: url(chrome://global/skin/icons/plus.svg);
}
#tabbrowser-tabs[hasadjacentnewtabbutton]:not([overflow]) ~ #new-tab-button,
#tabbrowser-tabs[overflow] > #tabbrowser-arrowscrollbox > #tabbrowser-arrowscrollbox-periphery > #tabs-newtab-button,
#tabbrowser-tabs:not([hasadjacentnewtabbutton]) > #tabbrowser-arrowscrollbox > #tabbrowser-arrowscrollbox-periphery > #tabs-newtab-button,
#TabsToolbar[customizing="true"] #tabs-newtab-button {
display: none;
}
/* All tabs button and menupopup */
#alltabs-button {
list-style-image: url(chrome://global/skin/icons/arrow-down.svg);
}
#tabbrowser-tabs[hiddensoundplaying] ~ #alltabs-button > .toolbarbutton-badge-stack > .toolbarbutton-badge {
background: transparent url(chrome://browser/skin/tabbrowser/tab-audio-playing-small.svg);
box-shadow: none;
/* Match the color of the button, rather than label default. */
color: inherit;
display: block;
-moz-context-properties: fill, fill-opacity, stroke;
fill: currentColor;
stroke: transparent;
/* "!important" is necessary to override the rule in toolbarbutton.css */
margin: -7px 0 0 !important;
margin-inline-end: -4px !important;
min-width: 12px;
min-height: 12px;
/* stylelint-disable-next-line media-query-no-invalid */
@media not (-moz-bool-pref: "browser.tabs.tabmanager.enabled") {
#tabbrowser-tabs:not([overflow], [hashiddentabs]) ~ & {
display: none;
}
#tabbrowser-tabs:not([overflow])[using-closing-tabs-spacer] ~ & {
/* temporary space to keep a tab's close button under the cursor */
display: flex;
visibility: hidden;
}
}
#tabbrowser-tabs[hiddensoundplaying] ~ & > .toolbarbutton-badge-stack > .toolbarbutton-badge {
background: transparent url(chrome://browser/skin/tabbrowser/tab-audio-playing-small.svg);
box-shadow: none;
/* Match the color of the button, rather than label default. */
color: inherit;
display: block;
-moz-context-properties: fill, fill-opacity, stroke;
fill: currentColor;
stroke: transparent;
/* "!important" is necessary to override the rule in toolbarbutton.css */
margin: -7px 0 0 !important;
margin-inline-end: -4px !important;
min-width: 12px;
min-height: 12px;
}
}
/* The list of tabs is in its own panel-subview-body which will scroll. We don't
@ -784,6 +878,27 @@ toolbar:not(#TabsToolbar) #firefox-view-button {
padding-top: 0;
}
#allTabsMenu-dropIndicatorHolder {
display: block;
position: relative;
}
#allTabsMenu-dropIndicator {
background: url(chrome://browser/skin/tabbrowser/tab-drag-indicator.svg) no-repeat center;
display: block;
position: absolute;
transform: rotate(-90deg);
width: 12px;
height: 29px;
inset-inline-start: 8px;
top: 0;
pointer-events: none;
&:-moz-locale-dir(rtl) {
transform: rotate(90deg);
}
}
.all-tabs-item {
margin-inline: var(--arrowpanel-menuitem-margin-inline);
border-radius: var(--arrowpanel-menuitem-border-radius);

View file

@ -23,36 +23,10 @@ if test -z "$_MOZ_USE_RTTI"; then
fi
])
dnl ========================================================
dnl =
dnl = Debugging Options
dnl =
dnl ========================================================
AC_DEFUN([MOZ_DEBUGGING_OPTS],
[
if test -n "$MOZ_DEBUG"; then
if test -n "$COMPILE_ENVIRONMENT"; then
AC_MSG_CHECKING([for valid debug flags])
_SAVE_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS $MOZ_DEBUG_FLAGS"
AC_TRY_COMPILE([#include <stdio.h>],
[printf("Hello World\n");],
_results=yes,
_results=no)
AC_MSG_RESULT([$_results])
if test "$_results" = "no"; then
AC_MSG_ERROR([These compiler flags are invalid: $MOZ_DEBUG_FLAGS])
fi
CFLAGS=$_SAVE_CFLAGS
fi
fi
])
dnl A high level macro for selecting compiler options.
AC_DEFUN([MOZ_COMPILER_OPTS],
[
MOZ_DEBUGGING_OPTS
MOZ_RTTI
if test "$GNU_CC"; then

View file

@ -115,6 +115,23 @@ def moz_no_debug_rtl(moz_debug, asan, target, enable_jemalloc):
set_config("MOZ_NO_DEBUG_RTL", moz_no_debug_rtl)
@depends(
try_compile(
includes=["stdio.h"],
body='puts("Hello World");',
check_msg="for valid debug flags",
flags=debug_flags,
when=moz_debug,
),
debug_flags,
when=moz_debug,
)
@imports(_from="mozbuild.shellutil", _import="quote")
def check_debug_flags(check, flags):
if not check:
die(f"These compiler flags are invalid: {quote(*flags)}")
# Try to make builds more reproducible and allow sharing built artifacts across
# source and object directories by using -ffile-prefix-map and friends. To
# "unwind" the prefix maps, use:

View file

@ -2151,13 +2151,13 @@ try_compile(
def default_debug_flags(compiler_info, target):
# Debug info is ON by default.
if compiler_info.type == "clang-cl":
return "-Z7"
return ("-Z7",)
elif target.kernel == "WINNT" and compiler_info.type == "clang":
return "-g -gcodeview"
return ("-g", "-gcodeview")
# The oldest versions of supported compilers default to DWARF-4, but
# newer versions may default to DWARF-5 or newer (e.g. clang 14), which
# Valgrind doesn't support. Force-use DWARF-4.
return "-gdwarf-4"
return ("-gdwarf-4",)
option(env="MOZ_DEBUG_FLAGS", nargs=1, help="Debug compiler flags")
@ -2174,14 +2174,15 @@ set_config("MOZ_DEBUG_SYMBOLS", depends_if("--enable-debug-symbols")(lambda _: T
@depends("MOZ_DEBUG_FLAGS", "--enable-debug-symbols", default_debug_flags)
@imports(_from="mozbuild.shellutil", _import="split")
def debug_flags(env_debug_flags, enable_debug_flags, default_debug_flags):
# If MOZ_DEBUG_FLAGS is set, and --enable-debug-symbols is set to a value,
# --enable-debug-symbols takes precedence. Note, the value of
# --enable-debug-symbols may be implied by --enable-debug.
if len(enable_debug_flags):
return enable_debug_flags[0]
return split(enable_debug_flags[0])
if env_debug_flags:
return env_debug_flags[0]
return split(env_debug_flags[0])
return default_debug_flags

View file

@ -1,11 +0,0 @@
[package]
name = "cfg-if"
version = "0.1.999"
edition = "2018"
license = "MPL-2.0"
[lib]
path = "lib.rs"
[dependencies]
cfg-if = "1.0"

View file

@ -0,0 +1,14 @@
[package]
name = "socket2"
version = "0.4.999"
edition = "2021"
license = "MPL-2.0"
[lib]
path = "lib.rs"
[dependencies.socket2]
version = "0.5"
[features]
all = ["socket2/all"]

View file

@ -2,4 +2,4 @@
* 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/. */
pub use cfg_if::*;
pub use socket2::*;

View file

@ -136,6 +136,7 @@ addIntegrationTask(async function testReloadingChangedGeneratedSource(
// Assert that it does not pause in commpressed files
assertNotPaused(dbg);
}
await waitForBreakpoint(dbg, "original-with-no-update.js", 6);
await assertBreakpoint(dbg, 6);
info(

View file

@ -85,6 +85,9 @@ add_task(async function openingWithDevToolsButUnknownSource() {
);
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
// View source may not have updated the selection just yet
ContentTaskUtils.waitForCondition(() => !!content.getSelection());
const selection = content.getSelection();
Assert.equal(
selection.toString(),

View file

@ -73,13 +73,40 @@ High priority tasks are also “drop everything”, except that in this case “
Handling needinfos
------------------
TL;DR: Dont leave people hanging. Reply and convert needinfos from others to self-needinfos.
TL;DR: Dont leave people hanging.
Many people have long lists of needinfos. Please dont ignore them. Heres how we suggest you burn them down, and keep them down:
Many people have long lists of needinfos. Please dont ignore them. Heres how we suggest you burn them down, and keep them down.
* Any needinfo older than 3 months has probably been forgotten about by the requester. Its okay to declare needinfo bankruptcy. If youre concerned about annoying the requester by clearing the needinfo, feel free to point them at this document and offer that they can re-request if there is still a problem.
* For newer requests, dont leave someone or something, .e.g BugBot hanging. If you can take action in the short term, do so. If you cant do it straight away, reply letting the requester know that you intend to work on it, but cant do so immediately. This clears the original needinfo. At the same time you can re-request a needinfo (using the “Request information from myself” feature). This converts the needinfo to a self-needinfo, and takes it off the attention list. Be mindful, do the conversion when you have given the right attention and response so that others are not hanging on there, or you've done the best you can for the time being to unblock others. We don't want it to become a way to ignore requests.
Old needinfos
~~~~~~~~~~~~~
Any needinfo older than 3 months has probably been forgotten about by the requester. Its okay to declare needinfo bankruptcy. Consider replying something like:
* `Sorry that I didn't get to this needinfo earlier. Please request again if this needinfo is still needed.`
This clears the needinfo and offers that the requester can ask again if there is still a problem.
If youre concerned about annoying the requester by clearing the needinfo, feel free to point them at this document.
New needinfos
~~~~~~~~~~~~~
For newer requests, dont leave someone or something, .e.g BugBot hanging. If you can take action in the short term, do so. If you cant do it straight away to come with a detailed response, consider replying something that might help move on the discussion, like:
* `I don't know, but you could do X in order to find out.`
* `Can you please provide more information about Y?`
* `I believe Z (someone else) can help.`
Providing visibility of your current plan helps move the needle, too. You can also consider replying something like:
* `Sorry that I don't have a quick thought or I don't expect to have bandwidth to help it now. Please request again if this should deserve a higher priority.`
This clears the needinfo.
Review requests

View file

@ -82,9 +82,9 @@ eslint-plugin-mozilla is used by a few projects outside of mozilla-central,
so they will pick up the rule addition when eslint-plugin-mozilla is next released.
Where existing failures are disabled/turned to warnings, these should be handled
in the :searchfox:`top-level .eslintrc.js file <.eslintrc.js>`, and follow-up bugs
must be filed before landing and referenced in the appropriate sections. The
follow-up bugs should block
in the :searchfox:`top-level .eslintrc-rollouts.js file <..eslintrc-rollouts.js>`,
and follow-up bugs must be filed before landing and referenced in the appropriate
sections. The follow-up bugs should block
`bug 1596191 <https://bugzilla.mozilla.org/show_bug.cgi?id=1596191>`_
Adding a New ESLint Plugin

View file

@ -35,9 +35,7 @@ very diverse skills:
- If you know **Rust**, you can also contribute to the `Rust programming
language <https://github.com/rust-lang/rust>`_ itself, numerous crates like `grcov <https://github.com/mozilla/grcov/>`_
or `Servo <https://servo.org/>`_, the web browser engine designed for parallelism and safety.
- If you know **Kotlin**, you can contribute to `Firefox
for Android <https://github.com/mozilla-mobile/fenix>`_ (code name:
"Fenix").
- If you know **Kotlin**, you can contribute to :ref:`Firefox for Android <Firefox Contributors' Quick Reference>` (code name: "Fenix"). `Fenix's code <https://searchfox.org/mozilla-central/source/mobile/android/>`_ is integrated into the same repository as Firefox Desktop.
- If you know **Swift**, you can contribute to `Firefox for
iOS <https://github.com/mozilla-mobile/firefox-ios>`_ and `Firefox
Focus for iOS <https://github.com/mozilla-mobile/focus-ios>`_.

View file

@ -94,8 +94,8 @@ If you aren't modifying the Firefox backend, then select one of the
:ref:`Artifact Mode <Understanding Artifact Builds>` options. If you are
building Firefox for Android, you should also see the :ref:`GeckoView Contributor Guide <geckoview-contributor-guide>`.
3. Build
--------
3. Build and Run
----------------
Now that your system is bootstrapped, you should be able to build!
@ -118,6 +118,15 @@ You can now use the ``./mach run`` command to run your locally built Firefox!
If your build fails, please reference the steps in the `Troubleshooting section <#troubleshooting>`_.
Signing
~~~~~~~
Code signing your Mac build is not required for local testing and is rarely
needed for development. The way Firefox is signed does impact functionality
such as passkey support so it is required in some cases. Generating a build as
close to a production build as possible requires code signing.
See :ref:`Signing Local macOS Builds` for more information.
Running outside the development environment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -127,7 +136,18 @@ To test your changes on another macOS system (or to keep that particular Firefox
./mach package
Copy the resulting ``.dmg`` file from ``obj-*/dist/`` to the target system, then double-click it as usual to find an ``.app`` bundle containing all dependencies. In newer versions of MacOS you will need to sign the package for this to work using :ref:`Signing Local macOS Builds`.
Copy the resulting ``.dmg`` file from ``obj-*/dist/`` to the target system,
then double-click it as usual to find an ``.app`` bundle containing all
dependencies.
On Apple Silicon Macs, you will need to sign the build for this to work using
:ref:`Signing Local macOS Builds`.
Once the build has been copied to the target system, open it with
right-click->Open. The build will not launch by default because it is not
notarized. In addition to code signing, notarization is required on macOS
10.15+ for a downloaded app to be launchable by double clicking the app in
Finder.
Now the fun starts
------------------

View file

@ -1266,8 +1266,9 @@ void Animation::WillComposeStyle() {
}
}
void Animation::ComposeStyle(StyleAnimationValueMap& aComposeResult,
const nsCSSPropertyIDSet& aPropertiesToSkip) {
void Animation::ComposeStyle(
StyleAnimationValueMap& aComposeResult,
const InvertibleAnimatedPropertyIDSet& aPropertiesToSkip) {
if (!mEffect) {
return;
}

View file

@ -9,6 +9,7 @@
#include "X11UndefineNone.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/AnimatedPropertyIDSet.h"
#include "mozilla/AnimationPerformanceWarning.h"
#include "mozilla/Attributes.h"
#include "mozilla/BasePrincipal.h"
@ -330,7 +331,7 @@ class Animation : public DOMEventTargetHelper,
* updated in |aComposeResult|.
*/
void ComposeStyle(StyleAnimationValueMap& aComposeResult,
const nsCSSPropertyIDSet& aPropertiesToSkip);
const InvertibleAnimatedPropertyIDSet& aPropertiesToSkip);
void NotifyEffectTimingUpdated();
void NotifyEffectPropertiesUpdated();
@ -344,7 +345,7 @@ class Animation : public DOMEventTargetHelper,
* is canceled, it will be released by its owning element and may not still
* exist when we would normally go to queue events on the next tick.
*/
virtual void MaybeQueueCancelEvent(const StickyTimeDuration& aActiveTime){};
virtual void MaybeQueueCancelEvent(const StickyTimeDuration& aActiveTime) {};
Maybe<uint32_t>& CachedChildIndexRef() { return mCachedChildIndex; }

View file

@ -372,7 +372,7 @@ static void ComposeSortedEffects(
StyleAnimationValueMap* aAnimationValues) {
const bool isTransition =
aCascadeLevel == EffectCompositor::CascadeLevel::Transitions;
nsCSSPropertyIDSet propertiesToSkip;
InvertibleAnimatedPropertyIDSet propertiesToSkip;
// Transitions should be overridden by running animations of the same
// property per https://drafts.csswg.org/css-transitions/#application:
//
@ -386,9 +386,11 @@ static void ComposeSortedEffects(
//
// MOZ_ASSERT_IF(aEffectSet, !aEffectSet->CascadeNeedsUpdate());
if (aEffectSet) {
propertiesToSkip =
isTransition ? aEffectSet->PropertiesForAnimationsLevel()
: aEffectSet->PropertiesForAnimationsLevel().Inverse();
// Note that we do invert the set on CascadeLevel::Animations because we
// don't want to skip those properties when composing the animation rule on
// CascadeLevel::Animations.
propertiesToSkip.Setup(&aEffectSet->PropertiesForAnimationsLevel(),
!isTransition);
}
for (KeyframeEffect* effect : aSortedEffects) {
@ -608,9 +610,11 @@ nsCSSPropertyIDSet EffectCompositor::GetOverriddenProperties(
nsCSSPropertyIDSet propertiesToTrackAsSet;
for (KeyframeEffect* effect : aEffectSet) {
for (const AnimationProperty& property : effect->Properties()) {
// Custom properties don't run on the compositor.
if (property.mProperty.IsCustom()) {
continue;
}
if (nsCSSProps::PropHasFlags(property.mProperty.mID,
CSSPropFlags::CanAnimateOnCompositor) &&
!propertiesToTrackAsSet.HasProperty(property.mProperty.mID)) {
@ -663,8 +667,6 @@ void EffectCompositor::UpdateCascadeResults(EffectSet& aEffectSet,
nsCSSPropertyIDSet& propertiesWithImportantRules =
aEffectSet.PropertiesWithImportantRules();
nsCSSPropertyIDSet& propertiesForAnimationsLevel =
aEffectSet.PropertiesForAnimationsLevel();
static constexpr nsCSSPropertyIDSet compositorAnimatables =
nsCSSPropertyIDSet::CompositorAnimatables();
@ -673,13 +675,10 @@ void EffectCompositor::UpdateCascadeResults(EffectSet& aEffectSet,
nsCSSPropertyIDSet prevCompositorPropertiesWithImportantRules =
propertiesWithImportantRules.Intersect(compositorAnimatables);
nsCSSPropertyIDSet prevPropertiesForAnimationsLevel =
propertiesForAnimationsLevel;
propertiesWithImportantRules.Empty();
propertiesForAnimationsLevel.Empty();
nsCSSPropertyIDSet propertiesForTransitionsLevel;
AnimatedPropertyIDSet propertiesForAnimationsLevel;
AnimatedPropertyIDSet propertiesForTransitionsLevel;
for (const KeyframeEffect* effect : sortedEffectList) {
MOZ_ASSERT(effect->GetAnimation(),
@ -687,19 +686,21 @@ void EffectCompositor::UpdateCascadeResults(EffectSet& aEffectSet,
CascadeLevel cascadeLevel = effect->GetAnimation()->CascadeLevel();
for (const AnimationProperty& prop : effect->Properties()) {
if (prop.mProperty.IsCustom()) {
continue;
}
if (overriddenProperties.HasProperty(prop.mProperty.mID)) {
// Note that nsCSSPropertyIDSet::HasProperty() returns false for custom
// properties. We don't support custom properties for compositor
// animations, so we are still using nsCSSPropertyIDSet to handle these
// properties.
// TODO: Bug 1869475. Support custom properties for compositor animations.
if (overriddenProperties.HasProperty(prop.mProperty)) {
propertiesWithImportantRules.AddProperty(prop.mProperty.mID);
}
switch (cascadeLevel) {
case EffectCompositor::CascadeLevel::Animations:
propertiesForAnimationsLevel.AddProperty(prop.mProperty.mID);
propertiesForAnimationsLevel.AddProperty(prop.mProperty);
break;
case EffectCompositor::CascadeLevel::Transitions:
propertiesForTransitionsLevel.AddProperty(prop.mProperty.mID);
propertiesForTransitionsLevel.AddProperty(prop.mProperty);
break;
}
}
@ -707,6 +708,13 @@ void EffectCompositor::UpdateCascadeResults(EffectSet& aEffectSet,
aEffectSet.MarkCascadeUpdated();
// Update EffectSet::mPropertiesForAnimationsLevel to the new set, after
// exiting this scope.
auto scopeExit = MakeScopeExit([&] {
aEffectSet.PropertiesForAnimationsLevel() =
std::move(propertiesForAnimationsLevel);
});
nsPresContext* presContext = nsContentUtils::GetContextForContent(aElement);
if (!presContext) {
return;
@ -726,10 +734,13 @@ void EffectCompositor::UpdateCascadeResults(EffectSet& aEffectSet,
// If we have transition properties and if the same propery for animations
// level is newly added or removed, we need to update the transition level
// rule since the it will be added/removed from the rule tree.
nsCSSPropertyIDSet changedPropertiesForAnimationLevel =
const AnimatedPropertyIDSet& prevPropertiesForAnimationsLevel =
aEffectSet.PropertiesForAnimationsLevel();
const AnimatedPropertyIDSet& changedPropertiesForAnimationLevel =
prevPropertiesForAnimationsLevel.Xor(propertiesForAnimationsLevel);
nsCSSPropertyIDSet commonProperties = propertiesForTransitionsLevel.Intersect(
changedPropertiesForAnimationLevel);
const AnimatedPropertyIDSet& commonProperties =
propertiesForTransitionsLevel.Intersect(
changedPropertiesForAnimationLevel);
if (!commonProperties.IsEmpty()) {
EffectCompositor::RestyleType restyleType =
changedPropertiesForAnimationLevel.Intersects(compositorAnimatables)

View file

@ -188,10 +188,10 @@ class EffectSet {
nsCSSPropertyIDSet& PropertiesWithImportantRules() {
return mPropertiesWithImportantRules;
}
nsCSSPropertyIDSet PropertiesForAnimationsLevel() const {
const AnimatedPropertyIDSet& PropertiesForAnimationsLevel() const {
return mPropertiesForAnimationsLevel;
}
nsCSSPropertyIDSet& PropertiesForAnimationsLevel() {
AnimatedPropertyIDSet& PropertiesForAnimationsLevel() {
return mPropertiesForAnimationsLevel;
}
@ -221,7 +221,7 @@ class EffectSet {
// Specifies the properties for which the result will be added to the
// animations level of the cascade and hence should be skipped when we are
// composing the animation style for the transitions level of the cascede.
nsCSSPropertyIDSet mPropertiesForAnimationsLevel;
AnimatedPropertyIDSet mPropertiesForAnimationsLevel;
#ifdef DEBUG
// Track how many iterators are referencing this effect set when we are

View file

@ -608,8 +608,9 @@ void KeyframeEffect::ComposeStyleRule(StyleAnimationValueMap& aAnimationValues,
&aComputedTiming, mEffectOptions.mIterationComposite);
}
void KeyframeEffect::ComposeStyle(StyleAnimationValueMap& aComposeResult,
const nsCSSPropertyIDSet& aPropertiesToSkip) {
void KeyframeEffect::ComposeStyle(
StyleAnimationValueMap& aComposeResult,
const InvertibleAnimatedPropertyIDSet& aPropertiesToSkip) {
ComputedTiming computedTiming = GetComputedTiming();
// If the progress is null, we don't have fill data for the current
@ -1652,8 +1653,8 @@ bool KeyframeEffect::ShouldBlockAsyncTransformAnimations(
// transform-related animations on the main thread (where we have the
// !important rule).
nsCSSPropertyIDSet blockedProperties =
effectSet->PropertiesWithImportantRules().Intersect(
effectSet->PropertiesForAnimationsLevel());
effectSet->PropertiesForAnimationsLevel().Intersect(
effectSet->PropertiesWithImportantRules());
if (blockedProperties.Intersects(aPropertySet)) {
aPerformanceWarning =
AnimationPerformanceWarning::Type::TransformIsBlockedByImportantRules;

View file

@ -76,7 +76,7 @@ struct AnimationProperty {
// The copy constructor/assignment doesn't copy mIsRunningOnCompositor and
// mPerformanceWarning.
AnimationProperty() : mProperty(eCSSProperty_UNKNOWN){};
AnimationProperty() : mProperty(eCSSProperty_UNKNOWN) {};
AnimationProperty(const AnimationProperty& aOther)
: mProperty(aOther.mProperty), mSegments(aOther.mSegments.Clone()) {}
AnimationProperty& operator=(const AnimationProperty& aOther) {
@ -275,7 +275,7 @@ class KeyframeEffect : public AnimationEffect {
// AnimationEffect for the current time except any properties contained
// in |aPropertiesToSkip|.
void ComposeStyle(StyleAnimationValueMap& aComposeResult,
const nsCSSPropertyIDSet& aPropertiesToSkip);
const InvertibleAnimatedPropertyIDSet& aPropertiesToSkip);
// Returns true if at least one property is being animated on compositor.
bool IsRunningOnCompositor() const;

View file

@ -30,11 +30,10 @@ using namespace mozilla;
using namespace mozilla::dom;
DOMParser::DOMParser(nsIGlobalObject* aOwner, nsIPrincipal* aDocPrincipal,
nsIURI* aDocumentURI, nsIURI* aBaseURI)
nsIURI* aDocumentURI)
: mOwner(aOwner),
mPrincipal(aDocPrincipal),
mDocumentURI(aDocumentURI),
mBaseURI(aBaseURI),
mForceEnableXULXBL(false),
mForceEnableDTD(false) {
MOZ_ASSERT(aDocPrincipal);
@ -249,21 +248,18 @@ already_AddRefed<DOMParser> DOMParser::Constructor(const GlobalObject& aOwner,
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIPrincipal> docPrincipal = aOwner.GetSubjectPrincipal();
nsCOMPtr<nsIURI> documentURI;
nsIURI* baseURI = nullptr;
if (docPrincipal->IsSystemPrincipal()) {
docPrincipal = NullPrincipal::Create(OriginAttributes());
documentURI = docPrincipal->GetURI();
} else {
// Grab document and base URIs off the window our constructor was
// called on. Error out if anything untoward happens.
// Grab document URI off the window our constructor was called on.
// Error out if anything untoward happens.
nsCOMPtr<nsPIDOMWindowInner> window =
do_QueryInterface(aOwner.GetAsSupports());
if (!window) {
rv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
baseURI = window->GetDocBaseURI();
documentURI = window->GetDocumentURI();
}
@ -275,7 +271,7 @@ already_AddRefed<DOMParser> DOMParser::Constructor(const GlobalObject& aOwner,
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aOwner.GetAsSupports());
MOZ_ASSERT(global);
RefPtr<DOMParser> domParser =
new DOMParser(global, docPrincipal, documentURI, baseURI);
new DOMParser(global, docPrincipal, documentURI);
return domParser.forget();
}
@ -291,7 +287,7 @@ already_AddRefed<DOMParser> DOMParser::CreateWithoutGlobal(ErrorResult& aRv) {
}
RefPtr<DOMParser> domParser =
new DOMParser(nullptr, docPrincipal, documentURI, nullptr);
new DOMParser(nullptr, docPrincipal, documentURI);
return domParser.forget();
}
@ -311,7 +307,7 @@ already_AddRefed<Document> DOMParser::SetUpDocument(DocumentFlavor aFlavor,
nsCOMPtr<Document> doc;
nsresult rv = NS_NewDOMDocument(getter_AddRefs(doc), u""_ns, u""_ns, nullptr,
mDocumentURI, mBaseURI, mPrincipal, true,
mDocumentURI, mDocumentURI, mPrincipal, true,
scriptHandlingObject, aFlavor);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);

View file

@ -77,7 +77,7 @@ class DOMParser final : public nsISupports, public nsWrapperCache {
private:
DOMParser(nsIGlobalObject* aOwner, nsIPrincipal* aDocPrincipal,
nsIURI* aDocumentURI, nsIURI* aBaseURI);
nsIURI* aDocumentURI);
already_AddRefed<Document> SetUpDocument(DocumentFlavor aFlavor,
ErrorResult& aRv);
@ -85,7 +85,6 @@ class DOMParser final : public nsISupports, public nsWrapperCache {
nsCOMPtr<nsIGlobalObject> mOwner;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsIURI> mDocumentURI;
nsCOMPtr<nsIURI> mBaseURI;
bool mForceEnableXULXBL;
bool mForceEnableDTD;

View file

@ -19139,6 +19139,11 @@ already_AddRefed<Document> Document::ParseHTMLUnsafe(GlobalObject& aGlobal,
doc->SetAllowDeclarativeShadowRoots(true);
doc->SetDocumentURI(uri);
nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
do_QueryInterface(aGlobal.GetAsSupports());
doc->SetScriptHandlingObject(scriptHandlingObject);
doc->SetDocumentCharacterSet(UTF_8_ENCODING);
rv = nsContentUtils::ParseDocumentHTML(aHTML, doc, false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;

View file

@ -0,0 +1,10 @@
<title>Ensure GetCommonFlattenedTreeAncestorForSelection won't crash when the provided contents are equal</title>
<script>
window.addEventListener("DOMContentLoaded", () => {
let b = a.attachShadow({mode: "open"})
let c = document.getSelection()
b.append(null)
c.setBaseAndExtent(a, 1, b, 1)
})
</script>
<blockquote id="a">

View file

@ -277,3 +277,4 @@ load 1887963_1.html
load 1887963_2.html
asserts(0-1) load 1887974.html
load 1890888.html
load 1897248.html

View file

@ -3033,6 +3033,10 @@ nsIContent* nsContentUtils::GetCommonFlattenedTreeAncestorHelper(
/* static */
nsIContent* nsContentUtils::GetCommonFlattenedTreeAncestorForSelection(
nsIContent* aContent1, nsIContent* aContent2) {
if (aContent1 == aContent2) {
return aContent1;
}
return GetCommonAncestorInternal(
aContent1, aContent2, [](nsIContent* aContent) {
return aContent->GetFlattenedTreeParentNodeForSelection();

View file

@ -13629,12 +13629,14 @@ class ClassMethod(ClassItem):
override=False,
canRunScript=False,
noDiscard=False,
delete=False,
):
"""
override indicates whether to flag the method as override
"""
assert not override or virtual
assert not (override and static)
assert not (delete and body)
self.returnType = returnType
self.args = args
self.inline = inline or bodyInHeader
@ -13649,6 +13651,7 @@ class ClassMethod(ClassItem):
self.override = override
self.canRunScript = canRunScript
self.noDiscard = noDiscard
self.delete = delete
ClassItem.__init__(self, name, visibility)
def getDecorators(self, declaring):
@ -13680,7 +13683,9 @@ class ClassMethod(ClassItem):
else ""
)
args = ", ".join([a.declare() for a in self.args])
if self.bodyInHeader:
if self.delete:
body = " = delete;\n"
elif self.bodyInHeader:
body = indent(self.getBody())
body = "\n{\n" + body + "}\n"
else:
@ -13703,7 +13708,7 @@ class ClassMethod(ClassItem):
)
def define(self, cgClass):
if self.bodyInHeader:
if self.delete or self.bodyInHeader:
return ""
templateArgs = cgClass.templateArgs
@ -17691,23 +17696,48 @@ class CGDictionary(CGThing):
body=body.define(),
)
def canHaveEqualsOperator(self):
return all(
m.type.isString() or m.type.isPrimitive() for (m, _) in self.memberInfo
)
def equalityOperator(self):
# For now we only allow equality operators if our members have a string
# type, a primitive type or an enum type.
if not all(
m.type.isString() or m.type.isPrimitive() or m.type.isEnum()
for m in self.dictionary.members
):
err = (
"[GenerateEqualityOperator] set on %s, but it"
% self.dictionary.needsEqualityOperator.identifier.name
)
if self.dictionary.needsEqualityOperator != self.dictionary:
err += "s ancestor %s" % self.dictionary.identifier.name
err += " contains types other than string, primitive or enum types."
raise TypeError(err)
def equalsOperator(self):
body = CGList([])
if self.dictionary.parent:
# If we have a parent dictionary we have to call its equals
# operator.
parentTest = CGGeneric(
fill(
"""
if (!${base}::operator==(aOther)) {
return false;
}
""",
base=self.makeClassName(self.dictionary.parent),
)
)
body.append(parentTest)
for m, _ in self.memberInfo:
memberName = self.makeMemberName(m.identifier.name)
memberTest = CGGeneric(
fill(
"""
if (${memberName} != aOther.${memberName}) {
return false;
}
""",
if (${memberName} != aOther.${memberName}) {
return false;
}
""",
memberName=memberName,
)
)
@ -17829,8 +17859,22 @@ class CGDictionary(CGThing):
else:
disallowCopyConstruction = True
if self.canHaveEqualsOperator():
methods.append(self.equalsOperator())
if d.needsEqualityOperator:
methods.append(self.equalityOperator())
elif d.parent and d.parent.needsEqualityOperator:
methods.append(
ClassMethod(
"operator==",
"bool",
[
Argument(
"const %s&" % self.makeClassName(self.dictionary), "aOther"
)
],
visibility="public",
delete=True,
)
)
struct = CGClass(
selfName,

View file

@ -2232,6 +2232,7 @@ class IDLDictionary(IDLObjectWithScope):
"_extendedAttrDict",
"needsConversionToJS",
"needsConversionFromJS",
"needsEqualityOperator",
)
def __init__(self, location, parentScope, name, parent, members):
@ -2246,6 +2247,7 @@ class IDLDictionary(IDLObjectWithScope):
self._extendedAttrDict = {}
self.needsConversionToJS = False
self.needsConversionFromJS = False
self.needsEqualityOperator = None
IDLObjectWithScope.__init__(self, location, parentScope, name)
@ -2310,6 +2312,14 @@ class IDLDictionary(IDLObjectWithScope):
[self.identifier.location],
)
inheritedMembers.extend(ancestor.members)
if (
self.getExtendedAttribute("GenerateEqualityOperator")
and ancestor.needsEqualityOperator is None
):
# Store the dictionary that has the [GenerateEqualityOperator]
# extended attribute, so we can use it when generating error
# messages.
ancestor.needsEqualityOperator = self
ancestor = ancestor.parent
# Catch name duplication
@ -2435,6 +2445,13 @@ class IDLDictionary(IDLObjectWithScope):
# implement ToJSON by converting to a JS object and
# then using JSON.stringify.
self.needsConversionToJS = True
elif identifier == "GenerateEqualityOperator":
if not attr.noArguments():
raise WebIDLError(
"[GenerateEqualityOperator] must take no arguments",
[attr.location],
)
self.needsEqualityOperator = self
elif identifier == "Unsorted":
if not attr.noArguments():
raise WebIDLError(

View file

@ -9,7 +9,6 @@
#include "imgIRequest.h"
#include "mozilla/dom/Element.h"
#include "nsTHashtable.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "nsContentUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/UniquePtr.h"
@ -26,11 +25,11 @@ using namespace gfx;
* due to CORS security.
*/
struct ImageCacheKey {
ImageCacheKey(imgIContainer* aImage, HTMLCanvasElement* aCanvas,
ImageCacheKey(imgIContainer* aImage, CanvasRenderingContext2D* aContext,
BackendType aBackendType)
: mImage(aImage), mCanvas(aCanvas), mBackendType(aBackendType) {}
: mImage(aImage), mContext(aContext), mBackendType(aBackendType) {}
nsCOMPtr<imgIContainer> mImage;
HTMLCanvasElement* mCanvas;
CanvasRenderingContext2D* mContext;
BackendType mBackendType;
};
@ -41,7 +40,7 @@ struct ImageCacheKey {
struct ImageCacheEntryData {
ImageCacheEntryData(const ImageCacheEntryData& aOther)
: mImage(aOther.mImage),
mCanvas(aOther.mCanvas),
mContext(aOther.mContext),
mBackendType(aOther.mBackendType),
mSourceSurface(aOther.mSourceSurface),
mSize(aOther.mSize),
@ -49,7 +48,7 @@ struct ImageCacheEntryData {
mCropRect(aOther.mCropRect) {}
explicit ImageCacheEntryData(const ImageCacheKey& aKey)
: mImage(aKey.mImage),
mCanvas(aKey.mCanvas),
mContext(aKey.mContext),
mBackendType(aKey.mBackendType) {}
nsExpirationState* GetExpirationState() { return &mState; }
@ -57,7 +56,7 @@ struct ImageCacheEntryData {
// Key
nsCOMPtr<imgIContainer> mImage;
HTMLCanvasElement* mCanvas;
CanvasRenderingContext2D* mContext;
BackendType mBackendType;
// Value
RefPtr<SourceSurface> mSourceSurface;
@ -79,13 +78,13 @@ class ImageCacheEntry : public PLDHashEntryHdr {
~ImageCacheEntry() = default;
bool KeyEquals(KeyTypePointer key) const {
return mData->mImage == key->mImage && mData->mCanvas == key->mCanvas &&
return mData->mImage == key->mImage && mData->mContext == key->mContext &&
mData->mBackendType == key->mBackendType;
}
static KeyTypePointer KeyToPointer(KeyType& key) { return &key; }
static PLDHashNumber HashKey(KeyTypePointer key) {
return HashGeneric(key->mImage.get(), key->mCanvas, key->mBackendType);
return HashGeneric(key->mImage.get(), key->mContext, key->mBackendType);
}
enum { ALLOW_MEMMOVE = true };
@ -152,7 +151,7 @@ class ImageCache final : public nsExpirationTracker<ImageCacheEntryData, 4> {
AllCanvasImageCacheKey(aObject->mImage, aObject->mBackendType));
// Deleting the entry will delete aObject since the entry owns aObject.
mCache.RemoveEntry(ImageCacheKey(aObject->mImage, aObject->mCanvas,
mCache.RemoveEntry(ImageCacheKey(aObject->mImage, aObject->mContext,
aObject->mBackendType));
}
@ -264,11 +263,33 @@ static already_AddRefed<imgIContainer> GetImageContainer(dom::Element* aImage) {
return imgContainer.forget();
}
void CanvasImageCache::NotifyCanvasDestroyed(
CanvasRenderingContext2D* aContext) {
MOZ_ASSERT(aContext);
if (!NS_IsMainThread() || !gImageCache) {
return;
}
for (auto i = gImageCache->mCache.Iter(); !i.Done(); i.Next()) {
ImageCacheEntryData* data = i.Get()->mData.get();
if (data->mContext == aContext) {
gImageCache->RemoveObject(data);
gImageCache->mAllCanvasCache.RemoveEntry(
AllCanvasImageCacheKey(data->mImage, data->mBackendType));
i.Remove();
}
}
}
void CanvasImageCache::NotifyDrawImage(
Element* aImage, HTMLCanvasElement* aCanvas, DrawTarget* aTarget,
Element* aImage, CanvasRenderingContext2D* aContext, DrawTarget* aTarget,
SourceSurface* aSource, const IntSize& aSize, const IntSize& aIntrinsicSize,
const Maybe<IntRect>& aCropRect) {
if (!aTarget) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aContext);
if (!aTarget || !aContext) {
return;
}
@ -285,7 +306,7 @@ void CanvasImageCache::NotifyDrawImage(
BackendType backendType = aTarget->GetBackendType();
AllCanvasImageCacheKey allCanvasCacheKey(imgContainer, backendType);
ImageCacheKey canvasCacheKey(imgContainer, aCanvas, backendType);
ImageCacheKey canvasCacheKey(imgContainer, aContext, backendType);
ImageCacheEntry* entry = gImageCache->mCache.PutEntry(canvasCacheKey);
if (entry) {
@ -311,6 +332,8 @@ void CanvasImageCache::NotifyDrawImage(
SourceSurface* CanvasImageCache::LookupAllCanvas(Element* aImage,
DrawTarget* aTarget) {
MOZ_ASSERT(NS_IsMainThread());
if (!gImageCache || !aTarget) {
return nullptr;
}
@ -329,12 +352,13 @@ SourceSurface* CanvasImageCache::LookupAllCanvas(Element* aImage,
return entry->mSourceSurface;
}
SourceSurface* CanvasImageCache::LookupCanvas(Element* aImage,
HTMLCanvasElement* aCanvas,
DrawTarget* aTarget,
IntSize* aSizeOut,
IntSize* aIntrinsicSizeOut,
Maybe<IntRect>* aCropRectOut) {
SourceSurface* CanvasImageCache::LookupCanvas(
Element* aImage, CanvasRenderingContext2D* aContext, DrawTarget* aTarget,
IntSize* aSizeOut, IntSize* aIntrinsicSizeOut,
Maybe<IntRect>* aCropRectOut) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aContext);
if (!gImageCache || !aTarget) {
return nullptr;
}
@ -351,7 +375,7 @@ SourceSurface* CanvasImageCache::LookupCanvas(Element* aImage,
// optimized surface given to a Skia backend would cause a readback. For
// details, see bug 1794442.
ImageCacheEntry* entry = gImageCache->mCache.GetEntry(
ImageCacheKey(imgContainer, aCanvas, aTarget->GetBackendType()));
ImageCacheKey(imgContainer, aContext, aTarget->GetBackendType()));
if (!entry) {
return nullptr;
}

View file

@ -14,7 +14,7 @@
namespace mozilla {
namespace dom {
class Element;
class HTMLCanvasElement;
class CanvasRenderingContext2D;
} // namespace dom
namespace gfx {
class DrawTarget;
@ -30,18 +30,23 @@ class CanvasImageCache {
public:
/**
* Notify that image element aImage was drawn to aCanvas element
* Notify that image element aImage was drawn to aContext canvas
* using the first frame of aRequest's image. The data for the surface is
* in aSurface, and the image size is in aSize. aIntrinsicSize is the size
* the surface is intended to be rendered at.
*/
static void NotifyDrawImage(dom::Element* aImage,
dom::HTMLCanvasElement* aCanvas,
dom::CanvasRenderingContext2D* aContext,
gfx::DrawTarget* aTarget, SourceSurface* aSource,
const gfx::IntSize& aSize,
const gfx::IntSize& aIntrinsicSize,
const Maybe<gfx::IntRect>& aCropRect);
/**
* Notify that aContext is being destroyed.
*/
static void NotifyCanvasDestroyed(dom::CanvasRenderingContext2D* aContext);
/**
* Check whether aImage has recently been drawn any canvas. If we return
* a non-null surface, then the same image was recently drawn into a canvas.
@ -50,11 +55,11 @@ class CanvasImageCache {
gfx::DrawTarget* aTarget);
/**
* Like the top above, but restricts the lookup to only aCanvas. This is
* Like the top above, but restricts the lookup to only aContext. This is
* required for CORS security.
*/
static SourceSurface* LookupCanvas(dom::Element* aImage,
dom::HTMLCanvasElement* aCanvas,
dom::CanvasRenderingContext2D* aContext,
gfx::DrawTarget* aTarget,
gfx::IntSize* aSizeOut,
gfx::IntSize* aIntrinsicSizeOut,

View file

@ -1090,6 +1090,7 @@ CanvasRenderingContext2D::CanvasRenderingContext2D(
}
CanvasRenderingContext2D::~CanvasRenderingContext2D() {
CanvasImageCache::NotifyCanvasDestroyed(this);
RemovePostRefreshObserver();
RemoveShutdownObserver();
ResetBitmap();
@ -5541,9 +5542,8 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
element = video;
}
srcSurf =
CanvasImageCache::LookupCanvas(element, mCanvasElement, mTarget,
&imgSize, &intrinsicImgSize, &cropRect);
srcSurf = CanvasImageCache::LookupCanvas(element, this, mTarget, &imgSize,
&intrinsicImgSize, &cropRect);
}
DirectDrawInfo drawInfo;
@ -5617,9 +5617,8 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
if (srcSurf) {
if (res.mImageRequest) {
CanvasImageCache::NotifyDrawImage(element, mCanvasElement, mTarget,
srcSurf, imgSize, intrinsicImgSize,
cropRect);
CanvasImageCache::NotifyDrawImage(element, this, mTarget, srcSurf,
imgSize, intrinsicImgSize, cropRect);
}
} else {
drawInfo = res.mDrawInfo;

View file

@ -933,7 +933,7 @@ dictionary ParentProcInfoDictionary {
* serialization, deserialization, and inheritance.
* (3) Update the methods on mozilla::OriginAttributesPattern, including matching.
*/
[GenerateInitFromJSON]
[GenerateInitFromJSON, GenerateEqualityOperator]
dictionary OriginAttributesDictionary {
unsigned long userContextId = 0;
unsigned long privateBrowsingId = 0;

View file

@ -1903,6 +1903,90 @@ worker involved is a `ChromeWorker` or not. At the moment the only
possible caller types are `System` (representing system-principal
callers) and `NonSystem`.
### `[GenerateInit]`
When set on a dictionary it will add two `Init` methods to the generated C++
class with the following signatures:
``` cpp
bool Init(BindingCallContext& cx, JS::Handle<JS::Value> val, const char* sourceDescription="Value", bool passedToJSImpl=false);
bool Init(JSContext* cx_, JS::Handle<JS::Value> val, const char* sourceDescription="Value", bool passedToJSImpl=false);
```
These methods will initialize the dictionary from `val` by following WebIDL's
[JavaScript type mapping](https://webidl.spec.whatwg.org/#js-dictionary).
### `[GenerateInitFromJSON]`
When set on a dictionary it will add an `Init` method to the generated C++
class with the following signature:
``` cpp
bool Init(const nsAString& aJSON);
```
This extended attribute will only have an effect if all of the types of the
dictionary's members are representable in JSON (they are a string type, a
primitive type that's not an unrestricted float/double, a void type, or a
sequence, union, dictionary or record containing these types).
The method is expected to be called with a JSON string as input. The JSON string
will be parsed into a JavaScript value, and then the dictionary is initialized
with this value by following WebIDL's
[JavaScript type mapping](https://webidl.spec.whatwg.org/#js-dictionary).
Note: As a side-effect of how this is implemented it will also add the two
`Init` methods that would be added by a [`[GenerateInit]`](#generateinit)
extended attribute.
### `[GenerateToJSON]`
When set on a dictionary it will add a `ToJSON` method to the generated C++
class with the following signature:
``` cpp
bool ToJSON(nsAString& aJSON);
```
The method will generate a JSON representation of the dictionary members' values
in `aJSON` by converting the dictionary to a JavaScript object by following
WebIDL's [JavaScript type mapping](https://webidl.spec.whatwg.org/#js-dictionary)
and then converting that object to a JSON string.
The same restrictions on types applies as on
[`[GenerateInitFromJSON]`](#generateinitfromjson).
Note: As a side-effect of how this is implemented it will also add the
`ToObjectInternal` method that would be added by a
[`[GenerateConversionToJS]`](#generateconversiontojs) extended attribute.
### `[GenerateConversionToJS]`
When set on a dictionary it will add a `ToObjectInternal` method to the
generated C++ class with the following signature:
``` cpp
bool ToObjectInternal(JSContext* cx, JS::MutableHandle<JS::Value> rval);
```
The method will create a JavaScript object by following WebIDL's
[JavaScript type mapping](https://webidl.spec.whatwg.org/#js-dictionary).
### `[GenerateEqualityOperator]`
When set on a dictionary it will add an equality operator to the generated C++
class.
This is only allowed on dictionaries who only have members (own or inherited)
with string, primitive or enum types.
### `[Unsorted]`
When set on a dictionary the dictionary's members will not be sorted in
lexicographic order (which is specified by WebIDL).
This should only ever be used on internal APIs that are not exposed to the Web!
## Helper objects
The C++ side of the bindings uses a number of helper objects.

View file

@ -381,10 +381,6 @@ HTMLFieldSetElement* HTMLElement::GetFieldSetInternal() const {
bool HTMLElement::CanBeDisabled() const { return IsFormAssociatedElement(); }
bool HTMLElement::DoesReadOnlyApply() const {
return IsFormAssociatedElement();
}
void HTMLElement::UpdateDisabledState(bool aNotify) {
bool oldState = IsDisabled();
nsGenericHTMLFormElement::UpdateDisabledState(aNotify);

View file

@ -75,7 +75,6 @@ class HTMLElement final : public nsGenericHTMLFormElement {
void SetFieldSetInternal(HTMLFieldSetElement* aFieldset) override;
HTMLFieldSetElement* GetFieldSetInternal() const override;
bool CanBeDisabled() const override;
bool DoesReadOnlyApply() const override;
void UpdateDisabledState(bool aNotify) override;
void UpdateFormOwner(bool aBindToTree, Element* aFormIdElement) override;

View file

@ -6553,7 +6553,7 @@ HTMLInputElement::ValueModeType HTMLInputElement::GetValueMode() const {
bool HTMLInputElement::IsMutable() const {
return !IsDisabled() &&
!(DoesReadOnlyApply() && State().HasState(ElementState::READONLY));
!(DoesReadWriteApply() && State().HasState(ElementState::READONLY));
}
bool HTMLInputElement::DoesRequiredApply() const {

View file

@ -2233,7 +2233,7 @@ void nsGenericHTMLFormElement::UpdateDisabledState(bool aNotify) {
if (!changedStates.IsEmpty()) {
ToggleStates(changedStates, aNotify);
if (DoesReadOnlyApply()) {
if (DoesReadWriteApply()) {
// :disabled influences :read-only / :read-write.
UpdateReadOnlyState(aNotify);
}
@ -2241,7 +2241,7 @@ void nsGenericHTMLFormElement::UpdateDisabledState(bool aNotify) {
}
bool nsGenericHTMLFormElement::IsReadOnlyInternal() const {
if (DoesReadOnlyApply()) {
if (DoesReadWriteApply()) {
return IsDisabled() || GetBoolAttr(nsGkAtoms::readonly);
}
return nsGenericHTMLElement::IsReadOnlyInternal();
@ -2682,7 +2682,7 @@ bool nsGenericHTMLFormControlElement::CanBeDisabled() const {
return type != FormControlType::Object && type != FormControlType::Output;
}
bool nsGenericHTMLFormControlElement::DoesReadOnlyApply() const {
bool nsGenericHTMLFormControlElement::DoesReadWriteApply() const {
auto type = ControlType();
if (!IsInputElement(type) && type != FormControlType::Textarea) {
return false;
@ -2716,7 +2716,7 @@ bool nsGenericHTMLFormControlElement::DoesReadOnlyApply() const {
case FormControlType::InputDatetimeLocal:
return true;
default:
MOZ_ASSERT_UNREACHABLE("Unexpected input type in DoesReadOnlyApply()");
MOZ_ASSERT_UNREACHABLE("Unexpected input type in DoesReadWriteApply()");
return true;
#else // DEBUG
default:

View file

@ -1140,9 +1140,10 @@ class nsGenericHTMLFormElement : public nsGenericHTMLElement {
virtual bool CanBeDisabled() const { return false; }
/**
* Returns if the readonly attribute applies.
* Returns true if :read-write pseudo class may match the element even if the
* element isn't part of designMode or contenteditable.
*/
virtual bool DoesReadOnlyApply() const { return false; }
virtual bool DoesReadWriteApply() const { return false; }
/**
* Returns true if the element is a form associated element.
@ -1200,7 +1201,7 @@ class nsGenericHTMLFormControlElement : public nsGenericHTMLFormElement,
// nsGenericHTMLFormElement
bool CanBeDisabled() const override;
bool DoesReadOnlyApply() const override;
bool DoesReadWriteApply() const override;
void SetFormInternal(mozilla::dom::HTMLFormElement* aForm,
bool aBindToTree) override;
mozilla::dom::HTMLFormElement* GetFormInternal() const override;

View file

@ -2663,6 +2663,7 @@ mozilla::ipc::IPCResult ContentChild::RecvRemoteType(
// Turn off Spectre mitigations in isolated web content processes.
if (StaticPrefs::javascript_options_spectre_disable_for_isolated_content() &&
StaticPrefs::browser_opaqueResponseBlocking() &&
(remoteTypePrefix == FISSION_WEB_REMOTE_TYPE ||
remoteTypePrefix == SERVICEWORKER_REMOTE_TYPE ||
remoteTypePrefix == WITH_COOP_COEP_REMOTE_TYPE ||

View file

@ -927,9 +927,10 @@ void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
// For key system requires clearlead, every codec needs to have clear support.
// If not, then we will remove the codec from supported codec.
if (aFlags.contains(CapabilitesFlag::NeedClearLeadCheck)) {
for (const auto& scheme : aCapabilitiesOut.encryptionSchemes()) {
nsTArray<KeySystemConfig::EMECodecString> noClearLeadCodecs;
for (const auto& codec : supportedVideoCodecs) {
nsTArray<KeySystemConfig::EMECodecString> noClearLeadCodecs;
for (const auto& codec : supportedVideoCodecs) {
bool foundSupportedScheme = false;
for (const auto& scheme : aCapabilitiesOut.encryptionSchemes()) {
nsAutoString additionalFeature(u"encryption-type=");
// If we don't specify 'encryption-iv-size', it would use 8 bytes IV as
// default [1]. If it's not supported, then we will try 16 bytes later.
@ -951,7 +952,8 @@ void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
CryptoSchemeToString(scheme), codec.get(),
rv ? "supported" : "not supported");
if (rv) {
continue;
foundSupportedScheme = true;
break;
}
// Try 16 bytes IV.
additionalFeature.AppendLiteral(u"encryption-iv-size=16,");
@ -961,20 +963,25 @@ void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
MFCDM_PARENT_SLOG("clearlead %s IV 16 bytes %s %s",
CryptoSchemeToString(scheme), codec.get(),
rv ? "supported" : "not supported");
// Failed on both, so remove the codec from supported codec.
if (!rv) {
noClearLeadCodecs.AppendElement(codec);
if (rv) {
foundSupportedScheme = true;
break;
}
}
for (const auto& codec : noClearLeadCodecs) {
MFCDM_PARENT_SLOG("%s: -video:%s", __func__, codec.get());
aCapabilitiesOut.videoCapabilities().RemoveElementsBy(
[&codec](const MFCDMMediaCapability& aCapbilities) {
return aCapbilities.contentType() == NS_ConvertUTF8toUTF16(codec);
});
supportedVideoCodecs.RemoveElement(codec);
// Failed on all schemes, add the codec to the list and remove it later.
if (!foundSupportedScheme) {
noClearLeadCodecs.AppendElement(codec);
}
}
for (const auto& codec : noClearLeadCodecs) {
MFCDM_PARENT_SLOG("%s: -video:%s", __func__, codec.get());
aCapabilitiesOut.videoCapabilities().RemoveElementsBy(
[&codec](const MFCDMMediaCapability& aCapbilities) {
return aCapbilities.contentType() == NS_ConvertUTF8toUTF16(codec);
});
supportedVideoCodecs.RemoveElement(codec);
}
}
// Only perform HDCP if necessary, "The hdcp query (item 4) has a
@ -1041,8 +1048,10 @@ mozilla::ipc::IPCResult MFCDMParent::RecvInit(
RequirementToStr(aParams.distinctiveID()),
RequirementToStr(aParams.persistentState()),
IsKeySystemHWSecure(mKeySystem, aParams.videoCapabilities()));
MOZ_ASSERT(IsTypeSupported(mFactory, mKeySystem));
MFCDM_REJECT_IF(!mFactory, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
MOZ_ASSERT(IsTypeSupported(mFactory, mKeySystem));
MFCDM_REJECT_IF_FAILED(CreateContentDecryptionModule(
mFactory, MapKeySystem(mKeySystem), aParams, mCDM),
NS_ERROR_FAILURE);

View file

@ -267,6 +267,14 @@ g.test('wrappers_do_not_share_labels')
module,
entryPoint: 'main',
},
// Specify a color attachment so we have at least one render target. Otherwise, details here
// are not relevant to this test.
fragment: {
targets: [{ format: 'rgba8unorm' }],
module: t.device.createShaderModule({
code: `@fragment fn main() -> @location(0) vec4f { return vec4f(0); }`,
}),
},
});
const layout1 = pipeline.getBindGroupLayout(0);
const layout2 = pipeline.getBindGroupLayout(0);

View file

@ -1 +1 @@
5c8510ec0d47180d1cd4dd92790b5a69335e162c
0ba03b439ce5e22b055893bb47477e666ac8e42f

View file

@ -480,7 +480,7 @@ _cairo_recording_surface_region_array_destroy (cairo_recording_surface_t *
cairo_recording_region_element_t *region_elements;
int i, num_elements;
num_elements = MAX(surface->commands.num_elements, _cairo_array_num_elements(&region_array->regions));
num_elements = MIN(surface->commands.num_elements, _cairo_array_num_elements(&region_array->regions));
elements = _cairo_array_index (&surface->commands, 0);
region_elements = _cairo_array_index (&region_array->regions, 0);
for (i = 0; i < num_elements; i++) {

View file

@ -192,20 +192,6 @@ ImageContainer::ImageContainer(Mode flag)
}
}
ImageContainer::ImageContainer(const CompositableHandle& aHandle)
: mRecursiveMutex("ImageContainer.mRecursiveMutex"),
mGenerationCounter(++sGenerationCounter),
mPaintCount(0),
mDroppedImageCount(0),
mImageFactory(nullptr),
mRotation(VideoRotation::kDegree_0),
mRecycleBin(nullptr),
mIsAsync(true),
mAsyncContainerHandle(aHandle),
mCurrentProducerID(-1) {
MOZ_ASSERT(mAsyncContainerHandle);
}
ImageContainer::~ImageContainer() {
if (mNotifyCompositeListener) {
mNotifyCompositeListener->ClearImageContainer();

View file

@ -326,13 +326,6 @@ class ImageContainer final : public SupportsThreadSafeWeakPtr<ImageContainer> {
explicit ImageContainer(ImageContainer::Mode flag = SYNCHRONOUS);
/**
* Create ImageContainer just to hold another ASYNCHRONOUS ImageContainer's
* async container ID.
* @param aAsyncContainerID async container ID for which we are a proxy
*/
explicit ImageContainer(const CompositableHandle& aHandle);
~ImageContainer();
typedef ContainerFrameID FrameID;

View file

@ -17,7 +17,7 @@ default = []
[dependencies.wgc]
package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu"
rev = "2f4522714c4037a1842d27bb448b634f089664ab"
rev = "18b758e3889bdd6ffa769085de15e2b96a0c1eb5"
# TODO: remove the replay feature on the next update containing https://github.com/gfx-rs/wgpu/pull/5182
features = ["serde", "replay", "trace", "strict_asserts", "wgsl", "api_log_info"]
@ -26,37 +26,37 @@ features = ["serde", "replay", "trace", "strict_asserts", "wgsl", "api_log_info"
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies.wgc]
package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu"
rev = "2f4522714c4037a1842d27bb448b634f089664ab"
rev = "18b758e3889bdd6ffa769085de15e2b96a0c1eb5"
features = ["metal"]
# We want the wgpu-core Direct3D backends on Windows.
[target.'cfg(windows)'.dependencies.wgc]
package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu"
rev = "2f4522714c4037a1842d27bb448b634f089664ab"
rev = "18b758e3889bdd6ffa769085de15e2b96a0c1eb5"
features = ["dx12"]
# We want the wgpu-core Vulkan backend on Linux and Windows.
[target.'cfg(any(windows, all(unix, not(any(target_os = "macos", target_os = "ios")))))'.dependencies.wgc]
package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu"
rev = "2f4522714c4037a1842d27bb448b634f089664ab"
rev = "18b758e3889bdd6ffa769085de15e2b96a0c1eb5"
features = ["vulkan"]
[dependencies.wgt]
package = "wgpu-types"
git = "https://github.com/gfx-rs/wgpu"
rev = "2f4522714c4037a1842d27bb448b634f089664ab"
rev = "18b758e3889bdd6ffa769085de15e2b96a0c1eb5"
[dependencies.wgh]
package = "wgpu-hal"
git = "https://github.com/gfx-rs/wgpu"
rev = "2f4522714c4037a1842d27bb448b634f089664ab"
rev = "18b758e3889bdd6ffa769085de15e2b96a0c1eb5"
features = ["windows_rs", "oom_panic", "device_lost_panic", "internal_error_panic"]
[target.'cfg(windows)'.dependencies.d3d12]
git = "https://github.com/gfx-rs/wgpu"
rev = "2f4522714c4037a1842d27bb448b634f089664ab"
rev = "18b758e3889bdd6ffa769085de15e2b96a0c1eb5"
[target.'cfg(windows)'.dependencies]
winapi = "0.3"

View file

@ -20,11 +20,11 @@ origin:
# Human-readable identifier for this version/release
# Generally "version NNN", "tag SSS", "bookmark SSS"
release: 2f4522714c4037a1842d27bb448b634f089664ab (2024-05-10T18:18:15Z).
release: 18b758e3889bdd6ffa769085de15e2b96a0c1eb5 (2024-05-17T20:01:43Z).
# Revision to pull in
# Must be a long or short commit SHA (long preferred)
revision: 2f4522714c4037a1842d27bb448b634f089664ab
revision: 18b758e3889bdd6ffa769085de15e2b96a0c1eb5
license: ['MIT', 'Apache-2.0']

View file

@ -1260,6 +1260,7 @@ pub unsafe extern "C" fn wgpu_client_create_compute_pipeline(
label,
layout: desc.layout,
stage: desc.stage.to_wgpu(),
cache: None,
};
let implicit = match desc.layout {
@ -1314,6 +1315,7 @@ pub unsafe extern "C" fn wgpu_client_create_render_pipeline(
depth_stencil: desc.depth_stencil.cloned(),
multisample: desc.multisample.clone(),
multiview: None,
cache: None,
};
let implicit = match desc.layout {

View file

@ -2,14 +2,15 @@
* 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::{id, RawString};
use crate::{id, server::Global, RawString};
use std::{borrow::Cow, ffi, slice};
use wgc::{
command::{
compute_commands as compute_ffi, render_commands as render_ffi, ComputePassDescriptor,
ComputePassTimestampWrites, RenderPassColorAttachment, RenderPassDepthStencilAttachment,
RenderPassDescriptor, RenderPassTimestampWrites,
render_commands as render_ffi, ComputePassDescriptor, ComputePassTimestampWrites,
RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor,
RenderPassTimestampWrites,
},
hal_api::HalApi,
id::CommandEncoderId,
};
use wgt::{BufferAddress, BufferSize, Color, DynamicOffset, IndexFormat};
@ -954,18 +955,18 @@ pub fn replay_render_pass(
dst_pass
}
pub fn replay_compute_pass(
pub fn replay_compute_pass<A: HalApi>(
global: &Global,
id: CommandEncoderId,
src_pass: &RecordedComputePass,
) -> wgc::command::ComputePass {
let mut dst_pass = wgc::command::ComputePass::new(
) -> Result<(), wgc::command::ComputePassError> {
let mut dst_pass = global.command_encoder_create_compute_pass::<A>(
id,
&wgc::command::ComputePassDescriptor {
label: src_pass.base.label.as_ref().map(|s| s.as_str().into()),
timestamp_writes: src_pass.timestamp_writes.as_ref(),
},
);
let mut dynamic_offsets = src_pass.base.dynamic_offsets.as_slice();
let mut dynamic_offsets = |len| {
let offsets;
@ -986,64 +987,55 @@ pub fn replay_compute_pass(
bind_group_id,
} => {
let offsets = dynamic_offsets(num_dynamic_offsets);
compute_ffi::wgpu_compute_pass_set_bind_group(
&mut dst_pass,
index,
bind_group_id,
offsets,
);
global.compute_pass_set_bind_group(&mut dst_pass, index, bind_group_id, offsets)?;
}
ComputeCommand::SetPipeline(pipeline_id) => {
compute_ffi::wgpu_compute_pass_set_pipeline(&mut dst_pass, pipeline_id)
global.compute_pass_set_pipeline(&mut dst_pass, pipeline_id)?;
}
ComputeCommand::Dispatch([x, y, z]) => {
compute_ffi::wgpu_compute_pass_dispatch_workgroups(&mut dst_pass, x, y, z);
global.compute_pass_dispatch_workgroups(&mut dst_pass, x, y, z);
}
ComputeCommand::DispatchIndirect { buffer_id, offset } => {
compute_ffi::wgpu_compute_pass_dispatch_workgroups_indirect(
global.compute_pass_dispatch_workgroups_indirect(
&mut dst_pass,
buffer_id,
offset,
);
)?;
}
ComputeCommand::PushDebugGroup { color, len } => {
let label = strings(len);
let label = std::str::from_utf8(label).unwrap();
compute_ffi::wgpu_compute_pass_push_debug_group(&mut dst_pass, label, color);
global.compute_pass_push_debug_group(&mut dst_pass, label, color);
}
ComputeCommand::PopDebugGroup => {
compute_ffi::wgpu_compute_pass_pop_debug_group(&mut dst_pass);
global.compute_pass_pop_debug_group(&mut dst_pass);
}
ComputeCommand::InsertDebugMarker { color, len } => {
let label = strings(len);
let label = std::str::from_utf8(label).unwrap();
compute_ffi::wgpu_compute_pass_insert_debug_marker(&mut dst_pass, label, color);
global.compute_pass_insert_debug_marker(&mut dst_pass, label, color);
}
ComputeCommand::WriteTimestamp {
query_set_id,
query_index,
} => {
compute_ffi::wgpu_compute_pass_write_timestamp(
&mut dst_pass,
query_set_id,
query_index,
);
global.compute_pass_write_timestamp(&mut dst_pass, query_set_id, query_index)?;
}
ComputeCommand::BeginPipelineStatisticsQuery {
query_set_id,
query_index,
} => {
compute_ffi::wgpu_compute_pass_begin_pipeline_statistics_query(
global.compute_pass_begin_pipeline_statistics_query(
&mut dst_pass,
query_set_id,
query_index,
);
)?;
}
ComputeCommand::EndPipelineStatisticsQuery => {
compute_ffi::wgpu_compute_pass_end_pipeline_statistics_query(&mut dst_pass);
global.compute_pass_end_pipeline_statistics_query(&mut dst_pass);
}
}
}
dst_pass
global.command_encoder_run_compute_pass(&dst_pass)
}

View file

@ -520,7 +520,6 @@ mod foreign {
| TransferError::CopyFromForbiddenTextureFormat { .. }
| TransferError::CopyToForbiddenTextureFormat { .. }
| TransferError::ExternalCopyToForbiddenTextureFormat(_)
| TransferError::InvalidDepthTextureExtent
| TransferError::TextureFormatsNotCopyCompatible { .. }
| TransferError::MissingDownlevelFlags(_)
| TransferError::InvalidSampleCount { .. }

View file

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::{
command::RecordedComputePass,
error::{ErrMsg, ErrorBuffer, ErrorBufferType},
wgpu_string, AdapterInformation, ByteBuf, CommandEncoderAction, DeviceAction, DropAction,
QueueWriteAction, SwapChainId, TextureAction,
@ -1093,12 +1094,35 @@ pub unsafe extern "C" fn wgpu_server_compute_pass(
global: &Global,
encoder_id: id::CommandEncoderId,
byte_buf: &ByteBuf,
error_buf: ErrorBuffer,
mut error_buf: ErrorBuffer,
) {
let pass = bincode::deserialize(byte_buf.as_slice()).unwrap();
let action = crate::command::replay_compute_pass(encoder_id, &pass).into_command();
let src_pass = bincode::deserialize(byte_buf.as_slice()).unwrap();
gfx_select!(encoder_id => global.command_encoder_action(encoder_id, action, error_buf));
trait ReplayComputePass {
fn replay_compute_pass<A>(
&self,
encoder_id: id::CommandEncoderId,
src_pass: &RecordedComputePass,
) -> Result<(), wgc::command::ComputePassError>
where
A: wgc::hal_api::HalApi;
}
impl ReplayComputePass for Global {
fn replay_compute_pass<A>(
&self,
encoder_id: id::CommandEncoderId,
src_pass: &RecordedComputePass,
) -> Result<(), wgc::command::ComputePassError>
where
A: wgc::hal_api::HalApi,
{
crate::command::replay_compute_pass::<A>(self, encoder_id, src_pass)
}
}
if let Err(err) = gfx_select!(encoder_id => global.replay_compute_pass(encoder_id, &src_pass)) {
error_buf.init(err);
}
}
#[no_mangle]

View file

@ -1185,6 +1185,106 @@ impl ClipNode {
}
}
}
/// Find the mask regions of a given clip node. For each mask region, map it in to the space defined
/// by `prim_spatial_node_index`, and invoke a closure with the local rect of that region. Returns
/// false for transformed clips (we can handle this case better in future).
pub fn get_local_mask_rects<F>(
&self,
prim_spatial_node_index: SpatialNodeIndex,
spatial_tree: &SpatialTree,
mut f: F,
) -> bool where F: FnMut(LayoutRect) {
let conversion = ClipSpaceConversion::new(
prim_spatial_node_index,
self.item.spatial_node_index,
spatial_tree,
);
match self.item.kind {
ClipItemKind::Rectangle { mode, .. } => {
match conversion {
ClipSpaceConversion::Local | ClipSpaceConversion::ScaleOffset(_) => {
// These clips will be handled by the vertex shader, so no mask is needed
// Ensure we don't see any ClipOut though - they should only arrive in
// this code path when we start passing box-shadows through here.
assert!(mode != ClipMode::ClipOut);
}
ClipSpaceConversion::Transform(..) => {
// For now, we don't handle mask regions for complex transforms. Previously, we didn't
// handle mask regions at all, so this is no worse than existing state. In future, we
// should conservatively map these cases if we come across any content which shows
// up as slow in this case.
return false;
}
}
}
ClipItemKind::RoundedRectangle { mode: ClipMode::Clip, rect, radius } => {
// Construct the mask regions for each corner
let mut top_left = LayoutRect::from_origin_and_size(
LayoutPoint::new(rect.min.x, rect.min.y),
LayoutSize::new(radius.top_left.width, radius.top_left.height),
);
let mut top_right = LayoutRect::from_origin_and_size(
LayoutPoint::new(
rect.max.x - radius.top_right.width,
rect.min.y,
),
LayoutSize::new(radius.top_right.width, radius.top_right.height),
);
let mut bottom_left = LayoutRect::from_origin_and_size(
LayoutPoint::new(
rect.min.x,
rect.max.y - radius.bottom_left.height,
),
LayoutSize::new(radius.bottom_left.width, radius.bottom_left.height),
);
let mut bottom_right = LayoutRect::from_origin_and_size(
LayoutPoint::new(
rect.max.x - radius.bottom_right.width,
rect.max.y - radius.bottom_right.height,
),
LayoutSize::new(radius.bottom_right.width, radius.bottom_right.height),
);
match conversion {
ClipSpaceConversion::Local => {
// No mapping necessary, same local space
}
ClipSpaceConversion::ScaleOffset(scale_offset) => {
top_left = scale_offset.map_rect(&top_left);
top_right = scale_offset.map_rect(&top_right);
bottom_left = scale_offset.map_rect(&bottom_left);
bottom_right = scale_offset.map_rect(&bottom_right);
}
ClipSpaceConversion::Transform(..) => {
// For now, we don't handle mask regions for complex transforms. Previously, we didn't
// handle mask regions at all, so this is no worse than existing state. In future, we
// should conservatively map these cases if we come across any content which shows
// up as slow in this case.
return false;
}
}
f(top_left);
f(top_right);
f(bottom_left);
f(bottom_right);
}
ClipItemKind::RoundedRectangle { mode: ClipMode::ClipOut, .. } => {
panic!("bug: old box-shadow clips unexpected in this path");
}
ClipItemKind::BoxShadow { .. } => {
panic!("bug: old box-shadow clips unexpected in this path");
}
ClipItemKind::Image { .. } => {
panic!("bug: image clips unexpected in this path");
}
}
true
}
}
#[derive(Default)]

View file

@ -12,6 +12,7 @@ use euclid::{SideOffsets2D, Size2D};
use malloc_size_of::MallocSizeOf;
use crate::composite::CompositorSurfaceKind;
use crate::clip::ClipLeafId;
use crate::quad::QuadTileClassifier;
use crate::segment::EdgeAaSegmentMask;
use crate::border::BorderSegmentCacheKey;
use crate::debug_item::{DebugItem, DebugMessage};
@ -1219,6 +1220,10 @@ pub struct PrimitiveScratchBuffer {
/// Temporary buffers for building segments in to during prepare pass
pub quad_direct_segments: Vec<QuadSegment>,
pub quad_indirect_segments: Vec<QuadSegment>,
/// A retained classifier for checking which segments of a tiled primitive
/// need a mask / are clipped / can be rendered directly
pub quad_tile_classifier: QuadTileClassifier,
}
impl Default for PrimitiveScratchBuffer {
@ -1235,6 +1240,7 @@ impl Default for PrimitiveScratchBuffer {
required_sub_graphs: FastHashSet::default(),
quad_direct_segments: Vec::new(),
quad_indirect_segments: Vec::new(),
quad_tile_classifier: QuadTileClassifier::new(),
}
}
}

View file

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{units::*, PremultipliedColorF, ClipMode};
use api::{units::*, ClipMode, PremultipliedColorF};
use euclid::point2;
use crate::batch::{BatchKey, BatchKind, BatchTextures};
@ -24,6 +24,7 @@ use crate::util::{MaxRect, ScaleOffset};
const MIN_AA_SEGMENTS_SIZE: f32 = 4.0;
const MIN_QUAD_SPLIT_SIZE: f32 = 256.0;
const MAX_TILES_PER_QUAD: usize = 4;
/// Describes how clipping affects the rendering of a quad primitive.
///
@ -152,7 +153,7 @@ pub fn push_quad(
return;
}
let surface = &frame_state.surfaces[pic_context.surface_index.0];
let surface = &mut frame_state.surfaces[pic_context.surface_index.0];
let Some(clipped_surface_rect) = surface.get_surface_rect(
&clip_chain.pic_coverage_rect, frame_context.spatial_tree
) else {
@ -194,23 +195,63 @@ pub fn push_quad(
let clip_coverage_rect = surface
.map_to_device_rect(&clip_chain.pic_coverage_rect, frame_context.spatial_tree);
surface.map_local_to_surface.set_target_spatial_node(
prim_spatial_node_index,
frame_context.spatial_tree,
);
let Some(pic_rect) = surface.map_local_to_surface.map(local_rect) else { return };
let unclipped_surface_rect = surface.map_to_device_rect(
&pic_rect, frame_context.spatial_tree
).round_out().to_i32();
// Set up the tile classifier for the params of this quad
scratch.quad_tile_classifier.reset(
x_tiles as usize,
y_tiles as usize,
*local_rect,
);
// Walk each clip, extract the local mask regions and add them to the tile classifier.
for i in 0 .. clip_chain.clips_range.count {
let clip_instance = frame_state.clip_store.get_instance_from_range(&clip_chain.clips_range, i);
let clip_node = &interned_clips[clip_instance.handle];
if !clip_node.get_local_mask_rects(
prim_spatial_node_index,
frame_context.spatial_tree,
|mask_region| {
scratch.quad_tile_classifier.add_mask_region(mask_region)
}
) {
// If we couldn't extract a mask region, just assume the entire primitive
// local rect is affected by the clip, for now.
scratch.quad_tile_classifier.add_mask_region(*local_rect);
}
}
// Classify each tile within the quad to be Pattern / Mask / Clipped
let tile_info = scratch.quad_tile_classifier.classify();
scratch.quad_direct_segments.clear();
scratch.quad_indirect_segments.clear();
let mut x_coords = vec![clipped_surface_rect.min.x];
let mut y_coords = vec![clipped_surface_rect.min.y];
let dx = (clipped_surface_rect.max.x - clipped_surface_rect.min.x) as f32 / x_tiles as f32;
let dy = (clipped_surface_rect.max.y - clipped_surface_rect.min.y) as f32 / y_tiles as f32;
let mut x_coords = vec![unclipped_surface_rect.min.x];
let mut y_coords = vec![unclipped_surface_rect.min.y];
let dx = (unclipped_surface_rect.max.x - unclipped_surface_rect.min.x) as f32 / x_tiles as f32;
let dy = (unclipped_surface_rect.max.y - unclipped_surface_rect.min.y) as f32 / y_tiles as f32;
for x in 1 .. (x_tiles as i32) {
x_coords.push((clipped_surface_rect.min.x as f32 + x as f32 * dx).round() as i32);
x_coords.push((unclipped_surface_rect.min.x as f32 + x as f32 * dx).round() as i32);
}
for y in 1 .. (y_tiles as i32) {
y_coords.push((clipped_surface_rect.min.y as f32 + y as f32 * dy).round() as i32);
y_coords.push((unclipped_surface_rect.min.y as f32 + y as f32 * dy).round() as i32);
}
x_coords.push(clipped_surface_rect.max.x);
y_coords.push(clipped_surface_rect.max.y);
x_coords.push(unclipped_surface_rect.max.x);
y_coords.push(unclipped_surface_rect.max.y);
for y in 0 .. y_coords.len()-1 {
let y0 = y_coords[y];
@ -228,43 +269,88 @@ pub fn push_quad(
continue;
}
// Check whether this tile requires a mask
let tile_info = &tile_info[y * x_tiles as usize + x];
let is_direct = match tile_info.kind {
QuadTileKind::Clipped => {
// This tile was entirely clipped, so we can skip drawing it
continue;
}
QuadTileKind::Pattern => {
prim_is_2d_scale_translation
}
QuadTileKind::PatternWithMask => {
false
}
};
let int_rect = DeviceIntRect {
min: point2(x0, y0),
max: point2(x1, y1),
};
let int_rect = match clipped_surface_rect.intersection(&int_rect) {
Some(rect) => rect,
None => continue,
};
let rect = int_rect.to_f32();
let task_id = add_render_task_with_mask(
pattern,
int_rect.size(),
rect.min,
clip_chain,
prim_spatial_node_index,
pic_context.raster_spatial_node_index,
main_prim_address,
transform_id,
aa_flags,
quad_flags,
device_pixel_scale,
needs_scissor,
frame_state,
);
if is_direct {
scratch.quad_direct_segments.push(QuadSegment { rect: rect.cast_unit(), task_id: RenderTaskId::INVALID });
} else {
let task_id = add_render_task_with_mask(
pattern,
int_rect.size(),
rect.min,
clip_chain,
prim_spatial_node_index,
pic_context.raster_spatial_node_index,
main_prim_address,
transform_id,
aa_flags,
quad_flags,
device_pixel_scale,
needs_scissor,
frame_state,
);
scratch.quad_indirect_segments.push(QuadSegment { rect: rect.cast_unit(), task_id });
scratch.quad_indirect_segments.push(QuadSegment { rect: rect.cast_unit(), task_id });
}
}
}
let is_masked = true;
add_composite_prim(
pattern,
is_masked,
prim_instance_index,
clip_coverage_rect.cast_unit(),
frame_state,
targets,
&scratch.quad_indirect_segments,
);
if !scratch.quad_direct_segments.is_empty() {
let local_to_device = map_prim_to_surface.as_2d_scale_offset()
.expect("bug: nine-patch segments should be axis-aligned only")
.scale(device_pixel_scale.0);
let device_prim_rect: DeviceRect = local_to_device.map_rect(&local_rect);
add_pattern_prim(
pattern,
local_to_device.inverse(),
prim_instance_index,
device_prim_rect.cast_unit(),
clip_coverage_rect.cast_unit(),
pattern.is_opaque,
frame_state,
targets,
&scratch.quad_direct_segments,
);
}
if !scratch.quad_indirect_segments.is_empty() {
add_composite_prim(
pattern,
true, // is_masked
prim_instance_index,
clip_coverage_rect.cast_unit(),
frame_state,
targets,
&scratch.quad_indirect_segments,
);
}
}
QuadRenderStrategy::NinePatch { clip_rect, radius } => {
let clip_coverage_rect = surface
@ -274,7 +360,7 @@ pub fn push_quad(
.expect("bug: nine-patch segments should be axis-aligned only")
.scale(device_pixel_scale.0);
let device_prim_rect: DeviceRect = local_to_device.map_rect(&local_rect);
let device_prim_rect: DeviceRect = local_to_device.map_rect(&local_rect);
let local_corner_0 = LayoutRect::new(
clip_rect.min,
@ -427,7 +513,7 @@ fn get_prim_render_strategy(
}
fn tile_count_for_size(size: f32) -> u16 {
(size / MIN_QUAD_SPLIT_SIZE).min(4.0).max(1.0).ceil() as u16
(size / MIN_QUAD_SPLIT_SIZE).min(MAX_TILES_PER_QUAD as f32).max(1.0).ceil() as u16
}
let prim_coverage_size = clip_chain.pic_coverage_rect.size();
@ -780,3 +866,388 @@ pub fn add_to_batch<F>(
}
}
/// Classification result for a tile within a quad
#[allow(dead_code)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum QuadTileKind {
// TODO(gw): We don't construct this just yet, will be enabled in a follow up patch
// Clipped out - can be skipped
Clipped,
// Requires the pattern only, can draw directly
Pattern,
// Requires a mask, must be drawn indirectly
PatternWithMask,
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[derive(Copy, Clone, Debug)]
pub struct QuadTileInfo {
rect: LayoutRect,
kind: QuadTileKind,
}
impl Default for QuadTileInfo {
fn default() -> Self {
QuadTileInfo {
rect: LayoutRect::zero(),
kind: QuadTileKind::Pattern,
}
}
}
/// A helper struct for classifying a set of tiles within a quad depending on
/// what strategy they can be used to draw them.
#[cfg_attr(feature = "capture", derive(Serialize))]
pub struct QuadTileClassifier {
buffer: [QuadTileInfo; MAX_TILES_PER_QUAD * MAX_TILES_PER_QUAD],
mask_regions: Vec<LayoutRect>,
clip_in_regions: Vec<LayoutRect>,
clip_out_regions: Vec<LayoutRect>,
rect: LayoutRect,
x_tiles: usize,
y_tiles: usize,
}
impl QuadTileClassifier {
pub fn new() -> Self {
QuadTileClassifier {
buffer: [QuadTileInfo::default(); MAX_TILES_PER_QUAD * MAX_TILES_PER_QUAD],
mask_regions: Vec::new(),
clip_in_regions: Vec::new(),
clip_out_regions: Vec::new(),
rect: LayoutRect::zero(),
x_tiles: 0,
y_tiles: 0,
}
}
pub fn reset(
&mut self,
x_tiles: usize,
y_tiles: usize,
rect: LayoutRect,
) {
assert_eq!(self.x_tiles, 0);
assert_eq!(self.y_tiles, 0);
self.x_tiles = x_tiles;
self.y_tiles = y_tiles;
self.rect = rect;
self.mask_regions.clear();
self.clip_in_regions.clear();
self.clip_out_regions.clear();
// TODO(gw): Might be some f32 accuracy issues with how we construct these,
// should be more robust here...
let tw = (rect.max.x - rect.min.x) / x_tiles as f32;
let th = (rect.max.y - rect.min.y) / y_tiles as f32;
for y in 0 .. y_tiles {
for x in 0 .. x_tiles {
let info = &mut self.buffer[y * x_tiles + x];
info.kind = QuadTileKind::Pattern;
let p0 = LayoutPoint::new(
rect.min.x + x as f32 * tw,
rect.min.y + y as f32 * th,
);
let p1 = LayoutPoint::new(
p0.x + tw,
p0.y + th,
);
info.rect = LayoutRect::new(p0, p1);
}
}
}
/// Add an area that needs a clip mask / indirect area
pub fn add_mask_region(
&mut self,
mask_region: LayoutRect,
) {
self.mask_regions.push(mask_region);
}
// TODO(gw): Make use of this to skip tiles that are completely clipped out in a follow up!
pub fn add_clip_rect(
&mut self,
clip_rect: LayoutRect,
clip_mode: ClipMode,
) {
match clip_mode {
ClipMode::Clip => {
self.clip_in_regions.push(clip_rect);
}
ClipMode::ClipOut => {
self.clip_out_regions.push(clip_rect);
}
}
}
/// Classify all the tiles in to categories, based on the provided masks and clip regions
pub fn classify(&mut self) -> &[QuadTileInfo] {
assert_ne!(self.x_tiles, 0);
assert_ne!(self.y_tiles, 0);
let tile_count = self.x_tiles * self.y_tiles;
let tiles = &mut self.buffer[0 .. tile_count];
for info in tiles.iter_mut() {
// If a clip region contains the entire tile, it's clipped
for clip_region in &self.clip_in_regions {
match info.kind {
QuadTileKind::Clipped => {},
QuadTileKind::Pattern | QuadTileKind::PatternWithMask => {
if clip_region.contains_box(&info.rect) {
info.kind = QuadTileKind::Clipped;
}
}
}
}
// If a tile doesn't intersect with a clip-out region, it's clipped
for clip_region in &self.clip_out_regions {
match info.kind {
QuadTileKind::Clipped => {},
QuadTileKind::Pattern | QuadTileKind::PatternWithMask => {
if !clip_region.intersects(&info.rect) {
info.kind = QuadTileKind::Clipped;
}
}
}
}
// If a tile intersects with a mask region, and isn't clipped, it needs a mask
for mask_region in &self.mask_regions {
match info.kind {
QuadTileKind::Clipped | QuadTileKind::PatternWithMask => {},
QuadTileKind::Pattern => {
if mask_region.intersects(&info.rect) {
info.kind = QuadTileKind::PatternWithMask;
}
}
}
}
}
self.x_tiles = 0;
self.y_tiles = 0;
tiles
}
}
#[cfg(test)]
fn qc_new(xc: usize, yc: usize, x0: f32, y0: f32, w: f32, h: f32) -> QuadTileClassifier {
let mut qc = QuadTileClassifier::new();
qc.reset(
xc,
yc,
LayoutRect::new(LayoutPoint::new(x0, y0), LayoutPoint::new(x0 + w, y0 + h),
));
qc
}
#[cfg(test)]
fn qc_verify(mut qc: QuadTileClassifier, expected: &[QuadTileKind]) {
let tiles = qc.classify();
assert_eq!(tiles.len(), expected.len());
for (tile, ex) in tiles.iter().zip(expected.iter()) {
assert_eq!(tile.kind, *ex, "Failed for tile {:?}", tile.rect.to_rect());
}
}
#[cfg(test)]
use QuadTileKind::{Pattern as p, Clipped as c, PatternWithMask as m};
#[test]
fn quad_classify_1() {
let qc = qc_new(3, 3, 0.0, 0.0, 100.0, 100.0);
qc_verify(qc, &[
p, p, p,
p, p, p,
p, p, p,
]);
}
#[test]
fn quad_classify_2() {
let mut qc = qc_new(3, 3, 0.0, 0.0, 100.0, 100.0);
let rect = LayoutRect::new(LayoutPoint::new(0.0, 0.0), LayoutPoint::new(100.0, 100.0));
qc.add_clip_rect(rect, ClipMode::Clip);
qc_verify(qc, &[
c, c, c,
c, c, c,
c, c, c,
]);
}
#[test]
fn quad_classify_3() {
let mut qc = qc_new(3, 3, 0.0, 0.0, 100.0, 100.0);
let rect = LayoutRect::new(LayoutPoint::new(40.0, 40.0), LayoutPoint::new(60.0, 60.0));
qc.add_clip_rect(rect, ClipMode::Clip);
qc_verify(qc, &[
p, p, p,
p, p, p,
p, p, p,
]);
}
#[test]
fn quad_classify_4() {
let mut qc = qc_new(3, 3, 0.0, 0.0, 100.0, 100.0);
let rect = LayoutRect::new(LayoutPoint::new(30.0, 30.0), LayoutPoint::new(70.0, 70.0));
qc.add_clip_rect(rect, ClipMode::Clip);
qc_verify(qc, &[
p, p, p,
p, c, p,
p, p, p,
]);
}
#[test]
fn quad_classify_5() {
let mut qc = qc_new(3, 3, 0.0, 0.0, 100.0, 100.0);
let rect = LayoutRect::new(LayoutPoint::new(30.0, 30.0), LayoutPoint::new(70.0, 70.0));
qc.add_clip_rect(rect, ClipMode::ClipOut);
qc_verify(qc, &[
p, p, p,
p, p, p,
p, p, p,
]);
}
#[test]
fn quad_classify_6() {
let mut qc = qc_new(3, 3, 0.0, 0.0, 100.0, 100.0);
let rect = LayoutRect::new(LayoutPoint::new(40.0, 40.0), LayoutPoint::new(60.0, 60.0));
qc.add_clip_rect(rect, ClipMode::ClipOut);
qc_verify(qc, &[
c, c, c,
c, p, c,
c, c, c,
]);
}
#[test]
fn quad_classify_7() {
let mut qc = qc_new(3, 3, 0.0, 0.0, 100.0, 100.0);
let rect = LayoutRect::new(LayoutPoint::new(20.0, 10.0), LayoutPoint::new(90.0, 80.0));
qc.add_mask_region(rect);
qc_verify(qc, &[
m, m, m,
m, m, m,
m, m, m,
]);
}
#[test]
fn quad_classify_8() {
let mut qc = qc_new(3, 3, 0.0, 0.0, 100.0, 100.0);
let rect = LayoutRect::new(LayoutPoint::new(40.0, 40.0), LayoutPoint::new(60.0, 60.0));
qc.add_mask_region(rect);
qc_verify(qc, &[
p, p, p,
p, m, p,
p, p, p,
]);
}
#[test]
fn quad_classify_9() {
let mut qc = qc_new(4, 4, 100.0, 200.0, 100.0, 100.0);
let rect = LayoutRect::new(LayoutPoint::new(90.0, 180.0), LayoutPoint::new(140.0, 240.0));
qc.add_mask_region(rect);
qc_verify(qc, &[
m, m, p, p,
m, m, p, p,
p, p, p, p,
p, p, p, p,
]);
}
#[test]
fn quad_classify_10() {
let mut qc = qc_new(4, 4, 100.0, 200.0, 100.0, 100.0);
let mask_rect = LayoutRect::new(LayoutPoint::new(90.0, 180.0), LayoutPoint::new(140.0, 240.0));
qc.add_mask_region(mask_rect);
let clip_rect = LayoutRect::new(LayoutPoint::new(120.0, 220.0), LayoutPoint::new(160.0, 280.0));
qc.add_clip_rect(clip_rect, ClipMode::Clip);
qc_verify(qc, &[
m, m, p, p,
m, c, p, p,
p, c, p, p,
p, p, p, p,
]);
}
#[test]
fn quad_classify_11() {
let mut qc = qc_new(4, 4, 100.0, 200.0, 100.0, 100.0);
let mask_rect = LayoutRect::new(LayoutPoint::new(90.0, 180.0), LayoutPoint::new(140.0, 240.0));
qc.add_mask_region(mask_rect);
let clip_rect = LayoutRect::new(LayoutPoint::new(120.0, 220.0), LayoutPoint::new(160.0, 280.0));
qc.add_clip_rect(clip_rect, ClipMode::Clip);
let clip_out_rect = LayoutRect::new(LayoutPoint::new(130.0, 200.0), LayoutPoint::new(160.0, 240.0));
qc.add_clip_rect(clip_out_rect, ClipMode::ClipOut);
qc_verify(qc, &[
c, m, p, c,
c, c, p, c,
c, c, c, c,
c, c, c, c,
]);
}
#[test]
fn quad_classify_12() {
let mut qc = qc_new(4, 4, 100.0, 200.0, 100.0, 100.0);
let clip_out_rect = LayoutRect::new(LayoutPoint::new(130.0, 200.0), LayoutPoint::new(160.0, 240.0));
qc.add_clip_rect(clip_out_rect, ClipMode::ClipOut);
let clip_rect = LayoutRect::new(LayoutPoint::new(120.0, 220.0), LayoutPoint::new(160.0, 280.0));
qc.add_clip_rect(clip_rect, ClipMode::Clip);
let mask_rect = LayoutRect::new(LayoutPoint::new(90.0, 180.0), LayoutPoint::new(140.0, 240.0));
qc.add_mask_region(mask_rect);
qc_verify(qc, &[
c, m, p, c,
c, c, p, c,
c, c, c, c,
c, c, c, c,
]);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before After
Before After

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