Update On Wed May 22 20:46:05 CEST 2024
This commit is contained in:
parent
c488624fcc
commit
221b7a721e
881 changed files with 83685 additions and 109693 deletions
247
Cargo.lock
generated
247
Cargo.lock
generated
|
@ -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",
|
||||
]
|
||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -88,6 +88,13 @@
|
|||
|
||||
@end
|
||||
|
||||
@interface mozMeterAccessible : mozRangeAccessible
|
||||
|
||||
// override
|
||||
- (NSString*)moxValueDescription;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Base accessible for an incrementable, a settable range
|
||||
*/
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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."
|
||||
// );
|
||||
}
|
||||
);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);"/>
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -202,6 +202,8 @@ async function reformatExpectedWebCompatInfo(tab, overrides) {
|
|||
}
|
||||
}
|
||||
|
||||
extra_labels.sort();
|
||||
|
||||
return reformatted;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
`;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -119,3 +119,7 @@ function openAndWaitForContextMenu(popup, button, onShown, onHidden) {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
function isActiveElement(el) {
|
||||
return el.getRootNode().activeElement == el;
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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])
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -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([]);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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"
|
14
build/rust/socket2/Cargo.toml
Normal file
14
build/rust/socket2/Cargo.toml
Normal 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"]
|
|
@ -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::*;
|
|
@ -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(
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -73,13 +73,40 @@ High priority tasks are also “drop everything”, except that in this case “
|
|||
Handling needinfos
|
||||
------------------
|
||||
|
||||
TL;DR: Don’t leave people hanging. Reply and convert needinfos from others to self-needinfos.
|
||||
TL;DR: Don’t leave people hanging.
|
||||
|
||||
Many people have long lists of needinfos. Please don’t ignore them. Here’s how we suggest you burn them down, and keep them down:
|
||||
Many people have long lists of needinfos. Please don’t ignore them. Here’s 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. It’s okay to declare needinfo bankruptcy. If you’re 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, don’t leave someone or something, .e.g BugBot hanging. If you can take action in the short term, do so. If you can’t do it straight away, reply letting the requester know that you intend to work on it, but can’t 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. It’s 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 you’re concerned about annoying the requester by clearing the needinfo, feel free to point them at this document.
|
||||
|
||||
|
||||
New needinfos
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
For newer requests, don’t leave someone or something, .e.g BugBot hanging. If you can take action in the short term, do so. If you can’t 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>`_.
|
||||
|
|
|
@ -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
|
||||
------------------
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
10
dom/base/crashtests/1897248.html
Normal file
10
dom/base/crashtests/1897248.html
Normal 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">
|
|
@ -277,3 +277,4 @@ load 1887963_1.html
|
|||
load 1887963_2.html
|
||||
asserts(0-1) load 1887974.html
|
||||
load 1890888.html
|
||||
load 1897248.html
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1 +1 @@
|
|||
5c8510ec0d47180d1cd4dd92790b5a69335e162c
|
||||
0ba03b439ce5e22b055893bb47477e666ac8e42f
|
||||
|
|
|
@ -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(®ion_array->regions));
|
||||
num_elements = MIN(surface->commands.num_elements, _cairo_array_num_elements(®ion_array->regions));
|
||||
elements = _cairo_array_index (&surface->commands, 0);
|
||||
region_elements = _cairo_array_index (®ion_array->regions, 0);
|
||||
for (i = 0; i < num_elements; i++) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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']
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -520,7 +520,6 @@ mod foreign {
|
|||
| TransferError::CopyFromForbiddenTextureFormat { .. }
|
||||
| TransferError::CopyToForbiddenTextureFormat { .. }
|
||||
| TransferError::ExternalCopyToForbiddenTextureFormat(_)
|
||||
| TransferError::InvalidDepthTextureExtent
|
||||
| TransferError::TextureFormatsNotCopyCompatible { .. }
|
||||
| TransferError::MissingDownlevelFlags(_)
|
||||
| TransferError::InvalidSampleCount { .. }
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 |
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue