Update On Fri Apr 18 20:22:15 CEST 2025

This commit is contained in:
github-action[bot] 2025-04-18 20:22:16 +02:00
parent 9c69112ad3
commit a4d062ffa7
3571 changed files with 182119 additions and 66740 deletions

187
Cargo.lock generated
View file

@ -1792,7 +1792,7 @@ dependencies = [
[[package]]
name = "error-support"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
source = "git+https://github.com/mozilla/application-services?rev=6a007c98292fa72965d36389ce32d7609e399217#6a007c98292fa72965d36389ce32d7609e399217"
dependencies = [
"error-support-macros",
"lazy_static",
@ -1804,7 +1804,7 @@ dependencies = [
[[package]]
name = "error-support-macros"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
source = "git+https://github.com/mozilla/application-services?rev=6a007c98292fa72965d36389ce32d7609e399217#6a007c98292fa72965d36389ce32d7609e399217"
dependencies = [
"proc-macro2",
"quote",
@ -1921,7 +1921,7 @@ dependencies = [
[[package]]
name = "firefox-versioning"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
source = "git+https://github.com/mozilla/application-services?rev=6a007c98292fa72965d36389ce32d7609e399217#6a007c98292fa72965d36389ce32d7609e399217"
dependencies = [
"serde_json",
"thiserror 1.999.999",
@ -2501,7 +2501,7 @@ dependencies = [
"qcms",
"rsdparsa_capi",
"rure",
"rusqlite",
"rusqlite 0.33.0",
"rust_minidump_writer_linux",
"signature_cache",
"static_prefs",
@ -2524,7 +2524,6 @@ dependencies = [
name = "gkrust-uniffi-components"
version = "0.1.0"
dependencies = [
"hashbrown 0.15.2",
"relevancy",
"search",
"suggest",
@ -2829,11 +2828,11 @@ dependencies = [
[[package]]
name = "hashlink"
version = "0.9.1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
dependencies = [
"hashbrown 0.14.999",
"hashbrown 0.15.2",
]
[[package]]
@ -3234,11 +3233,11 @@ dependencies = [
[[package]]
name = "interrupt-support"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
source = "git+https://github.com/mozilla/application-services?rev=6a007c98292fa72965d36389ce32d7609e399217#6a007c98292fa72965d36389ce32d7609e399217"
dependencies = [
"lazy_static",
"parking_lot",
"rusqlite",
"rusqlite 0.33.0",
"uniffi",
]
@ -3424,7 +3423,7 @@ dependencies = [
"nserror",
"nsstring",
"rkv",
"rusqlite",
"rusqlite 0.33.0",
"serde",
"serde_json",
"storage_variant",
@ -3440,7 +3439,7 @@ version = "0.1.0"
dependencies = [
"kvstore",
"moz_task",
"rusqlite",
"rusqlite 0.33.0",
"tempfile",
]
@ -3563,9 +3562,9 @@ checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
[[package]]
name = "libsqlite3-sys"
version = "0.28.0"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f"
checksum = "ad8935b44e7c13394a179a438e0cebba0fe08fe01b54f152e29a93b5cf993fd4"
dependencies = [
"cc",
"pkg-config",
@ -4205,7 +4204,7 @@ dependencies = [
"maybe-async",
"mls-rs-core",
"rand",
"rusqlite",
"rusqlite 0.31.999",
"thiserror 1.999.999",
"zeroize",
]
@ -4220,7 +4219,7 @@ dependencies = [
"nserror",
"nss-gk-api",
"nsstring",
"rusqlite",
"rusqlite 0.33.0",
"static_prefs",
"thin-vec",
"xpcom",
@ -4497,8 +4496,8 @@ checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664"
[[package]]
name = "naga"
version = "24.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=a0dbe5ebc6fa24422fb84b2e0fea1cc94dee5109#a0dbe5ebc6fa24422fb84b2e0fea1cc94dee5109"
version = "25.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=f1c496523ff0aa10c162fd01ad606960e925a5a4#f1c496523ff0aa10c162fd01ad606960e925a5a4"
dependencies = [
"arrayvec",
"bit-set",
@ -4511,18 +4510,19 @@ dependencies = [
"indexmap",
"log",
"num-traits",
"once_cell",
"rustc-hash 1.999.999",
"serde",
"spirv",
"strum",
"strum 0.26.999",
"thiserror 2.0.9",
"unicode-ident",
]
[[package]]
name = "neqo-bin"
version = "0.12.2"
source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451"
version = "0.13.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.13.1#0a356afeb45631036df6b3028951a02d16fa8d27"
dependencies = [
"clap",
"clap-verbosity-flag",
@ -4543,35 +4543,38 @@ dependencies = [
[[package]]
name = "neqo-common"
version = "0.12.2"
source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451"
version = "0.13.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.13.1#0a356afeb45631036df6b3028951a02d16fa8d27"
dependencies = [
"enum-map",
"env_logger",
"log",
"qlog",
"strum 0.26.999",
"windows",
]
[[package]]
name = "neqo-crypto"
version = "0.12.2"
source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451"
version = "0.13.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.13.1#0a356afeb45631036df6b3028951a02d16fa8d27"
dependencies = [
"bindgen 0.69.4",
"enum-map",
"log",
"mozbuild",
"neqo-common",
"semver",
"serde",
"serde_derive",
"strum 0.26.999",
"toml",
]
[[package]]
name = "neqo-http3"
version = "0.12.2"
source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451"
version = "0.13.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.13.1#0a356afeb45631036df6b3028951a02d16fa8d27"
dependencies = [
"enumset",
"log",
@ -4581,13 +4584,14 @@ dependencies = [
"neqo-transport",
"qlog",
"sfv",
"strum 0.26.999",
"url",
]
[[package]]
name = "neqo-qpack"
version = "0.12.2"
source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451"
version = "0.13.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.13.1#0a356afeb45631036df6b3028951a02d16fa8d27"
dependencies = [
"log",
"neqo-common",
@ -4598,10 +4602,11 @@ dependencies = [
[[package]]
name = "neqo-transport"
version = "0.12.2"
source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451"
version = "0.13.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.13.1#0a356afeb45631036df6b3028951a02d16fa8d27"
dependencies = [
"enum-map",
"enumset",
"indexmap",
"log",
"mtu",
@ -4610,12 +4615,13 @@ dependencies = [
"qlog",
"smallvec",
"static_assertions",
"strum 0.26.999",
]
[[package]]
name = "neqo-udp"
version = "0.12.2"
source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451"
version = "0.13.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.13.1#0a356afeb45631036df6b3028951a02d16fa8d27"
dependencies = [
"cfg_aliases",
"log",
@ -4874,13 +4880,11 @@ dependencies = [
"android_logger",
"byteorder",
"core-foundation 0.9.999",
"digest",
"env_logger",
"lazy_static",
"libloading",
"log",
"pkcs11-bindings",
"rand",
"rsclientcerts",
"sha2",
"static_prefs",
@ -4940,7 +4944,7 @@ checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
[[package]]
name = "payload-support"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
source = "git+https://github.com/mozilla/application-services?rev=6a007c98292fa72965d36389ce32d7609e399217#6a007c98292fa72965d36389ce32d7609e399217"
dependencies = [
"serde",
"serde_derive",
@ -5283,12 +5287,11 @@ dependencies = [
[[package]]
name = "qlog"
version = "0.13.0"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b5f65b920fa913ce92267bb3c4ed3b9c2f81d05f8e1376c3bbc95455eedb7df"
checksum = "0f15b83c59e6b945f2261c95a1dd9faf239187f32ff0a96af1d1d28c4557f919"
dependencies = [
"serde",
"serde_derive",
"serde_json",
"serde_with",
"smallvec",
@ -5302,9 +5305,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quinn-udp"
version = "0.5.10"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e46f3055866785f6b92bc6164b76be02ca8f2eb4b002c0354b28cf4c119e5944"
checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5"
dependencies = [
"cfg_aliases",
"libc",
@ -5444,7 +5447,7 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
[[package]]
name = "relevancy"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
source = "git+https://github.com/mozilla/application-services?rev=6a007c98292fa72965d36389ce32d7609e399217#6a007c98292fa72965d36389ce32d7609e399217"
dependencies = [
"anyhow",
"base64 0.21.999",
@ -5456,7 +5459,7 @@ dependencies = [
"rand",
"rand_distr",
"remote_settings",
"rusqlite",
"rusqlite 0.33.0",
"serde",
"serde_json",
"serde_path_to_error",
@ -5469,7 +5472,7 @@ dependencies = [
[[package]]
name = "remote_settings"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
source = "git+https://github.com/mozilla/application-services?rev=6a007c98292fa72965d36389ce32d7609e399217#6a007c98292fa72965d36389ce32d7609e399217"
dependencies = [
"anyhow",
"camino",
@ -5479,7 +5482,7 @@ dependencies = [
"log",
"parking_lot",
"regex",
"rusqlite",
"rusqlite 0.33.0",
"serde",
"serde_json",
"sha2",
@ -5600,9 +5603,16 @@ dependencies = [
[[package]]
name = "ron"
version = "0.9.0"
version = "0.9.999"
dependencies = [
"ron 0.10.1",
]
[[package]]
name = "ron"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63f3aa105dea217ef30d89581b65a4d527a19afc95ef5750be3890e8d3c5b837"
checksum = "beceb6f7bf81c73e73aeef6dd1356d9a1b2b4909e1f0fc3e59b034f9572d7b7f"
dependencies = [
"base64 0.22.1",
"bitflags 2.9.0",
@ -5616,8 +5626,11 @@ name = "rsclientcerts"
version = "0.1.0"
dependencies = [
"byteorder",
"digest",
"gecko-profiler",
"pkcs11-bindings",
"rand",
"sha2",
]
[[package]]
@ -5646,9 +5659,16 @@ dependencies = [
[[package]]
name = "rusqlite"
version = "0.31.0"
version = "0.31.999"
dependencies = [
"rusqlite 0.33.0",
]
[[package]]
name = "rusqlite"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae"
checksum = "1c6d5e5acb6f6129fe3f7ba0a7fc77bca1942cb568535e18e7bc40262baf3110"
dependencies = [
"bitflags 2.9.0",
"fallible-iterator",
@ -5800,7 +5820,7 @@ dependencies = [
[[package]]
name = "search"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
source = "git+https://github.com/mozilla/application-services?rev=6a007c98292fa72965d36389ce32d7609e399217#6a007c98292fa72965d36389ce32d7609e399217"
dependencies = [
"error-support",
"firefox-versioning",
@ -6091,13 +6111,13 @@ dependencies = [
[[package]]
name = "sql-support"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
source = "git+https://github.com/mozilla/application-services?rev=6a007c98292fa72965d36389ce32d7609e399217#6a007c98292fa72965d36389ce32d7609e399217"
dependencies = [
"interrupt-support",
"lazy_static",
"log",
"parking_lot",
"rusqlite",
"rusqlite 0.33.0",
"tempfile",
"thiserror 1.999.999",
]
@ -6167,18 +6187,25 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "strum"
version = "0.26.3"
version = "0.26.999"
dependencies = [
"strum 0.27.1",
]
[[package]]
name = "strum"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.26.4"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8"
dependencies = [
"heck",
"proc-macro2",
@ -6290,7 +6317,7 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "suggest"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
source = "git+https://github.com/mozilla/application-services?rev=6a007c98292fa72965d36389ce32d7609e399217#6a007c98292fa72965d36389ce32d7609e399217"
dependencies = [
"anyhow",
"chrono",
@ -6302,7 +6329,7 @@ dependencies = [
"parking_lot",
"remote_settings",
"rmp-serde",
"rusqlite",
"rusqlite 0.33.0",
"serde",
"serde_json",
"sql-support",
@ -6342,18 +6369,18 @@ dependencies = [
[[package]]
name = "sync-guid"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
source = "git+https://github.com/mozilla/application-services?rev=6a007c98292fa72965d36389ce32d7609e399217#6a007c98292fa72965d36389ce32d7609e399217"
dependencies = [
"base64 0.21.999",
"rand",
"rusqlite",
"rusqlite 0.33.0",
"serde",
]
[[package]]
name = "sync15"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
source = "git+https://github.com/mozilla/application-services?rev=6a007c98292fa72965d36389ce32d7609e399217#6a007c98292fa72965d36389ce32d7609e399217"
dependencies = [
"anyhow",
"error-support",
@ -6393,7 +6420,7 @@ dependencies = [
[[package]]
name = "tabs"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
source = "git+https://github.com/mozilla/application-services?rev=6a007c98292fa72965d36389ce32d7609e399217#6a007c98292fa72965d36389ce32d7609e399217"
dependencies = [
"anyhow",
"error-support",
@ -6401,7 +6428,7 @@ dependencies = [
"lazy_static",
"log",
"payload-support",
"rusqlite",
"rusqlite 0.33.0",
"serde",
"serde_derive",
"serde_json",
@ -6737,9 +6764,9 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "types"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
source = "git+https://github.com/mozilla/application-services?rev=6a007c98292fa72965d36389ce32d7609e399217#6a007c98292fa72965d36389ce32d7609e399217"
dependencies = [
"rusqlite",
"rusqlite 0.33.0",
"serde",
"serde_derive",
"serde_json",
@ -7119,7 +7146,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "viaduct"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
source = "git+https://github.com/mozilla/application-services?rev=6a007c98292fa72965d36389ce32d7609e399217#6a007c98292fa72965d36389ce32d7609e399217"
dependencies = [
"ffi-support",
"log",
@ -7289,7 +7316,7 @@ dependencies = [
[[package]]
name = "webext-storage"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
source = "git+https://github.com/mozilla/application-services?rev=6a007c98292fa72965d36389ce32d7609e399217#6a007c98292fa72965d36389ce32d7609e399217"
dependencies = [
"anyhow",
"error-support",
@ -7298,7 +7325,7 @@ dependencies = [
"lazy_static",
"log",
"parking_lot",
"rusqlite",
"rusqlite 0.33.0",
"serde",
"serde_derive",
"serde_json",
@ -7334,7 +7361,7 @@ dependencies = [
"peek-poke",
"plane-split",
"rayon",
"ron",
"ron 0.10.1",
"serde",
"smallvec",
"svg_fmt",
@ -7427,8 +7454,8 @@ dependencies = [
[[package]]
name = "wgpu-core"
version = "24.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=a0dbe5ebc6fa24422fb84b2e0fea1cc94dee5109#a0dbe5ebc6fa24422fb84b2e0fea1cc94dee5109"
version = "25.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=f1c496523ff0aa10c162fd01ad606960e925a5a4#f1c496523ff0aa10c162fd01ad606960e925a5a4"
dependencies = [
"arrayvec",
"bit-set",
@ -7444,7 +7471,7 @@ dependencies = [
"once_cell",
"parking_lot",
"profiling",
"ron",
"ron 0.9.999",
"rustc-hash 1.999.999",
"serde",
"smallvec",
@ -7457,24 +7484,24 @@ dependencies = [
[[package]]
name = "wgpu-core-deps-apple"
version = "24.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=a0dbe5ebc6fa24422fb84b2e0fea1cc94dee5109#a0dbe5ebc6fa24422fb84b2e0fea1cc94dee5109"
version = "25.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=f1c496523ff0aa10c162fd01ad606960e925a5a4#f1c496523ff0aa10c162fd01ad606960e925a5a4"
dependencies = [
"wgpu-hal",
]
[[package]]
name = "wgpu-core-deps-windows-linux-android"
version = "24.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=a0dbe5ebc6fa24422fb84b2e0fea1cc94dee5109#a0dbe5ebc6fa24422fb84b2e0fea1cc94dee5109"
version = "25.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=f1c496523ff0aa10c162fd01ad606960e925a5a4#f1c496523ff0aa10c162fd01ad606960e925a5a4"
dependencies = [
"wgpu-hal",
]
[[package]]
name = "wgpu-hal"
version = "24.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=a0dbe5ebc6fa24422fb84b2e0fea1cc94dee5109#a0dbe5ebc6fa24422fb84b2e0fea1cc94dee5109"
version = "25.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=f1c496523ff0aa10c162fd01ad606960e925a5a4#f1c496523ff0aa10c162fd01ad606960e925a5a4"
dependencies = [
"android_system_properties",
"arrayvec",
@ -7509,8 +7536,8 @@ dependencies = [
[[package]]
name = "wgpu-types"
version = "24.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=a0dbe5ebc6fa24422fb84b2e0fea1cc94dee5109#a0dbe5ebc6fa24422fb84b2e0fea1cc94dee5109"
version = "25.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=f1c496523ff0aa10c162fd01ad606960e925a5a4#f1c496523ff0aa10c162fd01ad606960e925a5a4"
dependencies = [
"bitflags 2.9.0",
"bytemuck",

View file

@ -65,7 +65,7 @@ rust-version = "1.82.0"
uniffi = "0.29.1"
uniffi_bindgen = "0.29.1"
# Shared across multiple application-services consumers.
rusqlite = "0.31.0"
rusqlite = "0.33.0"
# Shared across multiple glean consumers.
glean = "=64.0.1"
@ -204,13 +204,21 @@ web-sys = { path = "build/rust/dummy-web/web-sys" }
# Upgrade `core-foundation` 0.9.* to 0.10.
core-foundation = { path = "build/rust/core-foundation" }
# Patch `core-graphics-types` 0.1.* to 0.2.
core-graphics-types = { path = "build/rust/core-graphics-types" }
# Patch `half` 1.* to 2.
half = { path = "build/rust/half" }
# Upgrade `rusqlite` 0.31 to 0.33.
rusqlite = { path = "build/rust/rusqlite" }
# Patch `ron` 0.9.* to 0.10.
ron = { path = "build/rust/ron" }
# Patch `strum` 0.26.* to 0.27.
strum = { path = "build/rust/strum" }
# Overrides to allow easier use of common internal crates.
moz_asserts = { path = "mozglue/static/rust/moz_asserts" }
@ -251,14 +259,14 @@ malloc_size_of_derive = { path = "xpcom/rust/malloc_size_of_derive" }
objc = { git = "https://github.com/glandium/rust-objc", rev = "4de89f5aa9851ceca4d40e7ac1e2759410c04324" }
# application-services overrides to make updating them all simpler.
interrupt-support = { git = "https://github.com/mozilla/application-services", rev = "8e84c588a5cc2686973c5026ecd240d6275d7e94" }
relevancy = { git = "https://github.com/mozilla/application-services", rev = "8e84c588a5cc2686973c5026ecd240d6275d7e94" }
search = { git = "https://github.com/mozilla/application-services", rev = "8e84c588a5cc2686973c5026ecd240d6275d7e94" }
sql-support = { git = "https://github.com/mozilla/application-services", rev = "8e84c588a5cc2686973c5026ecd240d6275d7e94" }
suggest = { git = "https://github.com/mozilla/application-services", rev = "8e84c588a5cc2686973c5026ecd240d6275d7e94" }
sync15 = { git = "https://github.com/mozilla/application-services", rev = "8e84c588a5cc2686973c5026ecd240d6275d7e94" }
tabs = { git = "https://github.com/mozilla/application-services", rev = "8e84c588a5cc2686973c5026ecd240d6275d7e94" }
viaduct = { git = "https://github.com/mozilla/application-services", rev = "8e84c588a5cc2686973c5026ecd240d6275d7e94" }
webext-storage = { git = "https://github.com/mozilla/application-services", rev = "8e84c588a5cc2686973c5026ecd240d6275d7e94" }
interrupt-support = { git = "https://github.com/mozilla/application-services", rev = "6a007c98292fa72965d36389ce32d7609e399217" }
relevancy = { git = "https://github.com/mozilla/application-services", rev = "6a007c98292fa72965d36389ce32d7609e399217" }
search = { git = "https://github.com/mozilla/application-services", rev = "6a007c98292fa72965d36389ce32d7609e399217" }
sql-support = { git = "https://github.com/mozilla/application-services", rev = "6a007c98292fa72965d36389ce32d7609e399217" }
suggest = { git = "https://github.com/mozilla/application-services", rev = "6a007c98292fa72965d36389ce32d7609e399217" }
sync15 = { git = "https://github.com/mozilla/application-services", rev = "6a007c98292fa72965d36389ce32d7609e399217" }
tabs = { git = "https://github.com/mozilla/application-services", rev = "6a007c98292fa72965d36389ce32d7609e399217" }
viaduct = { git = "https://github.com/mozilla/application-services", rev = "6a007c98292fa72965d36389ce32d7609e399217" }
webext-storage = { git = "https://github.com/mozilla/application-services", rev = "6a007c98292fa72965d36389ce32d7609e399217" }
allocator-api2 = { path = "third_party/rust/allocator-api2" }

View file

@ -14,9 +14,11 @@
#include "States.h"
#include "nsAttrName.h"
#include "nsGenericHTMLElement.h"
#include "nsWhitespaceTokenizer.h"
#include "mozilla/BinarySearch.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Element.h"
#include "nsUnicharUtils.h"
@ -1602,11 +1604,27 @@ uint8_t aria::AttrCharacteristicsFor(nsAtom* aAtom) {
return 0;
}
bool aria::HasDefinedARIAHidden(nsIContent* aContent) {
bool aria::IsValidARIAHidden(nsIContent* aContent) {
return aContent && aContent->IsElement() &&
nsAccUtils::ARIAAttrValueIs(aContent->AsElement(),
nsGkAtoms::aria_hidden, nsGkAtoms::_true,
eCaseMatters);
eCaseMatters) &&
!ShouldIgnoreARIAHidden(aContent);
}
bool aria::ShouldIgnoreARIAHidden(nsIContent* aContent) {
if (!aContent) {
return false;
}
dom::Document* doc = aContent->OwnerDoc();
bool isValidElementType = (aContent == doc->GetDocumentElement());
if (auto docBody = doc->GetBody()) {
isValidElementType |= (aContent == docBody->AsContent());
}
return isValidElementType && doc->IsTopLevelContentDocument();
}
const nsRoleMapEntry* aria::GetRoleMap(const nsStaticAtom* aAriaRole) {

View file

@ -305,9 +305,16 @@ uint64_t UniversalStatesFor(dom::Element* aElement);
uint8_t AttrCharacteristicsFor(nsAtom* aAtom);
/**
* Return true if the element has defined aria-hidden.
* Return true if the element has defined aria-hidden
* and should not be ignored per ShouldIgnoreARIAHidden.
*/
bool HasDefinedARIAHidden(nsIContent* aContent);
bool IsValidARIAHidden(nsIContent* aContent);
/**
* Return true if the element should render its subtree
* regardless of the presence of aria-hidden.
*/
bool ShouldIgnoreARIAHidden(nsIContent* aContent);
/**
* Get the role map entry for a given ARIA role.

View file

@ -43,6 +43,11 @@ bool RequestDomainsIfInactive(uint64_t aRequiredCacheDomains) {
const bool isMissingRequiredCacheDomain =
(aRequiredCacheDomains & ~activeCacheDomains) != 0;
if (isMissingRequiredCacheDomain) {
if (!accService->ShouldAllowNewCacheDomains()) {
// Return true to indicate that the domain is not active, but don't
// actually request it.
return true;
}
aRequiredCacheDomains = GetCacheDomainSuperset(aRequiredCacheDomains);
const uint64_t cacheDomains = aRequiredCacheDomains | activeCacheDomains;

View file

@ -272,7 +272,9 @@ bool DomainsAreActive(uint64_t aRequiredCacheDomains);
bool RequestDomainsIfInactive(uint64_t aRequiredCacheDomains);
#define ASSERT_DOMAINS_ACTIVE(aCacheDomains) \
MOZ_ASSERT(DomainsAreActive(aCacheDomains), \
MOZ_ASSERT( \
(GetAccService() && !GetAccService()->ShouldAllowNewCacheDomains()) || \
DomainsAreActive(aCacheDomains), \
"Required domain(s) are not currently active.")
} // namespace a11y

View file

@ -493,7 +493,7 @@ void NotificationController::ScheduleProcessing() {
// NotificationCollector: protected
bool NotificationController::IsUpdatePending() {
return mPresShell->ObservingStyleFlushes() ||
return mPresShell->NeedStyleFlush() || mPresShell->NeedLayoutFlush() ||
mObservingState == eRefreshProcessingForUpdate || WaitingForParent() ||
mContentInsertions.Count() != 0 || mNotifications.Length() != 0 ||
!mTextArray.IsEmpty() ||

View file

@ -8,7 +8,7 @@ import re
def generate(relH, relIdl):
input = open(relIdl, "rt").read()
input = open(relIdl).read()
relations = re.findall(
r"const unsigned long RELATION_([A-Z_]+) = ([x0-9a-f]+);", input
)

View file

@ -8,7 +8,7 @@ import re
def generate(roleH, roleIdl):
input = open(roleIdl, "rt").read()
input = open(roleIdl).read()
roles = re.findall(r"const unsigned long ROLE_([A-Z_]+) = (\d+);", input)
roleH.write(

View file

@ -1184,7 +1184,7 @@ LocalAccessible* nsAccessibilityService::CreateAccessible(
if (!aNode->IsContent()) return nullptr;
nsIContent* content = aNode->AsContent();
if (aria::HasDefinedARIAHidden(content)) {
if (aria::IsValidARIAHidden(content)) {
if (aIsSubtreeHidden) {
*aIsSubtreeHidden = true;
}
@ -2096,5 +2096,34 @@ void PrefChanged(const char* aPref, void* aClosure) {
}
}
uint32_t CacheDomainActivationBlocker::sEntryCount = 0;
CacheDomainActivationBlocker::CacheDomainActivationBlocker() {
AssertIsOnMainThread();
if (sEntryCount++ != 0) {
// We're re-entering. This can happen if an earlier event (even in a
// different document) ends up calling an XUL method, since that can run
// script which can cause other events to fire. Only the outermost usage
// should change the flag.
return;
}
if (nsAccessibilityService* service = GetAccService()) {
MOZ_ASSERT(service->mShouldAllowNewCacheDomains);
service->mShouldAllowNewCacheDomains = false;
}
}
CacheDomainActivationBlocker::~CacheDomainActivationBlocker() {
AssertIsOnMainThread();
if (--sEntryCount != 0) {
// Only the outermost usage should change the flag.
return;
}
if (nsAccessibilityService* service = GetAccService()) {
MOZ_ASSERT(!service->mShouldAllowNewCacheDomains);
service->mShouldAllowNewCacheDomains = true;
}
}
} // namespace a11y
} // namespace mozilla

View file

@ -92,6 +92,23 @@ void PrefChanged(const char* aPref, void* aClosure);
*/
EPlatformDisabledState ReadPlatformDisabledState();
/**
* RAII class to prevent new cache domains from being requested. This is
* necessary in some cases when code for an OS accessibility API requires
* information in order to fire an event. We don't necessarily know that a
* client is even interested in that event, so requesting data that the client
* may never query doesn't make sense.
*/
class MOZ_RAII CacheDomainActivationBlocker {
public:
CacheDomainActivationBlocker();
~CacheDomainActivationBlocker();
private:
// Used to manage re-entry.
static uint32_t sEntryCount;
};
} // namespace a11y
} // namespace mozilla
@ -346,6 +363,7 @@ class nsAccessibilityService final : public mozilla::a11y::DocManager,
};
static uint64_t GetActiveCacheDomains() { return gCacheDomains; }
bool ShouldAllowNewCacheDomains() { return mShouldAllowNewCacheDomains; }
#if defined(ANDROID)
static mozilla::Monitor& GetAndroidMonitor();
@ -415,6 +433,10 @@ class nsAccessibilityService final : public mozilla::a11y::DocManager,
* Contains the currently active cache domains.
*/
static uint64_t gCacheDomains;
// True if requesting new cache domains should be allowed, false if this
// should be disallowed. This should only be changed by
// CacheDomainActivationBlocker.
bool mShouldAllowNewCacheDomains = true;
// Can be weak because all atoms are known static
using MarkupMap = nsTHashMap<nsAtom*, const mozilla::a11y::MarkupMapInfo*>;
@ -450,6 +472,7 @@ class nsAccessibilityService final : public mozilla::a11y::DocManager,
friend mozilla::a11y::xpcAccessibleApplication*
mozilla::a11y::XPCApplicationAcc();
friend class xpcAccessibilityService;
friend class mozilla::a11y::CacheDomainActivationBlocker;
};
/**

View file

@ -862,7 +862,7 @@ void DocAccessible::AttributeChanged(dom::Element* aElement,
// Update the accessible tree on aria-hidden change. Make sure to not create
// a tree under aria-hidden='true'.
if (aAttribute == nsGkAtoms::aria_hidden) {
if (aria::HasDefinedARIAHidden(aElement)) {
if (aria::IsValidARIAHidden(aElement)) {
ContentRemoved(aElement);
} else {
ContentInserted(aElement, aElement->GetNextSibling());
@ -1206,7 +1206,7 @@ LocalAccessible* DocAccessible::GetAccessibleOrContainer(
for (nsINode* currNode : dom::InclusiveFlatTreeAncestors(*start)) {
// No container if is inside of aria-hidden subtree.
if (aNoContainerIfPruned && currNode->IsElement() &&
aria::HasDefinedARIAHidden(currNode->AsElement())) {
aria::IsValidARIAHidden(currNode->AsElement())) {
return nullptr;
}

View file

@ -12,7 +12,11 @@ prefs = [
]
["browser_atspi_interfaces.js"]
["browser_groupPosition.js"]
["browser_prune_children.js"]
["browser_role.js"]
["browser_table.js"]

View file

@ -31,8 +31,6 @@ skip-if = ["os != 'win'"]
["browser_scroll_position_domain.js"]
["browser_text_offset_attributes_domain.js"]
["browser_state_domain.js"]
["browser_style_domain.js"]
@ -43,6 +41,8 @@ skip-if = ["os != 'win'"]
["browser_text_domain.js"]
["browser_text_offset_attributes_domain.js"]
["browser_transform_matrix_domain.js"]
["browser_value_domain.js"]

View file

@ -20,6 +20,7 @@ support-files = [
["browser_aria_activedescendant.js"]
# Caching tests
["browser_caching_actions.js"]
["browser_caching_attributes.js"]
@ -59,6 +60,7 @@ skip-if = ["os != 'win'"]
["browser_caching_value.js"]
# Events tests
["browser_events_announcement.js"]
skip-if = ["os == 'win'"] # Bug 1288839
@ -81,6 +83,7 @@ skip-if = ["os == 'win'"] # Bug 1288839
["browser_obj_group_002.js"]
# Tree update tests
["browser_treeupdate_ariadialog.js"]
["browser_treeupdate_ariaowns.js"]

View file

@ -6,5 +6,7 @@ support-files = [
"!/accessible/tests/mochitest/*.js",
"!/accessible/tests/browser/*.mjs",
]
["browser_computedARIARole.js"]
["browser_minimumRole.js"]

View file

@ -351,8 +351,10 @@ function wrapWithIFrame(doc, options = {}) {
if (options.contentSetup) {
// Hide the body initially so we can ensure that any changes made by
// contentSetup are included when the body's content is initially added to
// the accessibility tree.
iframeDocBodyAttrs["aria-hidden"] = "true";
// the accessibility tree. Use `hidden` instead of `aria-hidden` because the
// latter is ignored when applied to top level docs/<body> elements and we
// want to remain consistent with our handling for non-iframe docs.
iframeDocBodyAttrs.hidden = true;
}
if (options.remoteIframe) {
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
@ -427,8 +429,9 @@ function snippetToURL(doc, options = {}) {
} else if (options.contentSetup) {
// Hide the body initially so we can ensure that any changes made by
// contentSetup are included when the body's content is initially added to
// the accessibility tree.
attrs["aria-hidden"] = "true";
// the accessibility tree. Use `hidden` instead of `aria-hidden` because the
// latter is ignored when applied to top level docs/<body> elements.
attrs.hidden = true;
}
const encodedDoc = encodeURIComponent(
@ -614,13 +617,13 @@ function accessibleTask(doc, task, options = {}) {
info("Executing contentSetup");
const ready = waitForEvent(EVENT_REORDER, currentContentDoc());
await invokeContentTask(browser, [], options.contentSetup);
// snippetToURL set aria-hidden on the body. We now Remove aria-hidden
// snippetToURL set hidden on the body. We now Remove hidden
// and wait for a reorder on the body. This guarantees that any
// changes made by contentSetup are included when the body's content
// is initially added to the accessibility tree and that the
// accessibility tree is up to date.
await invokeContentTask(browser, [], () => {
content.document.body.removeAttribute("aria-hidden");
content.document.body.removeAttribute("hidden");
});
await ready;
info("contentSetup done");

View file

@ -188,11 +188,6 @@ addAccessibleTask(
{ chrome: true, topLevel: false /* bug 1834129 */ }
);
if (
Services.prefs.getBoolPref(
"dom.element.contenteditable.plaintext-only.enabled"
)
) {
addAccessibleTask(
`<style>
#input {
@ -216,7 +211,6 @@ if (
},
{ chrome: true, topLevel: false /* bug 1834129 */ }
);
}
addAccessibleTask(
``,

View file

@ -28,6 +28,8 @@ skip-if = [
["browser_link.js"]
["browser_test_aria_hidden.js"]
["browser_searchbar.js"]
["browser_select.js"]

View file

@ -0,0 +1,329 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* import-globals-from ../../mochitest/role.js */
loadScripts({ name: "role.js", dir: MOCHITESTS_DIR });
/**
* Verify loading a root doc with aria-hidden renders the document.
* Non-root doc elements, like embedded iframes, should continue
* to respect aria-hidden when present. This test ONLY tests
* tab documents, it should not run in iframes. There is a separate
* test for iframes below.
*/
addAccessibleTask(
`
<p id="content">I am some content in a document</p>
`,
async function testTabDocument(browser, docAcc) {
const originalTree = { DOCUMENT: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }] };
testAccessibleTree(docAcc, originalTree);
},
{
chrome: true,
topLevel: true,
iframe: false,
remoteIframe: false,
contentDocBodyAttrs: { "aria-hidden": "true" },
}
);
/**
* Verify adding aria-hidden to root doc elements has no effect.
* Non-root doc elements, like embedded iframes, should continue
* to respect aria-hidden when applied. This test ONLY tests
* tab documents, it should not run in iframes. There is a separate
* test for iframes below.
*/
addAccessibleTask(
`
<p id="content">I am some content in a document</p>
`,
async function testTabDocumentMutation(browser, docAcc) {
const originalTree = { DOCUMENT: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }] };
testAccessibleTree(docAcc, originalTree);
info("Adding aria-hidden=true to content doc");
const unexpectedEvents = { unexpected: [[EVENT_REORDER, docAcc]] };
await contentSpawnMutation(browser, unexpectedEvents, function () {
const b = content.document.body;
b.setAttribute("aria-hidden", "true");
});
testAccessibleTree(docAcc, originalTree);
},
{ chrome: true, topLevel: true, iframe: false, remoteIframe: false }
);
/**
* Verify loading an iframe doc with aria-hidden doesn't render the document.
* This test ONLY tests iframe documents, it should not run in tab docs.
* There is a separate test for tab docs above.
*/
addAccessibleTask(
`
<p id="content">I am some content in a document</p>
`,
async function testIframeDocument(browser, docAcc, topLevel) {
const originalTree = { DOCUMENT: [{ INTERNAL_FRAME: [{ DOCUMENT: [] }] }] };
testAccessibleTree(topLevel, originalTree);
},
{
chrome: false,
topLevel: false,
iframe: true,
remoteIframe: true,
iframeDocBodyAttrs: { "aria-hidden": "true" },
}
);
/**
* Verify adding aria-hidden to iframe doc elements removes
* their subtree. This test ONLY tests iframe documents, it
* should not run in tab documents. There is a separate test for
* tab documents above.
*/
addAccessibleTask(
`
<p id="content">I am some content in a document</p>
`,
async function testIframeDocumentMutation(browser, docAcc, topLevel) {
const originalTree = {
DOCUMENT: [
{
INTERNAL_FRAME: [
{
DOCUMENT: [
{
PARAGRAPH: [
{
TEXT_LEAF: [],
},
],
},
],
},
],
},
],
};
testAccessibleTree(topLevel, originalTree);
info("Adding aria-hidden=true to content doc");
await contentSpawnMutation(
browser,
{ expected: [[EVENT_REORDER, docAcc]] },
function () {
const b = content.document.body;
b.setAttribute("aria-hidden", "true");
}
);
const newTree = {
DOCUMENT: [
{
INTERNAL_FRAME: [
{
DOCUMENT: [],
},
],
},
],
};
testAccessibleTree(topLevel, newTree);
},
{ chrome: false, topLevel: false, iframe: true, remoteIframe: true }
);
// // ///////////////////////////////
// // //////////////////// SVG Tests
// // //////////////////////////////
const SVG_DOCUMENT_ID = "rootSVG";
const HIDDEN_SVG_URI =
"data:image/svg+xml,%3Csvg%20id%3D%22rootSVG%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20aria-hidden%3D%22true%22%3E%3Ctext%20x%3D%2210%22%20y%3D%2250%22%20font-size%3D%2230%22%20id%3D%22textSVG%22%3EMy%20SVG%3C%2Ftext%3E%3C%2Fsvg%3E";
const SVG_URI =
"data:image/svg+xml,%3Csvg%20id%3D%22rootSVG%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Ctext%20x%3D%2210%22%20y%3D%2250%22%20font-size%3D%2230%22%20id%3D%22textSVG%22%3EMy%20SVG%3C%2Ftext%3E%3C%2Fsvg%3E";
/**
* Verify loading an SVG document with aria-hidden=true renders the
* entire document subtree.
* Non-root svg elements, like those in embedded iframes, should
* continue to respect aria-hidden when applied.
*/
addAccessibleTask(
`hello world`,
async function testSVGDocument(browser) {
let loaded = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, SVG_DOCUMENT_ID);
info("Loading SVG");
browser.loadURI(Services.io.newURI(HIDDEN_SVG_URI), {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
});
await loaded;
const tree = {
DOCUMENT: [
{
TEXT_CONTAINER: [
{
TEXT_LEAF: [],
},
],
},
],
};
const root = getRootAccessible(document);
const svgRoot = findAccessibleChildByID(root, SVG_DOCUMENT_ID);
testAccessibleTree(svgRoot, tree);
},
{ chrome: true, topLevel: true, iframe: false, remoteIframe: false }
);
///////////
///// TODO: Bug 1960416
//////////
// /**
// * Verify loading an SVG document with aria-hidden=true
// * in an iframe does not render the document subtree.
// */
// addAccessibleTask(
// `hello world`,
// async function testSVGIframeDocument(browser) {
// info("Loading SVG");
// const loaded = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, SVG_DOCUMENT_ID);
// await SpecialPowers.spawn(browser, [DEFAULT_IFRAME_ID, HIDDEN_SVG_URI], (_id,_uri) => {
// content.document.getElementById(_id).src = _uri;
// });
// await loaded;
// const tree = {
// DOCUMENT: [],
// };
// const root = getRootAccessible(document);
// const svgRoot = findAccessibleChildByID(root, SVG_DOCUMENT_ID);
// testAccessibleTree(svgRoot, tree);
// },
// { chrome: false, topLevel: false, iframe: true, remoteIframe: true }
// );
/**
* Verify adding aria-hidden to root svg elements has no effect.
* Non-root svg elements, like those in embedded iframes, should
* continue to respect aria-hidden when applied.
*/
addAccessibleTask(
`hello world`,
async function testSVGDocumentMutation(browser) {
let loaded = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, SVG_DOCUMENT_ID);
info("Loading SVG");
browser.loadURI(Services.io.newURI(SVG_URI), {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
});
await loaded;
const originalTree = {
DOCUMENT: [
{
TEXT_CONTAINER: [
{
TEXT_LEAF: [],
},
],
},
],
};
const root = getRootAccessible(document);
const svgRoot = findAccessibleChildByID(root, SVG_DOCUMENT_ID);
testAccessibleTree(svgRoot, originalTree);
info("Adding aria-hidden=true to svg");
// XXX Bug 1959547: We incorrectly get a reorder
// here. The tree should be unaffected by this attribute,
// but it seems like it isn't! Below we'll verify that
// the tree isn't removed, despite this reorder.
const unexpectedEvents = { expected: [[EVENT_REORDER, SVG_DOCUMENT_ID]] };
info("Adding aria-hidden");
await contentSpawnMutation(
browser,
unexpectedEvents,
function (_id) {
const d = content.document.getElementById(_id);
d.setAttribute("aria-hidden", "true");
},
[SVG_DOCUMENT_ID]
);
// XXX Bug 1959547: We end up with an extra node in the
// tree after adding aria-hidden. It seems like SVG root
// element is splitting off / no longer behaves as the
// document...?
const newTree = {
DOCUMENT: [
{
DIAGRAM: [
{
TEXT_CONTAINER: [
{
TEXT_LEAF: [],
},
],
},
],
},
],
};
testAccessibleTree(svgRoot, newTree);
},
{ chrome: true, topLevel: true, iframe: false, remoteIframe: false }
);
/**
* Verify adding aria-hidden to root svg elements in iframes removes
* the svg subtree.
*/
addAccessibleTask(
`hello world`,
async function testSVGIframeDocumentMutation(browser) {
info("Loading SVG");
const loaded = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, SVG_DOCUMENT_ID);
await SpecialPowers.spawn(
browser,
[DEFAULT_IFRAME_ID, SVG_URI],
(contentId, _uri) => {
content.document.getElementById(contentId).src = _uri;
}
);
await loaded;
const originalTree = {
DOCUMENT: [
{
TEXT_CONTAINER: [
{
TEXT_LEAF: [],
},
],
},
],
};
const svgRoot = findAccessibleChildByID(
getRootAccessible(document),
SVG_DOCUMENT_ID
);
testAccessibleTree(svgRoot, originalTree);
info("Adding aria-hidden=true to svg");
const events = { expected: [[EVENT_REORDER, SVG_DOCUMENT_ID]] };
await contentSpawnMutation(
browser,
events,
function (_id) {
const d = content.document.getElementById(_id);
d.setAttribute("aria-hidden", "true");
},
[SVG_DOCUMENT_ID]
);
const newTree = { DOCUMENT: [] };
testAccessibleTree(svgRoot, newTree);
},
{ chrome: false, topLevel: false, iframe: true, remoteIframe: true }
);

View file

@ -240,7 +240,7 @@ class WaitForWinEvent:
ctypes.oledll.ole32.CoWaitForMultipleHandles(
COWAIT_DEFAULT, TIMEOUT, 1, handles, ctypes.byref(index)
)
except WindowsError as e:
except OSError as e:
if e.winerror == RPC_S_CALLPENDING:
raise TimeoutError("Timeout before desired event received")
raise
@ -382,7 +382,7 @@ class WaitForUiaEvent(comtypes.COMObject):
ctypes.oledll.ole32.CoWaitForMultipleHandles(
COWAIT_DEFAULT, TIMEOUT, 1, handles, ctypes.byref(index)
)
except WindowsError as e:
except OSError as e:
if e.winerror == RPC_S_CALLPENDING:
raise TimeoutError("Timeout before desired event received")
raise

View file

@ -447,6 +447,29 @@ addUiaTask(
}
);
/**
* Test the Value pattern on a document.
*/
addUiaTask(``, async function testValueDoc(browser) {
// A test snippet is a data: URI. The accessibility engine won't return these.
let url = new URL("https://example.net/document-builder.sjs");
url.searchParams.append("html", `<body id=${DEFAULT_CONTENT_DOC_BODY_ID}>`);
let loaded = waitForEvent(
EVENT_DOCUMENT_LOAD_COMPLETE,
DEFAULT_CONTENT_DOC_BODY_ID
);
BrowserTestUtils.startLoadingURIString(browser, url.href);
await loaded;
await definePyVar("doc", `getDocUia()`);
await definePyVar("pattern", `getUiaPattern(doc, "Value")`);
ok(await runPython(`bool(pattern)`), "doc has Value pattern");
is(
await runPython(`pattern.CurrentValue`),
url.href,
"doc has correct Value"
);
});
async function testRangeValueProps(id, ro, val, min, max, small, large) {
await assignPyVarToUiaWithId(id);
await definePyVar("pattern", `getUiaPattern(${id}, "RangeValue")`);

View file

@ -91,9 +91,6 @@ class LabelTextLeafRule : public PivotRule {
static void MaybeRaiseUiaLiveRegionEvent(Accessible* aAcc,
uint32_t aGeckoEvent) {
if (!::UiaClientsAreListening()) {
return;
}
if (Accessible* live = nsAccUtils::GetLiveRegionRoot(aAcc)) {
auto* uia = MsaaAccessible::GetFrom(live);
::UiaRaiseAutomationEvent(uia, UIA_LiveRegionChangedEventId);
@ -140,13 +137,18 @@ Accessible* uiaRawElmProvider::Acc() const {
/* static */
void uiaRawElmProvider::RaiseUiaEventForGeckoEvent(Accessible* aAcc,
uint32_t aGeckoEvent) {
if (!Compatibility::IsUiaEnabled()) {
if (!Compatibility::IsUiaEnabled() || !::UiaClientsAreListening()) {
return;
}
auto* uia = MsaaAccessible::GetFrom(aAcc);
if (!uia) {
return;
}
// Some UIA events include or depend on data that might not be cached yet. We
// shouldn't request additional cache domains in this case because a client
// might not even care about these events. Instead, we use explicit client
// queries as a signal to request domains.
CacheDomainActivationBlocker cacheBlocker;
PROPERTYID property = 0;
_variant_t newVal;
bool gotNewVal = false;
@ -205,7 +207,7 @@ void uiaRawElmProvider::RaiseUiaEventForGeckoEvent(Accessible* aAcc,
gotNewVal = true;
break;
}
if (property && ::UiaClientsAreListening()) {
if (property) {
// We can't get the old value. Thankfully, clients don't seem to need it.
_variant_t oldVal;
if (!gotNewVal) {
@ -220,7 +222,7 @@ void uiaRawElmProvider::RaiseUiaEventForGeckoEvent(Accessible* aAcc,
void uiaRawElmProvider::RaiseUiaEventForStateChange(Accessible* aAcc,
uint64_t aState,
bool aEnabled) {
if (!Compatibility::IsUiaEnabled()) {
if (!Compatibility::IsUiaEnabled() || !::UiaClientsAreListening()) {
return;
}
auto* uia = MsaaAccessible::GetFrom(aAcc);
@ -260,12 +262,10 @@ void uiaRawElmProvider::RaiseUiaEventForStateChange(Accessible* aAcc,
return;
}
MOZ_ASSERT(property);
if (::UiaClientsAreListening()) {
// We can't get the old value. Thankfully, clients don't seem to need it.
_variant_t oldVal;
::UiaRaiseAutomationPropertyChangedEvent(uia, property, oldVal, newVal);
}
}
// IUnknown
@ -1009,6 +1009,11 @@ uiaRawElmProvider::get_Value(__RPC__deref_out_opt BSTR* aRetVal) {
}
nsAutoString value;
acc->Value(value);
if (value.IsEmpty() && acc->IsDoc()) {
// Exposing the URl via the Value pattern doesn't seem to be documented
// anywhere. However, Chromium does it, as does the IA2 -> UIA proxy.
nsAccUtils::DocumentURL(acc, value);
}
*aRetVal = ::SysAllocStringLen(value.get(), value.Length());
if (!*aRetVal) {
return E_OUTOFMEMORY;
@ -1391,7 +1396,7 @@ bool uiaRawElmProvider::HasValuePattern() const {
Accessible* acc = Acc();
MOZ_ASSERT(acc);
if (acc->HasNumericValue() || acc->IsCombobox() || acc->IsHTMLLink() ||
acc->IsTextField()) {
acc->IsTextField() || acc->IsDoc()) {
return true;
}
const nsRoleMapEntry* roleMapEntry = acc->ARIARoleMap();

View file

@ -132,7 +132,7 @@ def print_cpp_file(fd, conf, incdirs):
includes = []
for e in conf.simple_events:
if e not in includes:
includes.append(("nsIAccessible%s" % e))
includes.append("nsIAccessible%s" % e)
types = []
for e in conf.simple_events:

View file

@ -172,7 +172,13 @@ Accessible* XULContentSelectDropdownAccessible::Parent() const {
DocAccessibleParent* focusedDoc =
DocAccessibleParent::GetFrom(focusedContext);
MOZ_ASSERT(focusedDoc && focusedDoc->IsDoc(), "No focused document found");
if (NS_WARN_IF(!focusedDoc)) {
// We can fail to get a document here if a user is
// performing a drag-and-drop selection with mouse. See
// `browser/base/content/tests/browser_selectpopup_large.js`
return LocalParent();
}
MOZ_ASSERT(focusedDoc->IsDoc(), "Got non-document?");
focusedAcc = focusedDoc->AsDoc()->GetFocusedAcc();
}

View file

@ -3,7 +3,6 @@
# 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 io
import re
import sys
from optparse import OptionParser
@ -28,9 +27,7 @@ if not options.version:
# builds), but also so that newly-built older versions (e.g. beta build) aren't
# considered "newer" than previously-built newer versions (e.g. a trunk nightly)
define, MOZ_BUILDID, buildid = (
io.open(options.buildid, "r", encoding="utf-8").read().split()
)
define, MOZ_BUILDID, buildid = open(options.buildid, encoding="utf-8").read().split()
# extract only the major version (i.e. "14" from "14.0b1")
majorVersion = re.match(r"^(\d+)[^\d].*", options.version).group(1)

View file

@ -71,6 +71,13 @@ pref("extensions.webextensions.remote", true);
pref("extensions.langpacks.signatures.required", true);
pref("xpinstall.signatures.required", true);
// Enable data collection permissions.
#ifdef NIGHTLY_BUILD
pref("extensions.dataCollectionPermissions.enabled", true);
#else
pref("extensions.dataCollectionPermissions.enabled", false);
#endif
// Dictionary download preference
pref("browser.dictionaries.download.url", "https://addons.mozilla.org/%LOCALE%/firefox/language-tools/");
@ -480,11 +487,7 @@ pref("browser.urlbar.richSuggestions.featureGate", true);
pref("browser.search.param.search_rich_suggestions", "fen");
// Feature gate pref for weather suggestions in the urlbar.
#ifdef NIGHTLY_BUILD
pref("browser.urlbar.weather.featureGate", true);
#else
pref("browser.urlbar.weather.featureGate", false);
#endif
// Enable clipboard suggestions feature, the pref should be removed once stable.
pref("browser.urlbar.clipboard.featureGate", false);
@ -578,10 +581,8 @@ pref("browser.urlbar.quicksuggest.impressionCaps.sponsoredEnabled", false);
// When non-zero, this is the character-count threshold (inclusive) for showing
// AMP suggestions as top picks. If an AMP suggestion is triggered by a keyword
// at least this many characters long, it will be shown as a top pick. Full
// keywords will also show AMP suggestions as top picks even if they have fewer
// characters than this threshold.
pref("browser.urlbar.quicksuggest.ampTopPickCharThreshold", 0);
// at least this many characters long, it will be shown as a top pick.
pref("browser.urlbar.quicksuggest.ampTopPickCharThreshold", 5);
// The matching strategy for AMP suggestions. Zero is the usual default
// exact-keyword strategy. Other values are the integers defined on
@ -907,6 +908,9 @@ pref("browser.spin_cursor_while_busy", false);
// Enable display of contextual-password-manager option in browser sidebar
pref("browser.contextual-password-manager.enabled", false);
// Add the "Passwords" tool to the sidebar if contextual-password-manager is enabled.
pref("sidebar.newTool.migration.passwords", '{ "visibilityPref": "browser.contextual-password-manager.enabled"}');
// Enables the display of the Mozilla VPN banner in private browsing windows
pref("browser.privatebrowsing.vpnpromourl", "https://vpn.mozilla.org/?utm_source=firefox-browser&utm_medium=firefox-%CHANNEL%-browser&utm_campaign=private-browsing-vpn-link");
@ -1054,11 +1058,7 @@ pref("browser.tabs.tooltipsShowPidAndActiveness", false);
pref("browser.tabs.hoverPreview.enabled", true);
pref("browser.tabs.hoverPreview.showThumbnails", true);
#ifdef EARLY_BETA_OR_EARLIER
pref("browser.tabs.groups.enabled", true);
#else
pref("browser.tabs.groups.enabled", false);
#endif
#ifdef NIGHTLY_BUILD
pref("browser.tabs.groups.smart.enabled", true);
@ -1815,6 +1815,12 @@ pref("browser.newtab.preload", true);
// population (2500 / 10000).
pref("browser.preonboarding.onTrainRolloutPopulation", 2500);
// Show "Download Firefox for mobile" QR code modal on newtab
pref("browser.newtabpage.activity-stream.mobileDownloadModal.enabled", false);
pref("browser.newtabpage.activity-stream.mobileDownloadModal.variant-a", false);
pref("browser.newtabpage.activity-stream.mobileDownloadModal.variant-b", false);
pref("browser.newtabpage.activity-stream.mobileDownloadModal.variant-c", false);
// Mozilla Ad Routing Service (MARS) unified ads service
pref("browser.newtabpage.activity-stream.unifiedAds.tiles.enabled", true);
pref("browser.newtabpage.activity-stream.unifiedAds.spocs.enabled", true);
@ -1990,6 +1996,7 @@ pref("browser.newtabpage.activity-stream.discoverystream.sections.region-content
pref("browser.newtabpage.activity-stream.discoverystream.sections.cards.enabled", true);
pref("browser.newtabpage.activity-stream.discoverystream.sections.personalization.inferred.enabled", false);
pref("browser.newtabpage.activity-stream.discoverystream.sections.personalization.inferred.user.enabled", true);
pref("browser.newtabpage.activity-stream.discoverystream.sections.personalization.inferred.blocked", false);
pref("browser.newtabpage.activity-stream.discoverystream.sections.interestPicker.enabled", false);
pref("browser.newtabpage.activity-stream.discoverystream.sections.interestPicker.visibleSections", "");
@ -2023,6 +2030,11 @@ pref("browser.newtabpage.activity-stream.discoverystream.onboardingExperience.en
// List of locales that get thumbs up/down on recommended stories by default.
pref("browser.newtabpage.activity-stream.discoverystream.thumbsUpDown.locale-thumbs-config", "en-US, en-GB, en-CA");
pref("browser.newtabpage.activity-stream.telemetry.privatePing.enabled", false);
// surface ID sent from merino to the client from the curated-recommendations request
pref("browser.newtabpage.activity-stream.telemetry.surfaceId", "");
// List of regions that get thumbs up/down on recommended stories by default.
#ifdef EARLY_BETA_OR_EARLIER
pref("browser.newtabpage.activity-stream.discoverystream.thumbsUpDown.region-thumbs-config", "US, CA");
@ -2554,20 +2566,24 @@ pref("browser.tabs.crashReporting.sendReport", true);
pref("browser.tabs.crashReporting.includeURL", false);
// Enables the "Unload Tab" context menu item
#ifdef NIGHTLY_BUILD
#ifdef EARLY_BETA_OR_EARLIER
pref("browser.tabs.unloadTabInContextMenu", true);
#else
pref("browser.tabs.unloadTabInContextMenu", false);
#endif
// Whether tabs that have been explicitly unloaded
// are faded out in the tab bar.
#ifdef EARLY_BETA_OR_EARLIER
pref("browser.tabs.fadeOutExplicitlyUnloadedTabs", true);
#else
pref("browser.tabs.fadeOutExplicitlyUnloadedTabs", false);
#endif
// Whether unloaded tabs (either from session restore or because
// they are explicitly unloaded) are faded out in the tab bar.
pref("browser.tabs.fadeOutUnloadedTabs", false);
// Whether tabs that have been explicitly unloaded
// are faded out in the tab bar.
pref("browser.tabs.fadeOutExplicitlyUnloadedTabs", false);
// If true, unprivileged extensions may use experimental APIs on
// nightly and developer edition.
pref("extensions.experiments.enabled", false);
@ -2823,7 +2839,6 @@ pref("identity.fxaccounts.toolbar.pxiToolbarEnabled.vpnEnabled", true);
// Prefs to control Mozilla account panels that shows an updated flow
// for users who don't have sync enabled
pref("identity.fxaccounts.toolbar.syncSetup.enabled", false);
pref("identity.fxaccounts.toolbar.syncSetup.panelAccessed", false);
// Toolbox preferences
@ -2985,6 +3000,7 @@ pref("devtools.netmonitor.panes-network-details-height", 450);
pref("devtools.netmonitor.panes-search-width", 550);
pref("devtools.netmonitor.panes-search-height", 450);
pref("devtools.netmonitor.filters", "[\"all\"]");
pref("devtools.netmonitor.requestfilter", "");
pref("devtools.netmonitor.visibleColumns",
"[\"override\",\"status\",\"method\",\"domain\",\"file\",\"initiator\",\"type\",\"transferred\",\"contentSize\",\"waterfall\"]"
);

View file

@ -614,19 +614,7 @@
</hbox>
</toolbarbutton>
<vbox id="PanelUI-signedin-panel" hidden="true">
<toolbarbutton id="PanelUI-fxa-menu-setup-sync-button"
class="subviewbutton"
data-l10n-id="appmenu-fxa-setup-sync"
hidden="true"
/>
<!-- The `Connect Another Device` button is disabled by default until the user logs into Sync. -->
<toolbarbutton id="PanelUI-fxa-menu-connect-device-button"
class="subviewbutton"
data-l10n-id="fxa-menu-connect-another-device"
disabled="true"
/>
<vbox id="PanelUI-fxa-menu-setup-sync-container" hidden="true">
<toolbarseparator id="PanelUI-set-up-sync-separator" />
<vbox class="PanelUI-fxa-menu-setup-sync" flex="1">
<hbox class="sync-status-container" align="center">
<image class="sync-status-indicator" role="presentation"/>
@ -640,11 +628,18 @@
class="sync-setup-description"
data-l10n-id="appmenuitem-fxa-sync-off-description"
crop="end"/>
<toolbarbutton id="PanelUI-fxa-menu-setup-sync-button-new"
<toolbarbutton id="PanelUI-fxa-menu-setup-sync-button"
class="subviewbutton"
data-l10n-id="appmenu-fxa-setup-sync-new"/>
</vbox>
<toolbarseparator id="PanelUI-set-up-sync-separator" />
</vbox>
<!-- The `Connect Another Device` button is disabled by default until the user logs into Sync. -->
<toolbarbutton id="PanelUI-fxa-menu-connect-device-button"
class="subviewbutton"
data-l10n-id="fxa-menu-connect-another-device"
disabled="true"
/>
<toolbarbutton id="PanelUI-fxa-menu-sendtab-button"
class="subviewbutton subviewbutton-nav"
data-l10n-id="fxa-menu-send-tab-to-device"

View file

@ -410,7 +410,7 @@ customElements.define(
});
doc.l10n.setAttributes(
checkboxEl,
"popup-notification-addon-privatebrowsing-checkbox"
"popup-notification-addon-privatebrowsing-checkbox2"
);
return checkboxEl;
}
@ -419,9 +419,14 @@ customElements.define(
const { grantTechnicalAndInteractionDataCollection } =
this.notification.options.customElementOptions;
MozXULElement.insertFTLIfNeeded(
"locales-preview/dataCollectionPermissions.ftl"
);
const checkboxEl = this.ownerDocument.createXULElement("checkbox");
checkboxEl.label = lazy.PERMISSION_L10N.formatValueSync(
"webext-perms-description-data-long-technicalAndInteraction"
this.ownerDocument.l10n.setAttributes(
checkboxEl,
"popup-notification-addon-technicalAndInteraction-checkbox"
);
checkboxEl.checked = grantTechnicalAndInteractionDataCollection;
checkboxEl.addEventListener("CheckboxStateChange", () => {

View file

@ -134,7 +134,7 @@ var gBrowserInit = {
// Call this after we set attributes that might change toolbars' computed
// text color.
ToolbarIconColor.init();
ToolbarIconColor.init(window);
},
onDOMContentLoaded() {
@ -274,6 +274,9 @@ var gBrowserInit = {
gBrowser.adoptTabGroup(tabToAdopt.group, 0);
gBrowser.removeTab(gBrowser.selectedTab);
} else {
if (tabToAdopt.group) {
Glean.tabgroup.tabInteractions.remove_new_window.add();
}
gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToAdopt);
}
@ -1000,7 +1003,7 @@ var gBrowserInit = {
CustomTitlebar.uninit();
ToolbarIconColor.uninit();
ToolbarIconColor.uninit(window);
// In certain scenarios it's possible for unload to be fired before onload,
// (e.g. if the window is being closed after browser.js loads but before the

View file

@ -787,12 +787,6 @@ var gSync = {
if (ctaCopyVariant) {
NimbusFeatures.fxaAvatarMenuItem.recordExposureEvent();
}
// We want to record exposure if the user has sync disabled and has
// clicked to open the FxA panel
if (this.isSignedIn && !UIState.get().syncEnabled) {
NimbusFeatures.syncSetupFlow.recordExposureEvent();
}
},
onFxAPanelViewHiding(panelview) {
@ -804,11 +798,9 @@ var gSync = {
onCommand(button) {
switch (button.id) {
case "PanelUI-fxa-menu-sync-prefs-button":
// fall through
case "PanelUI-fxa-menu-setup-sync-button":
this.openPrefsFromFxaMenu("sync_settings", button);
break;
case "PanelUI-fxa-menu-setup-sync-button-new":
case "PanelUI-fxa-menu-setup-sync-button":
this.openChooseWhatToSync("sync_settings", button);
break;
@ -1136,16 +1128,9 @@ var gSync = {
},
_disableSyncOffIndicator() {
const newSyncSetupEnabled =
NimbusFeatures.syncSetupFlow.getVariable("enabled");
const SYNC_PANEL_ACCESSED_PREF =
"identity.fxaccounts.toolbar.syncSetup.panelAccessed";
// If the user was enrolled in the experiment and hasn't previously accessed
// the panel, we disable the sync off indicator
if (
newSyncSetupEnabled &&
!Services.prefs.getBoolPref(SYNC_PANEL_ACCESSED_PREF, false)
) {
if (!Services.prefs.getBoolPref(SYNC_PANEL_ACCESSED_PREF, false)) {
// Turn off the indicator so the user doesn't see it in subsequent openings
Services.prefs.setBoolPref(SYNC_PANEL_ACCESSED_PREF, true);
}
@ -1154,20 +1139,15 @@ var gSync = {
_shouldShowSyncOffIndicator() {
// We only ever want to show the user the dot once, once they've clicked into the panel
// we do not show them the dot anymore
if (
Services.prefs.getBoolPref(
return !Services.prefs.getBoolPref(
"identity.fxaccounts.toolbar.syncSetup.panelAccessed",
false
)
) {
return false;
}
return NimbusFeatures.syncSetupFlow.getVariable("enabled");
);
},
updateFxAPanel(state = {}) {
const isNewSyncSetupFlowEnabled =
NimbusFeatures.syncSetupFlow.getVariable("enabled");
const expandedSignInCopy =
NimbusFeatures.expandSignInButton.getVariable("ctaCopyVariant");
const mainWindowEl = document.documentElement;
const menuHeaderTitleEl = PanelMultiView.getViewNode(
@ -1206,16 +1186,51 @@ var gSync = {
document,
"PanelUI-fxa-menu-profiles-separator"
);
const syncSetupEl = PanelMultiView.getViewNode(
document,
"PanelUI-fxa-menu-setup-sync-container"
);
const fxaToolbarMenuButton = document.getElementById(
"fxa-toolbar-menu-button"
);
let fxaAvatarLabelEl = document.getElementById("fxa-avatar-label");
// Reset FxA/Sync UI elements to default, which is signed out
cadButtonEl.setAttribute("disabled", true);
cadButtonEl.hidden = isNewSyncSetupFlowEnabled;
syncNowButtonEl.hidden = true;
signedInContainer.hidden = true;
fxaMenuAccountButtonEl.classList.remove("subviewbutton-nav");
fxaMenuAccountButtonEl.removeAttribute("closemenu");
menuHeaderDescriptionEl.hidden = false;
// Expanded sign in copy experiment is only for signed out users
// so if a text variant has been provided then we show the expanded label
// otherwise it'll be the default avatar icon
// fxaToolbarMenuButton can be null in certain testing scenarios
if (fxaToolbarMenuButton) {
if (
state.status === UIState.STATUS_NOT_CONFIGURED &&
expandedSignInCopy
) {
fxaAvatarLabelEl.setAttribute(
"value",
this.fluentStrings.formatValueSync(expandedSignInCopy)
);
fxaAvatarLabelEl.removeAttribute("hidden");
fxaToolbarMenuButton.setAttribute("data-l10n-id", "fxa-avatar-tooltip");
fxaToolbarMenuButton.classList.add("avatar-button-background");
} else {
// Either signed in, or experiment not enabled
fxaToolbarMenuButton.setAttribute(
"data-l10n-id",
"toolbar-button-account"
);
fxaToolbarMenuButton.classList.remove("avatar-button-background");
fxaAvatarLabelEl.hidden = true;
}
}
// The Firefox Account toolbar currently handles 3 different states for
// users. The default `not_configured` state shows an empty avatar, `unverified`
// state shows an avatar with an email icon, `login-failed` state shows an avatar
@ -1282,34 +1297,17 @@ var gSync = {
signedInContainer.hidden = false;
cadButtonEl.removeAttribute("disabled");
// Due to bug 1951719, we toggle both old and new sync setup
// elements as some platforms had a delay during sign-in/out
// that there were some scenarios where both showed up or the
// incorrect one
const oldSyncSetupEl = PanelMultiView.getViewNode(
document,
"PanelUI-fxa-menu-setup-sync-button"
);
const newSyncSetupEl = PanelMultiView.getViewNode(
document,
"PanelUI-fxa-menu-setup-sync-container"
);
if (state.syncEnabled) {
// Always show sync now and connect another device button when sync is enabled
syncNowButtonEl.removeAttribute("hidden");
cadButtonEl.removeAttribute("hidden");
oldSyncSetupEl.setAttribute("hidden", "true");
newSyncSetupEl.setAttribute("hidden", "true");
syncSetupEl.setAttribute("hidden", "true");
} else {
if (this._shouldShowSyncOffIndicator()) {
let fxaButton = document.getElementById("fxa-toolbar-menu-button");
fxaButton?.setAttribute("badge-status", "sync-disabled");
fxaToolbarMenuButton?.setAttribute("badge-status", "sync-disabled");
}
// Show the sync element depending on if the user is enrolled or not
isNewSyncSetupFlowEnabled
? newSyncSetupEl.removeAttribute("hidden")
: oldSyncSetupEl.removeAttribute("hidden");
syncSetupEl.removeAttribute("hidden");
}
// Reposition profiles elements

View file

@ -92,6 +92,7 @@ ChromeUtils.defineESModuleGetters(this, {
TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.sys.mjs",
ToolbarContextMenu: "resource:///modules/ToolbarContextMenu.sys.mjs",
ToolbarDropHandler: "resource:///modules/ToolbarDropHandler.sys.mjs",
ToolbarIconColor: "moz-src:///browser/themes/ToolbarIconColor.sys.mjs",
TranslationsParent: "resource://gre/actors/TranslationsParent.sys.mjs",
UITour: "moz-src:///browser/components/uitour/UITour.sys.mjs",
UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs",
@ -5039,6 +5040,9 @@ function duplicateTabIn(aTab, where, delta) {
});
break;
}
if (aTab.group) {
Glean.tabgroup.tabInteractions.duplicate.add();
}
}
var MousePosTracker = {
@ -5124,120 +5128,6 @@ var MousePosTracker = {
},
};
var ToolbarIconColor = {
_windowState: {
active: false,
fullscreen: false,
customtitlebar: false,
},
init() {
this._initialized = true;
window.addEventListener("nativethemechange", this);
window.addEventListener("activate", this);
window.addEventListener("deactivate", this);
window.addEventListener("toolbarvisibilitychange", this);
window.addEventListener("windowlwthemeupdate", this);
// If the window isn't active now, we assume that it has never been active
// before and will soon become active such that inferFromText will be
// called from the initial activate event.
if (Services.focus.activeWindow == window) {
this.inferFromText("activate");
}
},
uninit() {
this._initialized = false;
window.removeEventListener("nativethemechange", this);
window.removeEventListener("activate", this);
window.removeEventListener("deactivate", this);
window.removeEventListener("toolbarvisibilitychange", this);
window.removeEventListener("windowlwthemeupdate", this);
},
handleEvent(event) {
switch (event.type) {
case "activate":
case "deactivate":
case "nativethemechange":
case "windowlwthemeupdate":
this.inferFromText(event.type);
break;
case "toolbarvisibilitychange":
this.inferFromText(event.type, event.visible);
break;
}
},
// a cache of luminance values for each toolbar
// to avoid unnecessary calls to getComputedStyle
_toolbarLuminanceCache: new Map(),
inferFromText(reason, reasonValue) {
if (!this._initialized) {
return;
}
switch (reason) {
case "activate": // falls through
case "deactivate":
this._windowState.active = reason === "activate";
break;
case "fullscreen":
this._windowState.fullscreen = reasonValue;
break;
case "nativethemechange":
case "windowlwthemeupdate":
// theme change, we'll need to recalculate all color values
this._toolbarLuminanceCache.clear();
break;
case "toolbarvisibilitychange":
// toolbar changes dont require reset of the cached color values
break;
case "customtitlebar":
this._windowState.customtitlebar = reasonValue;
break;
}
let toolbarSelector = ".browser-toolbar:not([collapsed=true])";
if (AppConstants.platform == "macosx") {
toolbarSelector += ":not([type=menubar])";
}
// The getComputedStyle calls and setting the brighttext are separated in
// two loops to avoid flushing layout and making it dirty repeatedly.
let cachedLuminances = this._toolbarLuminanceCache;
let luminances = new Map();
for (let toolbar of document.querySelectorAll(toolbarSelector)) {
// toolbars *should* all have ids, but guard anyway to avoid blowing up
let cacheKey =
toolbar.id && toolbar.id + JSON.stringify(this._windowState);
// lookup cached luminance value for this toolbar in this window state
let luminance = cacheKey && cachedLuminances.get(cacheKey);
if (isNaN(luminance)) {
let { r, g, b } = InspectorUtils.colorToRGBA(
getComputedStyle(toolbar).color
);
luminance = 0.2125 * r + 0.7154 * g + 0.0721 * b;
if (cacheKey) {
cachedLuminances.set(cacheKey, luminance);
}
}
luminances.set(toolbar, luminance);
}
const luminanceThreshold = 127; // In between 0 and 255
for (let [toolbar, luminance] of luminances) {
if (luminance <= luminanceThreshold) {
toolbar.removeAttribute("brighttext");
} else {
toolbar.setAttribute("brighttext", "true");
}
}
},
};
var PanicButtonNotifier = {
init() {
this._initialized = true;

View file

@ -30,11 +30,7 @@
data-l10n-sync="true">
<head>
<!-- CSP might be disabled by C++ code. -->
#if defined(EARLY_BETA_OR_EARLIER)
<meta http-equiv="Content-Security-Policy" content="script-src chrome: moz-src: resource: 'report-sample'" />
#else
<meta http-equiv="Content-Security-Policy" content="script-src-attr 'none' 'report-sample'" />
#endif
<!-- The "global.css" stylesheet is imported first to allow other stylesheets to
override rules using selectors with the same specificity. This applies to

View file

@ -339,16 +339,31 @@
</menupopup>
<menupopup id="sidebar-history-context-menu">
<menuitem data-l10n-id="sidebar-history-context-menu-delete-page"
id="sidebar-history-context-delete-page"/>
<menuseparator/>
<menuitem data-l10n-id="sidebar-context-menu-open-in-tab"
id="sidebar-history-context-open-in-tab"/>
<menu id="sidebar-history-context-menu-container-tab"
data-l10n-id="sidebar-context-menu-open-in-container-tab"
selection-type="single"
node-type="link"
hide-if-private-browsing="true"
hide-if-usercontext-disabled="true">
<menupopup id="sidebar-history-context-menu-container-popup"/>
</menu>
<menuitem data-l10n-id="sidebar-context-menu-open-in-window"
id="sidebar-history-context-open-in-window"/>
<menuitem data-l10n-id="sidebar-context-menu-open-in-private-window"
id="sidebar-history-context-open-in-private-window"/>
<menuseparator/>
<menuitem data-l10n-id="sidebar-history-context-menu-delete-page-2"
id="sidebar-history-context-delete-page"/>
<menuitem data-l10n-id="sidebar-context-menu-forget-site"
id="sidebar-history-context-forget-site"/>
<menuseparator/>
<menuitem data-l10n-id="sidebar-context-menu-copy-link"
id="sidebar-history-context-copy-link"/>
<menuseparator/>
<menuitem data-l10n-id="sidebar-history-context-menu-bookmark-page"
id="sidebar-history-context-bookmark-page"/>
</menupopup>
<menupopup id="sidebar-synced-tabs-context-menu">

View file

@ -385,6 +385,16 @@ document.addEventListener(
}
});
const containerHistoryPopup = document.getElementById(
"sidebar-history-context-menu-container-popup"
);
containerHistoryPopup.addEventListener("command", event =>
PlacesUIUtils.openInContainerTab(event)
);
containerHistoryPopup.addEventListener("popupshowing", event =>
PlacesUIUtils.createContainerTabMenu(event)
);
document
.getElementById("context_reopenInContainerPopupMenu")
.addEventListener("command", event => {

View file

@ -463,9 +463,10 @@
data-l10n-id="toolbar-button-account"
cui-areatype="toolbar"
removable="true">
<vbox>
<hbox>
<image id="fxa-avatar-image"/>
</vbox>
<label id="fxa-avatar-label" hidden="true"/>
</hbox>
</toolbarbutton>
<toolbarbutton id="unified-extensions-button"

View file

@ -176,8 +176,6 @@ export class nsContextMenu {
nsContextMenu.contentData = null;
}
this.remoteType = this.actor?.domProcess?.remoteType;
const { gBrowser } = this.window;
this.shouldDisplay = context.shouldDisplay;
@ -276,6 +274,8 @@ export class nsContextMenu {
);
}
this.remoteType = this.actor.manager.domProcess.remoteType;
this.selectedText = this.selectionInfo.text;
this.isTextSelected = !!this.selectedText.length;
this.webExtBrowserType = this.browser.getAttribute(

View file

@ -15,7 +15,7 @@ add_task(async function test_shortcut_key_label_in_fullscreen_menu_item() {
const shortCutKeyLabel = isMac ? "\u2303\u2318F" : "F11";
const enterMenuItemId = isMac ? "enterFullScreenItem" : "fullScreenItem";
const exitMenuItemId = isMac ? "exitFullScreenItem" : "fullScreenItem";
const accelKeyLabelSelector = ".menu-accel-container > label";
const accelKeyLabelSelector = ".menu-accel";
const tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,

View file

@ -20,6 +20,10 @@ const PREF_ACCEPTED_POLICY_VERSION =
PREF_BRANCH + "dataSubmissionPolicyAcceptedVersion";
const PREF_ACCEPTED_POLICY_DATE =
PREF_BRANCH + "dataSubmissionPolicyNotifiedTime";
const PREF_TOS_ROLLOUT_POPULATION =
"browser.preonboarding.onTrainRolloutPopulation";
const PREF_TOS_ROLLOUT_ENROLLED =
"browser.preonboarding.enrolledInOnTrainRollout";
const PREF_TELEMETRY_LOG_LEVEL = "toolkit.telemetry.log.level";
@ -127,6 +131,8 @@ add_setup(async function () {
Preferences.set(PREF_BYPASS_NOTIFICATION, bypassNotification);
Preferences.set(PREF_CURRENT_POLICY_VERSION, currentPolicyVersion);
Preferences.reset(PREF_TELEMETRY_LOG_LEVEL);
Preferences.reset(PREF_TOS_ROLLOUT_ENROLLED);
Preferences.reset(PREF_TOS_ROLLOUT_POPULATION);
return closeAllNotifications();
});
@ -138,6 +144,9 @@ add_setup(async function () {
// Ensure this isn't the first run, because then we open the first run page.
Preferences.set(PREF_FIRST_RUN, false);
TelemetryReportingPolicy.testUpdateFirstRun();
// Do not trigger ToS modal for Linux, Mac, and MSIX builds
Preferences.set(PREF_TOS_ROLLOUT_ENROLLED, false);
Preferences.set(PREF_TOS_ROLLOUT_POPULATION, 0);
});
function clearAcceptedPolicy() {

View file

@ -29,6 +29,7 @@ const startupPhases = {
allowlist: {
modules: new Set([
"resource:///modules/BrowserGlue.sys.mjs",
"moz-src:///browser/components/DesktopActorRegistry.sys.mjs",
"resource:///modules/StartupRecorder.sys.mjs",
"resource://gre/modules/AppConstants.sys.mjs",
"resource://gre/modules/ActorManagerParent.sys.mjs",

View file

@ -20,6 +20,8 @@ add_task(async function () {
let unexpectedRects = 0;
let alreadyFocused = false;
let inRange = (val, min, max) => min <= val && val <= max;
let tabBoundingRect = undefined;
for (let i = 1; i < frames.length; ++i) {
let frame = frames[i],
previousFrame = frames[i - 1];
@ -41,6 +43,22 @@ add_task(async function () {
/**
* Please don't add anything new unless justified!
*/
{
name: "Shadow around active tab should not flicker on macOS (bug 1960967)",
condition(r) {
const tabRect = tabBoundingRect
? tabBoundingRect
: (tabBoundingRect = gBrowser.tabContainer
.querySelector("tab[selected=true] .tab-background")
.getBoundingClientRect());
return (
inRange(r.x1, tabRect.x - 2, tabRect.x + 2) &&
inRange(r.y1, tabRect.y - 2, tabRect.y + 2) &&
inRange(r.w, tabRect.width - 4, tabRect.width + 4) &&
inRange(r.h, tabRect.height - 4, tabRect.height + 4)
);
},
},
];
let rectText = `${rect.toSource()}, window width: ${width}`;

View file

@ -42,6 +42,8 @@ add_task(async function () {
"We shouldn't have added any new expected reflows for window close."
);
let inRange = (val, min, max) => min <= val && val <= max;
let tabBoundingRect = undefined;
await withPerfObserver(
async function () {
let promiseOrigBrowserFocused = TestUtils.waitForCondition(() => {
@ -60,6 +62,24 @@ add_task(async function () {
}
return rects;
},
exceptions: [
{
name: "Shadow around active tab should not flicker on macOS (bug 1960967)",
condition(r) {
const tabRect = tabBoundingRect
? tabBoundingRect
: (tabBoundingRect = win.gBrowser.tabContainer
.querySelector("tab[selected=true] .tab-background")
.getBoundingClientRect());
return (
inRange(r.x1, tabRect.x - 2, tabRect.x + 2) &&
inRange(r.y1, tabRect.y - 2, tabRect.y + 2) &&
inRange(r.w, tabRect.width - 4, tabRect.width + 4) &&
inRange(r.h, tabRect.height - 4, tabRect.height + 4)
);
},
},
],
},
},
win

View file

@ -41,6 +41,7 @@ add_task(async function () {
let alreadyFocused = false;
let inRange = (val, min, max) => min <= val && val <= max;
let tabBoundingRect = undefined;
let expectations = {
expectedReflows: EXPECTED_REFLOWS,
frames: {
@ -108,6 +109,22 @@ add_task(async function () {
) && // in the toolbar
inRange(r.x1, 30, 90), // close to the left of the screen
},
{
name: "Shadow around active tab should not flicker on macOS (bug 1960967)",
condition(r) {
const tabRect = tabBoundingRect
? tabBoundingRect
: (tabBoundingRect = gBrowser.tabContainer
.querySelector("tab[selected=true] .tab-background")
.getBoundingClientRect());
return (
inRange(r.x1, tabRect.x - 2, tabRect.x + 2) &&
inRange(r.y1, tabRect.y - 2, tabRect.y + 2) &&
inRange(r.w, tabRect.width - 4, tabRect.width + 4) &&
inRange(r.h, tabRect.height - 4, tabRect.height + 4)
);
},
},
],
},
};

View file

@ -35,7 +35,7 @@ function dirtyFrame(win) {
* @param win (browser window, optional)
* The browser window to monitor. Defaults to the current window.
*
* @return An array of reflow stacks
* @return An array of reflow stacks and paths
*/
async function recordReflows(testPromise, win = window) {
// Collect all reflow stacks, we'll process them later.
@ -44,7 +44,33 @@ async function recordReflows(testPromise, win = window) {
let observer = {
reflow() {
// Gather information about the current code path.
reflows.push(new Error().stack);
let stack = new Error().stack;
let path = stack
.trim()
.split("\n")
.slice(1) // the first frame which is our test code.
.map(line => line.replace(/:\d+:\d+$/, "")); // strip line numbers.
// Stack trace is empty. Reflow was triggered by native code, which
// we ignore.
if (path.length === 0) {
ChromeUtils.addProfilerMarker(
"ignoredNativeReflow",
{ category: "Test" },
"Intentionally ignoring reflow without JS stack"
);
return;
}
if (
path[0] ===
"forceRefreshDriverTick@chrome://mochikit/content/tests/SimpleTest/AccessibilityUtils.js"
) {
// a11y-checks fake a refresh driver tick.
return;
}
reflows.push({ stack, path: path.join("|") });
// Just in case, dirty the frame now that we've reflowed. This will
// allow us to detect additional reflows that occur in this same tick
@ -59,8 +85,8 @@ async function recordReflows(testPromise, win = window) {
},
reflowInterruptible() {
// Interruptible reflows are the reflows caused by the refresh
// driver ticking. These are fine.
// Interruptible reflows are always triggered by native code, like the
// refresh driver. These are fine.
},
QueryInterface: ChromeUtils.generateQI([
@ -152,19 +178,7 @@ function reportUnexpectedReflows(reflows, expectedReflows = []) {
);
}
for (let stack of reflows) {
let path = stack
.split("\n")
.slice(1) // the first frame which is our test code.
.map(line => line.replace(/:\d+:\d+$/, "")) // strip line numbers.
.join("|");
// Stack trace is empty. Reflow was triggered by native code, which
// we ignore.
if (path === "") {
continue;
}
for (let { stack, path } of reflows) {
// Functions from EventUtils.js calculate coordinates and
// dimensions, causing us to reflow. That's the test
// harness and we don't care about that, so we'll filter that out.

View file

@ -13,6 +13,8 @@ support-files = ["head.js"]
["browser_all_files_referenced.js"]
skip-if = ["verify && bits == 32"] # Causes OOMs when run repeatedly
["browser_glean_metrics_exist.js"]
["browser_misused_characters_in_strings.js"]
support-files = ["bug1262648_string_with_newlines.dtd"]
skip-if = ["os == 'win' && msix"] # Permafail on MSIX packages due to it running on files it shouldn't.
@ -24,8 +26,6 @@ skip-if = ["os == 'win' && msix"] # Permafail on MSIX packages due to it running
["browser_parsable_script.js"]
skip-if = ["ccov && os == 'linux'"] # https://bugzilla.mozilla.org/show_bug.cgi?id=1608081
["browser_glean_metrics_exist.js"]
["browser_sentence_case_strings.js"]
["browser_title_case_menus.js"]

View file

@ -214,7 +214,8 @@ add_task(async function test_ui_state_signedin() {
"PanelUI-fxa-menu-account-signout-button",
],
disabledItems: [],
hiddenItems: ["PanelUI-fxa-menu-setup-sync-button"],
hiddenItems: ["PanelUI-fxa-menu-setup-sync-container"],
visibleItems: [],
});
await checkProfilesButtons(
@ -431,7 +432,6 @@ add_task(async function test_ui_state_signed_in() {
enabledItems: [
"PanelUI-fxa-menu-sendtab-button",
"PanelUI-fxa-menu-connect-device-button",
"PanelUI-fxa-menu-setup-sync-button",
"PanelUI-fxa-menu-account-signout-button",
],
disabledItems: [],
@ -439,6 +439,7 @@ add_task(async function test_ui_state_signed_in() {
"PanelUI-fxa-menu-syncnow-button",
"PanelUI-fxa-menu-sync-prefs-button",
],
visibleItems: ["PanelUI-fxa-menu-setup-sync-container"],
});
checkFxAAvatar("signedin");
await closeFxaPanel();
@ -476,7 +477,6 @@ add_task(async function test_ui_state_signed_in_no_display_name() {
enabledItems: [
"PanelUI-fxa-menu-sendtab-button",
"PanelUI-fxa-menu-connect-device-button",
"PanelUI-fxa-menu-setup-sync-button",
"PanelUI-fxa-menu-account-signout-button",
],
disabledItems: [],
@ -484,6 +484,7 @@ add_task(async function test_ui_state_signed_in_no_display_name() {
"PanelUI-fxa-menu-syncnow-button",
"PanelUI-fxa-menu-sync-prefs-button",
],
visibleItems: ["PanelUI-fxa-menu-setup-sync-container"],
});
checkFxAAvatar("signedin");
await closeFxaPanel();
@ -523,7 +524,6 @@ add_task(async function test_ui_state_unverified() {
headerDescription: state.email,
enabledItems: [
"PanelUI-fxa-menu-sendtab-button",
"PanelUI-fxa-menu-setup-sync-button",
"PanelUI-fxa-menu-account-signout-button",
],
disabledItems: ["PanelUI-fxa-menu-connect-device-button"],
@ -531,6 +531,7 @@ add_task(async function test_ui_state_unverified() {
"PanelUI-fxa-menu-syncnow-button",
"PanelUI-fxa-menu-sync-prefs-button",
],
visibleItems: ["PanelUI-fxa-menu-setup-sync-container"],
});
checkFxAAvatar("unverified");
await closeFxaPanel();
@ -570,7 +571,6 @@ add_task(async function test_ui_state_loginFailed() {
headerDescription: state.displayName,
enabledItems: [
"PanelUI-fxa-menu-sendtab-button",
"PanelUI-fxa-menu-setup-sync-button",
"PanelUI-fxa-menu-account-signout-button",
],
disabledItems: ["PanelUI-fxa-menu-connect-device-button"],
@ -578,6 +578,7 @@ add_task(async function test_ui_state_loginFailed() {
"PanelUI-fxa-menu-syncnow-button",
"PanelUI-fxa-menu-sync-prefs-button",
],
visibleItems: ["PanelUI-fxa-menu-setup-sync-container"],
});
checkFxAAvatar("login-failed");
await closeFxaPanel();
@ -691,6 +692,7 @@ add_task(async function test_experiment_ui_state_unconfigured() {
"PanelUI-fxa-menu-syncnow-button",
"PanelUI-fxa-menu-sync-prefs-button",
],
visibleItems: [],
});
// Revert the pref at the end of the test
@ -759,7 +761,8 @@ add_task(async function test_experiment_ui_state_signedin() {
"PanelUI-fxa-menu-vpn-button",
],
disabledItems: [],
hiddenItems: ["PanelUI-fxa-menu-setup-sync-button"],
hiddenItems: ["PanelUI-fxa-menu-setup-sync-container"],
visibleItems: [],
});
checkFxAAvatar("signedin");
gSync.relativeTimeFormat = origRelativeTimeFormat;
@ -781,16 +784,7 @@ add_task(async function test_experiment_ui_state_signedin() {
await closeTabAndMainPanel();
});
add_task(async function test_new_sync_setup_ui_exp_enabled() {
// Enroll in the experiment with the feature enabled
await ExperimentAPI.ready();
let doCleanup = await ExperimentFakes.enrollWithFeatureConfig({
featureId: NimbusFeatures.syncSetupFlow.featureId,
value: {
enabled: true,
},
});
add_task(async function test_new_sync_setup_ui() {
let state = {
status: UIState.STATUS_SIGNED_IN,
syncEnabled: false,
@ -811,20 +805,21 @@ add_task(async function test_new_sync_setup_ui_exp_enabled() {
headerDescription: "Foo Bar",
enabledItems: [
"PanelUI-fxa-menu-sendtab-button",
"PanelUI-fxa-menu-setup-sync-container", // New set-up element should be visible
"PanelUI-fxa-menu-account-signout-button",
"PanelUI-fxa-menu-connect-device-button",
],
disabledItems: [],
hiddenItems: [
"PanelUI-fxa-menu-syncnow-button",
"PanelUI-fxa-menu-sync-prefs-button",
"PanelUI-fxa-menu-connect-device-button", // CAD should also be hidden
"PanelUI-fxa-menu-setup-sync-button", // Old button should be hidden
],
visibleItems: [
"PanelUI-fxa-menu-setup-sync-container",
"PanelUI-fxa-menu-connect-device-button",
],
});
await closeFxaPanel();
await doCleanup();
// We need to reset the panel back to hidden since in the code we flip between the old and new sync setup ids
// so subsequent tests will fail if checking this new container
@ -834,52 +829,6 @@ add_task(async function test_new_sync_setup_ui_exp_enabled() {
newSyncSetup.setAttribute("hidden", true);
});
add_task(async function test_new_sync_setup_ui_no_exp() {
// Enroll in the experiment with the feature disabled
await ExperimentAPI.ready();
let doCleanup = await ExperimentFakes.enrollWithFeatureConfig({
featureId: NimbusFeatures.syncSetupFlow.featureId,
value: {
enabled: false,
},
});
let state = {
status: UIState.STATUS_SIGNED_IN,
syncEnabled: false,
email: "foo@bar.com",
displayName: "Foo Bar",
avatarURL: "https://foo.bar",
};
gSync.updateAllUI(state);
await openFxaPanel();
checkMenuBarItem("sync-enable");
checkPanelHeader();
checkFxaToolbarButtonPanel({
headerTitle: "Manage account",
headerDescription: "Foo Bar",
enabledItems: [
"PanelUI-fxa-menu-sendtab-button",
"PanelUI-fxa-menu-connect-device-button",
"PanelUI-fxa-menu-setup-sync-button", // Old setup button should be visible
"PanelUI-fxa-menu-account-signout-button",
],
disabledItems: [],
hiddenItems: [
"PanelUI-fxa-menu-syncnow-button",
"PanelUI-fxa-menu-sync-prefs-button",
"PanelUI-fxa-menu-setup-sync-container", // New setup container should be hidden
],
});
await doCleanup();
await closeFxaPanel();
});
// Ensure we can see the new "My services" section if the user has enabled relay on their account
add_task(async function test_ui_my_services_signedin() {
await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com/");
@ -938,9 +887,10 @@ add_task(async function test_ui_my_services_signedin() {
],
disabledItems: [],
hiddenItems: [
"PanelUI-fxa-menu-setup-sync-button",
"PanelUI-fxa-menu-setup-sync-container",
"PanelUI-fxa-menu-relay-button", // the relay button in the "other protections" side should be hidden
],
visibleItems: [],
});
checkFxAAvatar("signedin");
gSync.relativeTimeFormat = origRelativeTimeFormat;
@ -962,6 +912,91 @@ add_task(async function test_ui_my_services_signedin() {
await closeTabAndMainPanel();
});
add_task(async function test_experiment_signin_button_signed_out() {
// Enroll in Nimbus experiment
await ExperimentAPI.ready();
let cleanupNimbus = await ExperimentFakes.enrollWithFeatureConfig({
featureId: NimbusFeatures.expandSignInButton.featureId,
value: {
ctaCopyVariant: "fxa-avatar-sign-in",
},
});
// Set UI state to STATUS_NOT_CONFIGURED (signed out)
let state = { status: UIState.STATUS_NOT_CONFIGURED };
gSync.updateAllUI(state);
const fxaAvatarLabel = document.getElementById("fxa-avatar-label");
ok(fxaAvatarLabel, "Avatar label element should exist");
is(
fxaAvatarLabel.hidden,
false,
"Avatar label should be visible when Nimbus experiment is enabled"
);
const expectedLabel =
gSync.fluentStrings.formatValueSync("fxa-avatar-sign-in");
is(
fxaAvatarLabel.getAttribute("value"),
expectedLabel,
`Avatar label should have the expected localized value: ${expectedLabel}`
);
// Clean up experiment
await cleanupNimbus();
// Reset UI state after experiment cleanup
gSync.updateAllUI(state);
is(
fxaAvatarLabel.hidden,
true,
"Avatar label should be hidden after Nimbus experiment is cleaned up"
);
});
add_task(async function test_experiment_signin_button_signed_in() {
// Enroll in Nimbus experiment
await ExperimentAPI.ready();
let cleanupNimbus = await ExperimentFakes.enrollWithFeatureConfig({
featureId: NimbusFeatures.expandSignInButton.featureId,
value: {
ctaCopyVariant: "fxa-avatar-sign-in",
},
});
let state = {
status: UIState.STATUS_SIGNED_IN,
syncEnabled: true,
email: "foo@bar.com",
displayName: "Foo Bar",
avatarURL: "https://foo.bar",
lastSync: new Date(),
syncing: false,
};
gSync.updateAllUI(state);
const fxaAvatarLabel = document.getElementById("fxa-avatar-label");
is(
fxaAvatarLabel.hidden,
true,
"Avatar label should never be visible when signed in"
);
// Clean up experiment
await cleanupNimbus();
// Reset UI state after experiment cleanup
gSync.updateAllUI(state);
is(
fxaAvatarLabel.hidden,
true,
"Avatar label should still be hidden when signed in without experiment"
);
});
function checkPanelUIStatusBar({
description,
title,
@ -1053,6 +1088,7 @@ async function checkFxaToolbarButtonPanel({
enabledItems,
disabledItems,
hiddenItems,
visibleItems,
}) {
is(
document.getElementById("fxa-menu-header-title").value,
@ -1079,6 +1115,25 @@ async function checkFxaToolbarButtonPanel({
const el = document.getElementById(id);
is(el.getAttribute("hidden"), "true", id + " is hidden");
}
for (const id of visibleItems) {
const el = document.getElementById(id);
ok(isElementVisible(el), `${id} is visible`);
}
}
function isElementVisible(el) {
if (!el) {
return false;
}
let style = window.getComputedStyle(el);
// The “hidden” property on the element itself
// might not exist or might be false, so we also
// check that the computed style is not hiding it
// (display: none or visibility: hidden).
return (
!el.hidden && style.display !== "none" && style.visibility !== "hidden"
);
}
async function checkProfilesButtons(

View file

@ -43,14 +43,14 @@ support-files = ["openPromptOffTimeout.html"]
["browser_promptFocus.js"]
["browser_prompt_close_event.js"]
["browser_prompt_close_groups.js"]
support-files = ["file_beforeunload_stop.html"]
fail-if = [
"a11y_checks",
] # Bug 1953248 a11y_checks fails when testing multiple tab prompts
["browser_prompt_close_event.js"]
["browser_prompt_closed_window.js"]
["browser_switchTabPermissionPrompt.js"]

View file

@ -54,6 +54,11 @@ category browser-idle-startup resource://gre/modules/UpdateListener.sys.mjs Upda
category browser-idle-startup resource:///modules/WindowsJumpLists.sys.mjs WinTaskbarJumpList.startup
#endif
# Note that these telemetry entries schedule their own idle tasks,
# so they are guaranteed to run after everything else.
category browser-idle-startup moz-src:///browser/components/StartupTelemetry.sys.mjs StartupTelemetry.browserIdleStartup
category browser-best-effort-idle-startup moz-src:///browser/components/StartupTelemetry.sys.mjs StartupTelemetry.bestEffortIdleStartup
# App shutdown consumers
category browser-quit-application-granted resource:///modules/BrowserUsageTelemetry.sys.mjs BrowserUsageTelemetry.uninit
category browser-quit-application-granted resource:///modules/Interactions.sys.mjs Interactions.uninit

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,232 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
let lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
CommonDialog: "resource://gre/modules/CommonDialog.sys.mjs",
SessionStartup: "resource:///modules/sessionstore/SessionStartup.sys.mjs",
});
export var DefaultBrowserCheck = {
async prompt(win) {
const shellService = win.getShellService();
const needPin =
(await shellService.doesAppNeedPin()) ||
(await shellService.doesAppNeedStartMenuPin());
win.MozXULElement.insertFTLIfNeeded("branding/brand.ftl");
win.MozXULElement.insertFTLIfNeeded(
"browser/defaultBrowserNotification.ftl"
);
// Record default prompt impression
let now = Math.floor(Date.now() / 1000).toString();
Services.prefs.setCharPref(
"browser.shell.mostRecentDefaultPromptSeen",
now
);
// Resolve the translations for the prompt elements and return only the
// string values
let pinMessage;
if (AppConstants.platform == "macosx") {
pinMessage = "default-browser-prompt-message-pin-mac";
} else if (
AppConstants.platform == "win" &&
Services.sysinfo.getProperty("hasWinPackageId", false)
) {
pinMessage = "default-browser-prompt-message-pin-msix";
} else {
pinMessage = "default-browser-prompt-message-pin";
}
let [promptTitle, promptMessage, askLabel, yesButton, notNowButton] = (
await win.document.l10n.formatMessages([
{
id: needPin
? "default-browser-prompt-title-pin"
: "default-browser-prompt-title-alt",
},
{
id: needPin ? pinMessage : "default-browser-prompt-message-alt",
},
{ id: "default-browser-prompt-checkbox-not-again-label" },
{
id: needPin
? "default-browser-prompt-button-primary-set"
: "default-browser-prompt-button-primary-alt",
},
{ id: "default-browser-prompt-button-secondary" },
])
).map(({ value }) => value);
let ps = Services.prompt;
let buttonFlags =
ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0 +
ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_1 +
ps.BUTTON_POS_0_DEFAULT;
let rv = await ps.asyncConfirmEx(
win.browsingContext,
ps.MODAL_TYPE_INTERNAL_WINDOW,
promptTitle,
promptMessage,
buttonFlags,
yesButton,
notNowButton,
null,
askLabel,
false, // checkbox state
{
headerIconCSSValue: lazy.CommonDialog.DEFAULT_APP_ICON_CSS,
}
);
let buttonNumClicked = rv.get("buttonNumClicked");
let checkboxState = rv.get("checked");
if (buttonNumClicked == 0) {
// We must explicitly await pinning to the taskbar before
// trying to set as default. If we fall back to setting
// as default through the Windows Settings menu that interferes
// with showing the pinning notification as we no longer have
// window focus.
try {
await shellService.pinToTaskbar();
} catch (e) {
this.log.error("Failed to pin to taskbar", e);
}
try {
await shellService.pinToStartMenu();
} catch (e) {
this.log.error("Failed to pin to Start Menu", e);
}
try {
await shellService.setAsDefault();
} catch (e) {
this.log.error("Failed to set the default browser", e);
}
}
if (checkboxState) {
shellService.shouldCheckDefaultBrowser = false;
Services.prefs.setCharPref("browser.shell.userDisabledDefaultCheck", now);
}
try {
let resultEnum = buttonNumClicked * 2 + !checkboxState;
Glean.browser.setDefaultResult.accumulateSingleSample(resultEnum);
} catch (ex) {
/* Don't break if Telemetry is acting up. */
}
},
/**
* Checks if the default browser check prompt will be shown.
*
* @param {boolean} isStartupCheck
* If true, prefs will be set and telemetry will be recorded.
* @returns {boolean} True if the default browser check prompt will be shown.
*/
async willCheckDefaultBrowser(isStartupCheck) {
let win = lazy.BrowserWindowTracker.getTopWindow();
let shellService = win.getShellService();
// Perform default browser checking.
if (!shellService) {
return false;
}
let shouldCheck =
!AppConstants.DEBUG && shellService.shouldCheckDefaultBrowser;
// Even if we shouldn't check the default browser, we still continue when
// isStartupCheck = true to set prefs and telemetry.
if (!shouldCheck && !isStartupCheck) {
return false;
}
// Skip the "Set Default Browser" check during first-run or after the
// browser has been run a few times.
const skipDefaultBrowserCheck =
Services.prefs.getBoolPref(
"browser.shell.skipDefaultBrowserCheckOnFirstRun"
) &&
!Services.prefs.getBoolPref(
"browser.shell.didSkipDefaultBrowserCheckOnFirstRun"
);
let promptCount = Services.prefs.getIntPref(
"browser.shell.defaultBrowserCheckCount",
0
);
// If SessionStartup's state is not initialized, checking sessionType will set
// its internal state to "do not restore".
await lazy.SessionStartup.onceInitialized;
let willRecoverSession =
lazy.SessionStartup.sessionType == lazy.SessionStartup.RECOVER_SESSION;
// Don't show the prompt if we're already the default browser.
let isDefault = false;
let isDefaultError = false;
try {
isDefault = shellService.isDefaultBrowser(isStartupCheck, false);
} catch (ex) {
isDefaultError = true;
}
if (isDefault && isStartupCheck) {
let now = Math.floor(Date.now() / 1000).toString();
Services.prefs.setCharPref(
"browser.shell.mostRecentDateSetAsDefault",
now
);
}
let willPrompt = shouldCheck && !isDefault && !willRecoverSession;
if (willPrompt) {
if (skipDefaultBrowserCheck) {
if (isStartupCheck) {
Services.prefs.setBoolPref(
"browser.shell.didSkipDefaultBrowserCheckOnFirstRun",
true
);
}
willPrompt = false;
} else {
promptCount++;
if (isStartupCheck) {
Services.prefs.setIntPref(
"browser.shell.defaultBrowserCheckCount",
promptCount
);
}
if (!AppConstants.RELEASE_OR_BETA && promptCount > 3) {
willPrompt = false;
}
}
}
if (isStartupCheck) {
try {
// Report default browser status on startup to telemetry
// so we can track whether we are the default.
Glean.browser.isUserDefault[isDefault ? "true" : "false"].add();
Glean.browser.isUserDefaultError[
isDefaultError ? "true" : "false"
].add();
Glean.browser.setDefaultAlwaysCheck[
shouldCheck ? "true" : "false"
].add();
Glean.browser.setDefaultDialogPromptRawcount.accumulateSingleSample(
promptCount
);
} catch (ex) {
/* Don't break the default prompt if telemetry is broken. */
}
}
return willPrompt;
},
};

View file

@ -0,0 +1,831 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { ActorManagerParent } from "resource://gre/modules/ActorManagerParent.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
SelectableProfileService:
"resource:///modules/profiles/SelectableProfileService.sys.mjs",
});
/**
* Fission-compatible JSProcess implementations.
* Each actor options object takes the form of a ProcessActorOptions dictionary.
* Detailed documentation of these options is in dom/docs/ipc/jsactors.rst,
* available at https://firefox-source-docs.mozilla.org/dom/ipc/jsactors.html
*/
let JSPROCESSACTORS = {
// Miscellaneous stuff that needs to be initialized per process.
BrowserProcess: {
child: {
esModuleURI: "resource:///actors/BrowserProcessChild.sys.mjs",
observers: [
// WebRTC related notifications. They are here to avoid loading WebRTC
// components when not needed.
"getUserMedia:request",
"recording-device-stopped",
"PeerConnection:request",
"recording-device-events",
"recording-window-ended",
],
},
},
RefreshBlockerObserver: {
child: {
esModuleURI: "resource:///actors/RefreshBlockerChild.sys.mjs",
observers: [
"webnavigation-create",
"chrome-webnavigation-create",
"webnavigation-destroy",
"chrome-webnavigation-destroy",
],
},
enablePreference: "accessibility.blockautorefresh",
onPreferenceChanged: isEnabled => {
lazy.BrowserWindowTracker.orderedWindows.forEach(win => {
for (let browser of win.gBrowser.browsers) {
try {
browser.sendMessageToActor(
"PreferenceChanged",
{ isEnabled },
"RefreshBlocker",
"all"
);
} catch (ex) {}
}
});
},
},
};
/**
* Fission-compatible JSWindowActor implementations.
* Detailed documentation of these options is in dom/docs/ipc/jsactors.rst,
* available at https://firefox-source-docs.mozilla.org/dom/ipc/jsactors.html
*/
let JSWINDOWACTORS = {
Megalist: {
parent: {
esModuleURI: "resource://gre/actors/MegalistParent.sys.mjs",
},
child: {
esModuleURI: "resource://gre/actors/MegalistChild.sys.mjs",
events: {
DOMContentLoaded: {},
},
},
includeChrome: true,
matches: ["chrome://global/content/megalist/megalist.html"],
allFrames: true,
enablePreference: "browser.contextual-password-manager.enabled",
},
AboutLogins: {
parent: {
esModuleURI: "resource:///actors/AboutLoginsParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/AboutLoginsChild.sys.mjs",
events: {
AboutLoginsCopyLoginDetail: { wantUntrusted: true },
AboutLoginsCreateLogin: { wantUntrusted: true },
AboutLoginsDeleteLogin: { wantUntrusted: true },
AboutLoginsDismissBreachAlert: { wantUntrusted: true },
AboutLoginsImportFromBrowser: { wantUntrusted: true },
AboutLoginsImportFromFile: { wantUntrusted: true },
AboutLoginsImportReportInit: { wantUntrusted: true },
AboutLoginsImportReportReady: { wantUntrusted: true },
AboutLoginsInit: { wantUntrusted: true },
AboutLoginsGetHelp: { wantUntrusted: true },
AboutLoginsOpenPreferences: { wantUntrusted: true },
AboutLoginsOpenSite: { wantUntrusted: true },
AboutLoginsRecordTelemetryEvent: { wantUntrusted: true },
AboutLoginsRemoveAllLogins: { wantUntrusted: true },
AboutLoginsSortChanged: { wantUntrusted: true },
AboutLoginsSyncEnable: { wantUntrusted: true },
AboutLoginsUpdateLogin: { wantUntrusted: true },
AboutLoginsExportPasswords: { wantUntrusted: true },
},
},
matches: ["about:logins", "about:logins?*", "about:loginsimportreport"],
allFrames: true,
remoteTypes: ["privilegedabout"],
},
AboutMessagePreview: {
parent: {
esModuleURI: "resource:///actors/AboutMessagePreviewParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/AboutMessagePreviewChild.sys.mjs",
events: {
DOMDocElementInserted: { capture: true },
},
},
matches: ["about:messagepreview", "about:messagepreview?*"],
},
AboutPocket: {
parent: {
esModuleURI: "resource:///actors/AboutPocketParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/AboutPocketChild.sys.mjs",
events: {
DOMDocElementInserted: { capture: true },
},
},
remoteTypes: ["privilegedabout"],
matches: [
"about:pocket-saved*",
"about:pocket-signup*",
"about:pocket-home*",
"about:pocket-style-guide*",
],
},
AboutPrivateBrowsing: {
parent: {
esModuleURI: "resource:///actors/AboutPrivateBrowsingParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/AboutPrivateBrowsingChild.sys.mjs",
events: {
DOMDocElementInserted: { capture: true },
},
},
matches: ["about:privatebrowsing*"],
},
AboutProtections: {
parent: {
esModuleURI: "resource:///actors/AboutProtectionsParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/AboutProtectionsChild.sys.mjs",
events: {
DOMDocElementInserted: { capture: true },
},
},
matches: ["about:protections", "about:protections?*"],
},
AboutReader: {
parent: {
esModuleURI: "resource:///actors/AboutReaderParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/AboutReaderChild.sys.mjs",
events: {
DOMContentLoaded: {},
pageshow: { mozSystemGroup: true },
// Don't try to create the actor if only the pagehide event fires.
// This can happen with the initial about:blank documents.
pagehide: { mozSystemGroup: true, createActor: false },
},
},
messageManagerGroups: ["browsers"],
},
AboutTabCrashed: {
parent: {
esModuleURI: "resource:///actors/AboutTabCrashedParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/AboutTabCrashedChild.sys.mjs",
events: {
DOMDocElementInserted: { capture: true },
},
},
matches: ["about:tabcrashed*"],
},
AboutWelcomeShopping: {
parent: {
esModuleURI: "resource:///actors/AboutWelcomeParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/AboutWelcomeChild.sys.mjs",
events: {
Update: {},
},
},
matches: ["about:shoppingsidebar"],
remoteTypes: ["privilegedabout"],
messageManagerGroups: ["shopping-sidebar", "browsers", "review-checker"],
},
AboutWelcome: {
parent: {
esModuleURI: "resource:///actors/AboutWelcomeParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/AboutWelcomeChild.sys.mjs",
events: {
// This is added so the actor instantiates immediately and makes
// methods available to the page js on load.
DOMDocElementInserted: {},
},
},
matches: ["about:welcome"],
remoteTypes: ["privilegedabout"],
// See Bug 1618306
// Remove this preference check when we turn on separate about:welcome for all users.
enablePreference: "browser.aboutwelcome.enabled",
},
BackupUI: {
parent: {
esModuleURI: "resource:///actors/BackupUIParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/BackupUIChild.sys.mjs",
events: {
"BackupUI:InitWidget": { wantUntrusted: true },
"BackupUI:EnableScheduledBackups": { wantUntrusted: true },
"BackupUI:DisableScheduledBackups": { wantUntrusted: true },
"BackupUI:ShowFilepicker": { wantUntrusted: true },
"BackupUI:GetBackupFileInfo": { wantUntrusted: true },
"BackupUI:RestoreFromBackupFile": { wantUntrusted: true },
"BackupUI:RestoreFromBackupChooseFile": { wantUntrusted: true },
"BackupUI:EnableEncryption": { wantUntrusted: true },
"BackupUI:DisableEncryption": { wantUntrusted: true },
"BackupUI:RerunEncryption": { wantUntrusted: true },
"BackupUI:ShowBackupLocation": { wantUntrusted: true },
"BackupUI:EditBackupLocation": { wantUntrusted: true },
},
},
matches: ["about:preferences*", "about:settings*"],
enablePreference: "browser.backup.preferences.ui.enabled",
},
BlockedSite: {
parent: {
esModuleURI: "resource:///actors/BlockedSiteParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/BlockedSiteChild.sys.mjs",
events: {
AboutBlockedLoaded: { wantUntrusted: true },
click: {},
},
},
matches: ["about:blocked?*"],
allFrames: true,
},
BrowserTab: {
child: {
esModuleURI: "resource:///actors/BrowserTabChild.sys.mjs",
},
messageManagerGroups: ["browsers"],
},
ClickHandler: {
parent: {
esModuleURI: "resource:///actors/ClickHandlerParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/ClickHandlerChild.sys.mjs",
events: {
chromelinkclick: { capture: true, mozSystemGroup: true },
},
},
allFrames: true,
},
/* Note: this uses the same JSMs as ClickHandler, but because it
* relies on "normal" click events anywhere on the page (not just
* links) and is expensive, and only does something for the
* small group of people who have the feature enabled, it is its
* own actor which is only registered if the pref is enabled.
*/
MiddleMousePasteHandler: {
parent: {
esModuleURI: "resource:///actors/ClickHandlerParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/ClickHandlerChild.sys.mjs",
events: {
auxclick: { capture: true, mozSystemGroup: true },
},
},
enablePreference: "middlemouse.contentLoadURL",
allFrames: true,
},
ContentSearch: {
parent: {
esModuleURI: "resource:///actors/ContentSearchParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/ContentSearchChild.sys.mjs",
events: {
ContentSearchClient: { capture: true, wantUntrusted: true },
},
},
matches: [
"about:home",
"about:welcome",
"about:newtab",
"about:privatebrowsing",
"about:test-about-content-search-ui",
],
remoteTypes: ["privilegedabout"],
},
ContextMenu: {
parent: {
esModuleURI: "resource:///actors/ContextMenuParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/ContextMenuChild.sys.mjs",
events: {
contextmenu: { mozSystemGroup: true },
},
},
allFrames: true,
},
DecoderDoctor: {
parent: {
esModuleURI: "resource:///actors/DecoderDoctorParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/DecoderDoctorChild.sys.mjs",
observers: ["decoder-doctor-notification"],
},
messageManagerGroups: ["browsers"],
allFrames: true,
},
DOMFullscreen: {
parent: {
esModuleURI: "resource:///actors/DOMFullscreenParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/DOMFullscreenChild.sys.mjs",
events: {
"MozDOMFullscreen:Request": {},
"MozDOMFullscreen:Entered": {},
"MozDOMFullscreen:NewOrigin": {},
"MozDOMFullscreen:Exit": {},
"MozDOMFullscreen:Exited": {},
},
},
messageManagerGroups: ["browsers"],
allFrames: true,
},
EncryptedMedia: {
parent: {
esModuleURI: "resource:///actors/EncryptedMediaParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/EncryptedMediaChild.sys.mjs",
observers: ["mediakeys-request"],
},
messageManagerGroups: ["browsers"],
allFrames: true,
},
FormValidation: {
parent: {
esModuleURI: "resource:///actors/FormValidationParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/FormValidationChild.sys.mjs",
events: {
MozInvalidForm: {},
// Listening to pageshow event is only relevant if an invalid form
// popup was open, so don't create the actor when fired.
pageshow: { createActor: false },
},
},
allFrames: true,
},
GenAI: {
parent: {
esModuleURI: "resource:///actors/GenAIParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/GenAIChild.sys.mjs",
events: {
mousedown: {},
mouseup: {},
},
},
allFrames: true,
onAddActor(register, unregister) {
let isRegistered = false;
// Register the actor if we have a provider set and not yet registered
const maybeRegister = () => {
if (Services.prefs.getCharPref("browser.ml.chat.provider", "")) {
if (!isRegistered) {
register();
isRegistered = true;
}
} else if (isRegistered) {
unregister();
isRegistered = false;
}
};
Services.prefs.addObserver("browser.ml.chat.provider", maybeRegister);
maybeRegister();
},
},
LightweightTheme: {
child: {
esModuleURI: "resource:///actors/LightweightThemeChild.sys.mjs",
events: {
pageshow: { mozSystemGroup: true },
DOMContentLoaded: {},
},
},
includeChrome: true,
allFrames: true,
matches: [
"about:asrouter",
"about:home",
"about:newtab",
"about:welcome",
"chrome://browser/content/syncedtabs/sidebar.xhtml",
"chrome://browser/content/places/historySidebar.xhtml",
"chrome://browser/content/places/bookmarksSidebar.xhtml",
"about:firefoxview",
"about:editprofile",
"about:deleteprofile",
"about:newprofile",
],
},
LinkHandler: {
parent: {
esModuleURI: "resource:///actors/LinkHandlerParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/LinkHandlerChild.sys.mjs",
events: {
DOMHeadElementParsed: {},
DOMLinkAdded: {},
DOMLinkChanged: {},
pageshow: {},
// The `pagehide` event is only used to clean up state which will not be
// present if the actor hasn't been created.
pagehide: { createActor: false },
},
},
messageManagerGroups: ["browsers"],
},
LinkPreview: {
parent: {
esModuleURI: "resource:///actors/LinkPreviewParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/LinkPreviewChild.sys.mjs",
},
includeChrome: true,
enablePreference: "browser.ml.linkPreview.enabled",
},
PageInfo: {
child: {
esModuleURI: "resource:///actors/PageInfoChild.sys.mjs",
},
allFrames: true,
},
PageStyle: {
parent: {
esModuleURI: "resource:///actors/PageStyleParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/PageStyleChild.sys.mjs",
events: {
pageshow: { createActor: false },
},
},
messageManagerGroups: ["browsers"],
allFrames: true,
},
Pdfjs: {
parent: {
esModuleURI: "resource://pdf.js/PdfjsParent.sys.mjs",
},
child: {
esModuleURI: "resource://pdf.js/PdfjsChild.sys.mjs",
},
allFrames: true,
},
// GMP crash reporting
Plugin: {
parent: {
esModuleURI: "resource:///actors/PluginParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/PluginChild.sys.mjs",
events: {
PluginCrashed: { capture: true },
},
},
allFrames: true,
},
PointerLock: {
parent: {
esModuleURI: "resource:///actors/PointerLockParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/PointerLockChild.sys.mjs",
events: {
"MozDOMPointerLock:Entered": {},
"MozDOMPointerLock:Exited": {},
},
},
messageManagerGroups: ["browsers"],
allFrames: true,
},
Profiles: {
parent: {
esModuleURI: "resource:///actors/ProfilesParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/ProfilesChild.sys.mjs",
events: {
DOMDocElementInserted: { wantUntrusted: true },
},
},
matches: ["about:editprofile", "about:deleteprofile", "about:newprofile"],
remoteTypes: ["privilegedabout"],
onAddActor(register, unregister) {
let registered = false;
const maybeRegister = () => {
let isEnabled = lazy.SelectableProfileService.isEnabled;
if (isEnabled && !registered) {
register();
} else if (!isEnabled && registered) {
unregister();
}
registered = isEnabled;
};
// Defer all this logic until a little later in startup
Services.obs.addObserver(() => {
// Update when the pref changes
lazy.SelectableProfileService.on("enableChanged", maybeRegister);
maybeRegister();
}, "final-ui-startup");
},
},
Prompt: {
parent: {
esModuleURI: "resource:///actors/PromptParent.sys.mjs",
},
includeChrome: true,
allFrames: true,
},
RefreshBlocker: {
parent: {
esModuleURI: "resource:///actors/RefreshBlockerParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/RefreshBlockerChild.sys.mjs",
},
messageManagerGroups: ["browsers"],
enablePreference: "accessibility.blockautorefresh",
},
ReviewChecker: {
parent: {
esModuleURI: "resource:///actors/ReviewCheckerParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/ReviewCheckerChild.sys.mjs",
events: {
ContentReady: { wantUntrusted: true },
PolledRequestMade: { wantUntrusted: true },
// This is added so the actor instantiates immediately and makes
// methods available to the page js on load.
DOMDocElementInserted: {},
ReportProductAvailable: { wantUntrusted: true },
AdClicked: { wantUntrusted: true },
AdImpression: { wantUntrusted: true },
DisableShopping: { wantUntrusted: true },
CloseShoppingSidebar: { wantUntrusted: true },
MoveSidebarToLeft: { wantUntrusted: true },
MoveSidebarToRight: { wantUntrusted: true },
ShowSidebarSettings: { wantUntrusted: true },
},
},
matches: ["about:shoppingsidebar"],
remoteTypes: ["privilegedabout"],
messageManagerGroups: ["review-checker", "browsers"],
enablePreference: "browser.shopping.experience2023.integratedSidebar",
},
ScreenshotsComponent: {
parent: {
esModuleURI: "resource:///modules/ScreenshotsUtils.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/ScreenshotsComponentChild.sys.mjs",
events: {
"Screenshots:Close": {},
"Screenshots:Copy": {},
"Screenshots:Download": {},
"Screenshots:HidePanel": {},
"Screenshots:OverlaySelection": {},
"Screenshots:RecordEvent": {},
"Screenshots:ShowPanel": {},
"Screenshots:FocusPanel": {},
},
},
enablePreference: "screenshots.browser.component.enabled",
},
ScreenshotsHelper: {
parent: {
esModuleURI: "resource:///modules/ScreenshotsUtils.sys.mjs",
},
child: {
esModuleURI: "resource:///modules/ScreenshotsHelperChild.sys.mjs",
},
allFrames: true,
enablePreference: "screenshots.browser.component.enabled",
},
SearchSERPTelemetry: {
parent: {
esModuleURI: "resource:///actors/SearchSERPTelemetryParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/SearchSERPTelemetryChild.sys.mjs",
events: {
DOMContentLoaded: {},
pageshow: { mozSystemGroup: true },
// The 'pagehide' event is only used to clean up state, and should not
// force actor creation.
pagehide: { createActor: false },
load: { mozSystemGroup: true, capture: true },
},
},
matches: ["https://*/*"],
},
ShieldFrame: {
parent: {
esModuleURI: "resource://normandy-content/ShieldFrameParent.sys.mjs",
},
child: {
esModuleURI: "resource://normandy-content/ShieldFrameChild.sys.mjs",
events: {
pageshow: {},
pagehide: {},
ShieldPageEvent: { wantUntrusted: true },
},
},
matches: ["about:studies*"],
},
ShoppingSidebar: {
parent: {
esModuleURI: "resource:///actors/ShoppingSidebarParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/ShoppingSidebarChild.sys.mjs",
events: {
ContentReady: { wantUntrusted: true },
PolledRequestMade: { wantUntrusted: true },
// This is added so the actor instantiates immediately and makes
// methods available to the page js on load.
DOMDocElementInserted: {},
ReportProductAvailable: { wantUntrusted: true },
AdClicked: { wantUntrusted: true },
AdImpression: { wantUntrusted: true },
DisableShopping: { wantUntrusted: true },
},
},
matches: ["about:shoppingsidebar"],
remoteTypes: ["privilegedabout"],
messageManagerGroups: ["shopping-sidebar", "browsers"],
enablePreference: "browser.shopping.experience2023.enabled",
},
SpeechDispatcher: {
parent: {
esModuleURI: "resource:///actors/SpeechDispatcherParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/SpeechDispatcherChild.sys.mjs",
observers: ["chrome-synth-voices-error"],
},
messageManagerGroups: ["browsers"],
allFrames: true,
},
ASRouter: {
parent: {
esModuleURI: "resource:///actors/ASRouterParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/ASRouterChild.sys.mjs",
events: {
// This is added so the actor instantiates immediately and makes
// methods available to the page js on load.
DOMDocElementInserted: {},
},
},
matches: ["about:asrouter*", "about:welcome*", "about:privatebrowsing*"],
remoteTypes: ["privilegedabout"],
},
SwitchDocumentDirection: {
child: {
esModuleURI: "resource:///actors/SwitchDocumentDirectionChild.sys.mjs",
},
allFrames: true,
},
UITour: {
parent: {
esModuleURI: "moz-src:///browser/components/uitour/UITourParent.sys.mjs",
},
child: {
esModuleURI: "moz-src:///browser/components/uitour/UITourChild.sys.mjs",
events: {
mozUITour: { wantUntrusted: true },
},
},
enablePreference: "browser.uitour.enabled",
messageManagerGroups: ["browsers"],
},
WebRTC: {
parent: {
esModuleURI: "resource:///actors/WebRTCParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/WebRTCChild.sys.mjs",
},
allFrames: true,
},
};
export let DesktopActorRegistry = {
init() {
ActorManagerParent.addJSProcessActors(JSPROCESSACTORS);
ActorManagerParent.addJSWindowActors(JSWINDOWACTORS);
},
};

View file

@ -0,0 +1,891 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
FormAutofillUtils: "resource://gre/modules/shared/FormAutofillUtils.sys.mjs",
FirefoxBridgeExtensionUtils:
"resource:///modules/FirefoxBridgeExtensionUtils.sys.mjs",
LoginHelper: "resource://gre/modules/LoginHelper.sys.mjs",
PlacesUIUtils: "resource:///modules/PlacesUIUtils.sys.mjs",
UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
UsageReporting: "resource://gre/modules/UsageReporting.sys.mjs",
});
export let ProfileDataUpgrader = {
_migrateXULStoreForDocument(fromURL, toURL) {
Array.from(Services.xulStore.getIDsEnumerator(fromURL)).forEach(id => {
Array.from(Services.xulStore.getAttributeEnumerator(fromURL, id)).forEach(
attr => {
let value = Services.xulStore.getValue(fromURL, id, attr);
Services.xulStore.setValue(toURL, id, attr, value);
}
);
});
},
_migrateHashedKeysForXULStoreForDocument(docUrl) {
Array.from(Services.xulStore.getIDsEnumerator(docUrl))
.filter(id => id.startsWith("place:"))
.forEach(id => {
Services.xulStore.removeValue(docUrl, id, "open");
let hashedId = lazy.PlacesUIUtils.obfuscateUrlForXulStore(id);
Services.xulStore.setValue(docUrl, hashedId, "open", "true");
});
},
/**
* This method transforms data in the profile directory so that it can be
* used in the current version of Firefox. It is organized similar to
* typical database version upgrades: we are invoked with the version of the
* profile data on disk (`existingDataVersion`) and the version we expect/need
* (`newVersion`), and execute any necessary migrations.
*
* In practice, most of the migrations move user choices from one preference
* to another, or ensure that other mechanical file moves (e.g. of document
* URLs like browser.xhtml).
*
* If you're adding a new migration, you will need to increment
* APP_DATA_VERSION in BrowserGlue.sys.mjs' _migrateUI. That version is not
* in this module so that we can avoid loading this module entirely in common
* cases (Firefox startups where a profile is not upgraded).
*
* Note that this is invoked very early on startup and should try to avoid
* doing very expensive things immediately unless absolutely necessary. Some
* of the migrations will therefore set a pref or otherwise flag that their
* component needs to do more work later, perhaps during idle tasks or
* similar, to avoid front-loading the component initialization into this
* early part of startup. Of course, some of these migrations (e.g. to ensure
* that browser windows remember their sizes if the URL to browser.xhtml has
* changed) _need_ to run very early, and that is OK.
*
* @param {integer} existingDataVersion
* The version of the data in the profile.
* @param {integer} newVersion
* The version that the application expects/needs.
*/
// eslint-disable-next-line complexity
upgrade(existingDataVersion, newVersion) {
const BROWSER_DOCURL = AppConstants.BROWSER_CHROME_URL;
let xulStore = Services.xulStore;
if (existingDataVersion < 90) {
this._migrateXULStoreForDocument(
"chrome://browser/content/places/historySidebar.xul",
"chrome://browser/content/places/historySidebar.xhtml"
);
this._migrateXULStoreForDocument(
"chrome://browser/content/places/places.xul",
"chrome://browser/content/places/places.xhtml"
);
this._migrateXULStoreForDocument(
"chrome://browser/content/places/bookmarksSidebar.xul",
"chrome://browser/content/places/bookmarksSidebar.xhtml"
);
}
// Clear socks proxy values if they were shared from http, to prevent
// websocket breakage after bug 1577862 (see bug 969282).
if (
existingDataVersion < 91 &&
Services.prefs.getBoolPref("network.proxy.share_proxy_settings", false) &&
Services.prefs.getIntPref("network.proxy.type", 0) == 1
) {
let httpProxy = Services.prefs.getCharPref("network.proxy.http", "");
let httpPort = Services.prefs.getIntPref("network.proxy.http_port", 0);
let socksProxy = Services.prefs.getCharPref("network.proxy.socks", "");
let socksPort = Services.prefs.getIntPref("network.proxy.socks_port", 0);
if (httpProxy && httpProxy == socksProxy && httpPort == socksPort) {
Services.prefs.setCharPref(
"network.proxy.socks",
Services.prefs.getCharPref("network.proxy.backup.socks", "")
);
Services.prefs.setIntPref(
"network.proxy.socks_port",
Services.prefs.getIntPref("network.proxy.backup.socks_port", 0)
);
}
}
if (existingDataVersion < 92) {
// privacy.userContext.longPressBehavior pref was renamed and changed to a boolean
let longpress = Services.prefs.getIntPref(
"privacy.userContext.longPressBehavior",
0
);
if (longpress == 1) {
Services.prefs.setBoolPref(
"privacy.userContext.newTabContainerOnLeftClick.enabled",
true
);
}
}
if (existingDataVersion < 93) {
// The Gecko Profiler Addon is now an internal component. Remove the old
// addon, and enable the new UI.
function enableProfilerButton(wasAddonActive) {
// Enable the feature pref. This will add it to the customization palette,
// but not to the the navbar.
Services.prefs.setBoolPref(
"devtools.performance.popup.feature-flag",
true
);
if (wasAddonActive) {
const { ProfilerMenuButton } = ChromeUtils.importESModule(
"resource://devtools/client/performance-new/popup/menu-button.sys.mjs"
);
if (!ProfilerMenuButton.isInNavbar()) {
ProfilerMenuButton.addToNavbar();
}
}
}
let addonPromise;
try {
addonPromise = lazy.AddonManager.getAddonByID(
"geckoprofiler@mozilla.com"
);
} catch (error) {
console.error(
"Could not access the AddonManager to upgrade the profile. This is most " +
"likely because the upgrader is being run from an xpcshell test where " +
"the AddonManager is not initialized."
);
}
Promise.resolve(addonPromise).then(addon => {
if (!addon) {
// Either the addon wasn't installed, or the call to getAddonByID failed.
return;
}
// Remove the old addon.
const wasAddonActive = addon.isActive;
addon
.uninstall()
.catch(console.error)
.then(() => enableProfilerButton(wasAddonActive))
.catch(console.error);
}, console.error);
}
// Clear unused socks proxy backup values - see bug 1625773.
if (existingDataVersion < 94) {
let backup = Services.prefs.getCharPref("network.proxy.backup.socks", "");
let backupPort = Services.prefs.getIntPref(
"network.proxy.backup.socks_port",
0
);
let socksProxy = Services.prefs.getCharPref("network.proxy.socks", "");
let socksPort = Services.prefs.getIntPref("network.proxy.socks_port", 0);
if (backup == socksProxy) {
Services.prefs.clearUserPref("network.proxy.backup.socks");
}
if (backupPort == socksPort) {
Services.prefs.clearUserPref("network.proxy.backup.socks_port");
}
}
if (existingDataVersion < 95) {
const oldPrefName = "media.autoplay.enabled.user-gestures-needed";
const oldPrefValue = Services.prefs.getBoolPref(oldPrefName, true);
const newPrefValue = oldPrefValue ? 0 : 1;
Services.prefs.setIntPref("media.autoplay.blocking_policy", newPrefValue);
Services.prefs.clearUserPref(oldPrefName);
}
if (existingDataVersion < 96) {
const oldPrefName = "browser.urlbar.openViewOnFocus";
const oldPrefValue = Services.prefs.getBoolPref(oldPrefName, true);
Services.prefs.setBoolPref(
"browser.urlbar.suggest.topsites",
oldPrefValue
);
Services.prefs.clearUserPref(oldPrefName);
}
if (existingDataVersion < 97) {
let userCustomizedWheelMax = Services.prefs.prefHasUserValue(
"general.smoothScroll.mouseWheel.durationMaxMS"
);
let userCustomizedWheelMin = Services.prefs.prefHasUserValue(
"general.smoothScroll.mouseWheel.durationMinMS"
);
if (!userCustomizedWheelMin && !userCustomizedWheelMax) {
// If the user has an existing profile but hasn't customized the wheel
// animation duration, they will now get the new default values. This
// condition used to set a migrationPercent pref to 0, so that users
// upgrading an older profile would gradually have their wheel animation
// speed migrated to the new values. However, that "gradual migration"
// was phased out by FF 86, so we don't need to set that pref anymore.
} else if (userCustomizedWheelMin && !userCustomizedWheelMax) {
// If they customized just one of the two, save the old value for the
// other one as well, because the two values go hand-in-hand and we
// don't want to move just one to a new value and leave the other one
// at a customized value. In both of these cases, we leave the "migration
// complete" percentage at 100, because they have customized this and
// don't need any further migration.
Services.prefs.setIntPref(
"general.smoothScroll.mouseWheel.durationMaxMS",
400
);
} else if (!userCustomizedWheelMin && userCustomizedWheelMax) {
// Same as above case, but for the other pref.
Services.prefs.setIntPref(
"general.smoothScroll.mouseWheel.durationMinMS",
200
);
} else {
// The last remaining case is if they customized both values, in which
// case also don't need to do anything; the user's customized values
// will be retained and respected.
}
}
if (existingDataVersion < 98) {
Services.prefs.clearUserPref("browser.search.cohort");
}
if (existingDataVersion < 99) {
Services.prefs.clearUserPref("security.tls.version.enable-deprecated");
}
if (existingDataVersion < 102) {
// In Firefox 83, we moved to a dynamic button, so it needs to be removed
// from default placement. This is done early enough that it doesn't
// impact adding new managed bookmarks.
const { CustomizableUI } = ChromeUtils.importESModule(
"resource:///modules/CustomizableUI.sys.mjs"
);
CustomizableUI.removeWidgetFromArea("managed-bookmarks");
}
// We have to rerun these because we had to use 102 on beta.
// They were 101 and 102 before.
if (existingDataVersion < 103) {
// Set a pref if the bookmarks toolbar was already visible,
// so we can keep it visible when navigating away from newtab
let bookmarksToolbarWasVisible =
Services.xulStore.getValue(
BROWSER_DOCURL,
"PersonalToolbar",
"collapsed"
) == "false";
if (bookmarksToolbarWasVisible) {
// Migrate the user to the "always visible" value. See firefox.js for
// the other possible states.
Services.prefs.setCharPref(
"browser.toolbars.bookmarks.visibility",
"always"
);
}
Services.xulStore.removeValue(
BROWSER_DOCURL,
"PersonalToolbar",
"collapsed"
);
Services.prefs.clearUserPref(
"browser.livebookmarks.migrationAttemptsLeft"
);
}
// For existing profiles, continue putting bookmarks in the
// "other bookmarks" folder.
if (existingDataVersion < 104) {
Services.prefs.setCharPref(
"browser.bookmarks.defaultLocation",
"unfiled"
);
}
// Renamed and flipped the logic of a pref to make its purpose more clear.
if (existingDataVersion < 105) {
const oldPrefName = "browser.urlbar.imeCompositionClosesPanel";
const oldPrefValue = Services.prefs.getBoolPref(oldPrefName, true);
Services.prefs.setBoolPref(
"browser.urlbar.keepPanelOpenDuringImeComposition",
!oldPrefValue
);
Services.prefs.clearUserPref(oldPrefName);
}
// Initialize the new browser.urlbar.showSuggestionsBeforeGeneral pref.
if (existingDataVersion < 106) {
lazy.UrlbarPrefs.initializeShowSearchSuggestionsFirstPref();
}
if (existingDataVersion < 107) {
// Migrate old http URIs for mailto handlers to their https equivalents.
// The handler service will do this. We need to wait with migrating
// until the handler service has started up, so just set a pref here.
const kPref = "browser.handlers.migrations";
// We might have set up another migration further up. Create an array,
// and drop empty strings resulting from the `split`:
let migrations = Services.prefs
.getCharPref(kPref, "")
.split(",")
.filter(x => !!x);
migrations.push("secure-mail");
Services.prefs.setCharPref(kPref, migrations.join(","));
}
if (existingDataVersion < 108) {
// Migrate old ctrlTab pref to new ctrlTab pref
let defaultValue = false;
let oldPrefName = "browser.ctrlTab.recentlyUsedOrder";
let oldPrefDefault = true;
// Use old pref value if the user used Ctrl+Tab before, elsewise use new default value
if (Services.prefs.getBoolPref("browser.engagement.ctrlTab.has-used")) {
let newPrefValue = Services.prefs.getBoolPref(
oldPrefName,
oldPrefDefault
);
Services.prefs.setBoolPref(
"browser.ctrlTab.sortByRecentlyUsed",
newPrefValue
);
} else {
Services.prefs.setBoolPref(
"browser.ctrlTab.sortByRecentlyUsed",
defaultValue
);
}
}
if (existingDataVersion < 109) {
// Migrate old pref to new pref
if (
Services.prefs.prefHasUserValue("signon.recipes.remoteRecipesEnabled")
) {
// Fetch the previous value of signon.recipes.remoteRecipesEnabled and assign it to signon.recipes.remoteRecipes.enabled.
Services.prefs.setBoolPref(
"signon.recipes.remoteRecipes.enabled",
Services.prefs.getBoolPref(
"signon.recipes.remoteRecipesEnabled",
true
)
);
//Then clear user pref
Services.prefs.clearUserPref("signon.recipes.remoteRecipesEnabled");
}
}
if (existingDataVersion < 120) {
// Migrate old titlebar bool pref to new int-based one.
const oldPref = "browser.tabs.drawInTitlebar";
const newPref = "browser.tabs.inTitlebar";
if (Services.prefs.prefHasUserValue(oldPref)) {
// We may have int prefs for builds between bug 1736518 and bug 1739539.
const oldPrefType = Services.prefs.getPrefType(oldPref);
if (oldPrefType == Services.prefs.PREF_BOOL) {
Services.prefs.setIntPref(
newPref,
Services.prefs.getBoolPref(oldPref) ? 1 : 0
);
} else {
Services.prefs.setIntPref(
newPref,
Services.prefs.getIntPref(oldPref)
);
}
Services.prefs.clearUserPref(oldPref);
}
}
if (existingDataVersion < 121) {
// Migrate stored uris and convert them to use hashed keys
this._migrateHashedKeysForXULStoreForDocument(BROWSER_DOCURL);
this._migrateHashedKeysForXULStoreForDocument(
"chrome://browser/content/places/bookmarksSidebar.xhtml"
);
this._migrateHashedKeysForXULStoreForDocument(
"chrome://browser/content/places/historySidebar.xhtml"
);
}
if (existingDataVersion < 122) {
// Migrate xdg-desktop-portal pref from old to new prefs.
try {
const oldPref = "widget.use-xdg-desktop-portal";
if (Services.prefs.getBoolPref(oldPref)) {
Services.prefs.setIntPref(
"widget.use-xdg-desktop-portal.file-picker",
1
);
Services.prefs.setIntPref(
"widget.use-xdg-desktop-portal.mime-handler",
1
);
}
Services.prefs.clearUserPref(oldPref);
} catch (ex) {}
}
// Bug 1745248: Due to multiple backouts, do not use UI Version 123
// as this version is most likely set for the Nightly channel
if (existingDataVersion < 124) {
// Migrate "extensions.formautofill.available" and
// "extensions.formautofill.creditCards.available" from old to new prefs
const oldFormAutofillModule = "extensions.formautofill.available";
const oldCreditCardsAvailable =
"extensions.formautofill.creditCards.available";
const newCreditCardsAvailable =
"extensions.formautofill.creditCards.supported";
const newAddressesAvailable =
"extensions.formautofill.addresses.supported";
if (Services.prefs.prefHasUserValue(oldFormAutofillModule)) {
let moduleAvailability = Services.prefs.getCharPref(
oldFormAutofillModule
);
if (moduleAvailability == "on") {
Services.prefs.setCharPref(newAddressesAvailable, moduleAvailability);
Services.prefs.setCharPref(
newCreditCardsAvailable,
Services.prefs.getBoolPref(oldCreditCardsAvailable) ? "on" : "off"
);
}
if (moduleAvailability == "off") {
Services.prefs.setCharPref(
newCreditCardsAvailable,
moduleAvailability
);
Services.prefs.setCharPref(newAddressesAvailable, moduleAvailability);
}
}
// after migrating, clear old prefs so we can remove them later.
Services.prefs.clearUserPref(oldFormAutofillModule);
Services.prefs.clearUserPref(oldCreditCardsAvailable);
}
if (existingDataVersion < 125) {
// Bug 1756243 - Clear PiP cached coordinates since we changed their
// coordinate space.
const PIP_PLAYER_URI =
"chrome://global/content/pictureinpicture/player.xhtml";
try {
for (let value of ["left", "top", "width", "height"]) {
Services.xulStore.removeValue(
PIP_PLAYER_URI,
"picture-in-picture",
value
);
}
} catch (ex) {
console.error("Failed to clear XULStore PiP values: ", ex);
}
}
function migrateXULAttributeToStyle(url, id, attr) {
try {
let value = Services.xulStore.getValue(url, id, attr);
if (value) {
Services.xulStore.setValue(url, id, "style", `${attr}: ${value}px;`);
}
} catch (ex) {
console.error(`Error migrating ${id}'s ${attr} value: `, ex);
}
}
// Bug 1792748 used version 129 with a buggy variant of the sidebar width
// migration. This version is already in use in the nightly channel, so it
// shouldn't be used.
// Bug 1793366: migrate sidebar persisted attribute from width to style.
if (existingDataVersion < 130) {
migrateXULAttributeToStyle(BROWSER_DOCURL, "sidebar-box", "width");
}
// Migration 131 was moved to 133 to allow for an uplift.
if (existingDataVersion < 132) {
// These attributes are no longer persisted, thus remove them from xulstore.
for (let url of [
"chrome://browser/content/places/bookmarkProperties.xhtml",
"chrome://browser/content/places/bookmarkProperties2.xhtml",
]) {
for (let attr of ["width", "screenX", "screenY"]) {
xulStore.removeValue(url, "bookmarkproperties", attr);
}
}
}
if (existingDataVersion < 133) {
xulStore.removeValue(BROWSER_DOCURL, "urlbar-container", "width");
}
// Migration 134 was removed because it was no longer necessary.
if (existingDataVersion < 135 && AppConstants.platform == "linux") {
// Avoid changing titlebar setting for users that used to had it off.
try {
if (!Services.prefs.prefHasUserValue("browser.tabs.inTitlebar")) {
let de = Services.appinfo.desktopEnvironment;
let oldDefault = de.includes("gnome") || de.includes("pantheon");
if (!oldDefault) {
Services.prefs.setIntPref("browser.tabs.inTitlebar", 0);
}
}
} catch (e) {
console.error("Error migrating tabsInTitlebar setting", e);
}
}
if (existingDataVersion < 136) {
migrateXULAttributeToStyle(
"chrome://browser/content/places/places.xhtml",
"placesList",
"width"
);
}
if (existingDataVersion < 137) {
// The default value for enabling smooth scrolls is now false if the
// user prefers reduced motion. If the value was previously set, do
// not reset it, but if it was not explicitly set preserve the old
// default value.
if (
!Services.prefs.prefHasUserValue("general.smoothScroll") &&
Services.appinfo.prefersReducedMotion
) {
Services.prefs.setBoolPref("general.smoothScroll", true);
}
}
if (existingDataVersion < 138) {
// Bug 1757297: Change scheme of all existing 'https-only-load-insecure'
// permissions with https scheme to http scheme.
try {
Services.perms
.getAllByTypes(["https-only-load-insecure"])
.filter(permission => permission.principal.schemeIs("https"))
.forEach(permission => {
const capability = permission.capability;
const uri = permission.principal.URI.mutate()
.setScheme("http")
.finalize();
const principal =
Services.scriptSecurityManager.createContentPrincipal(uri, {});
Services.perms.removePermission(permission);
Services.perms.addFromPrincipal(
principal,
"https-only-load-insecure",
capability
);
});
} catch (e) {
console.error("Error migrating https-only-load-insecure permission", e);
}
}
if (existingDataVersion < 139) {
// Reset the default permissions to ALLOW_ACTION to rollback issues for
// affected users, see Bug 1579517
// originInfo in the format [origin, type]
[
["https://www.mozilla.org", "uitour"],
["https://support.mozilla.org", "uitour"],
["about:home", "uitour"],
["about:newtab", "uitour"],
["https://addons.mozilla.org", "install"],
["https://support.mozilla.org", "remote-troubleshooting"],
["about:welcome", "autoplay-media"],
].forEach(originInfo => {
// Reset permission on the condition that it is set to
// UNKNOWN_ACTION, we want to prevent resetting user
// manipulated permissions
if (
Services.perms.UNKNOWN_ACTION ==
Services.perms.testPermissionFromPrincipal(
Services.scriptSecurityManager.createContentPrincipalFromOrigin(
originInfo[0]
),
originInfo[1]
)
) {
// Adding permissions which have default values does not create
// new permissions, but rather remove the UNKNOWN_ACTION permission
// overrides. User's not affected by Bug 1579517 will not be affected by this addition.
Services.perms.addFromPrincipal(
Services.scriptSecurityManager.createContentPrincipalFromOrigin(
originInfo[0]
),
originInfo[1],
Services.perms.ALLOW_ACTION
);
}
});
}
if (existingDataVersion < 140) {
// Remove browser.fixup.alternate.enabled pref in Bug 1850902.
Services.prefs.clearUserPref("browser.fixup.alternate.enabled");
}
if (existingDataVersion < 141) {
for (const filename of ["signons.sqlite", "signons.sqlite.corrupt"]) {
const filePath = PathUtils.join(PathUtils.profileDir, filename);
IOUtils.remove(filePath, { ignoreAbsent: true }).catch(console.error);
}
}
if (existingDataVersion < 142) {
// Bug 1860392 - Remove incorrectly persisted theming values from sidebar style.
try {
let value = xulStore.getValue(BROWSER_DOCURL, "sidebar-box", "style");
if (value) {
// Remove custom properties.
value = value
.split(";")
.filter(v => !v.trim().startsWith("--"))
.join(";");
xulStore.setValue(BROWSER_DOCURL, "sidebar-box", "style", value);
}
} catch (ex) {
console.error(ex);
}
}
if (existingDataVersion < 143) {
// Version 143 has been superseded by version 145 below.
}
if (existingDataVersion < 144) {
// TerminatorTelemetry was removed in bug 1879136. Before it was removed,
// the ShutdownDuration.json file would be written to disk at shutdown
// so that the next launch of the browser could read it in and send
// shutdown performance measurements.
//
// Unfortunately, this mechanism and its measurements were fairly
// unreliable, so they were removed.
for (const filename of [
"ShutdownDuration.json",
"ShutdownDuration.json.tmp",
]) {
const filePath = PathUtils.join(PathUtils.profileDir, filename);
IOUtils.remove(filePath, { ignoreAbsent: true }).catch(console.error);
}
}
if (existingDataVersion < 145) {
if (AppConstants.platform == "win") {
// In Firefox 122, we enabled the firefox and firefox-private protocols.
// We switched over to using firefox-bridge and firefox-private-bridge,
// but we want to clean up the use of the other protocols.
lazy.FirefoxBridgeExtensionUtils.maybeDeleteBridgeProtocolRegistryEntries(
lazy.FirefoxBridgeExtensionUtils.OLD_PUBLIC_PROTOCOL,
lazy.FirefoxBridgeExtensionUtils.OLD_PRIVATE_PROTOCOL
);
// Clean up the old user prefs from FX 122
Services.prefs.clearUserPref(
"network.protocol-handler.external.firefox"
);
Services.prefs.clearUserPref(
"network.protocol-handler.external.firefox-private"
);
// In Firefox 126, we switched over to using native messaging so the
// protocols are no longer necessary even in firefox-bridge and
// firefox-private-bridge form
lazy.FirefoxBridgeExtensionUtils.maybeDeleteBridgeProtocolRegistryEntries(
lazy.FirefoxBridgeExtensionUtils.PUBLIC_PROTOCOL,
lazy.FirefoxBridgeExtensionUtils.PRIVATE_PROTOCOL
);
Services.prefs.clearUserPref(
"network.protocol-handler.external.firefox-bridge"
);
Services.prefs.clearUserPref(
"network.protocol-handler.external.firefox-private-bridge"
);
Services.prefs.clearUserPref("browser.shell.customProtocolsRegistered");
}
}
// Version 146 had a typo issue and thus it has been replaced by 147.
if (existingDataVersion < 147) {
// We're securing the boolean prefs for OS Authentication.
// This is achieved by converting them into a string pref and encrypting the values
// stored inside it.
// Note: we don't run this on nightly builds and we also do not run this
// for users with primary password enabled. That means both these sets of
// users will have the features turned on by default. For Nightly this is
// an intentional product decision; for primary password this is because
// we cannot encrypt the opt-out value without asking for the primary
// password, which in turn means we cannot migrate without doing so. It
// is also very difficult to postpone this migration because there is no
// way to know when the user has put in the primary password. We will
// probably reconsider some of this architecture in future, but for now
// this is the least-painful method considering the alternatives, cf.
// bug 1901899.
if (
!AppConstants.NIGHTLY_BUILD &&
!lazy.LoginHelper.isPrimaryPasswordSet()
) {
const hasRunBetaMigration = Services.prefs
.getCharPref("browser.startup.homepage_override.mstone", "")
.startsWith("127.0");
// Version 146 UI migration wrote to a wrong `creditcards` pref when
// the feature was disabled, instead it should have used `creditCards`.
// The correct pref name is in AUTOFILL_CREDITCARDS_REAUTH_PREF.
// Note that we only wrote prefs if the feature was disabled.
let ccTypoDisabled = !lazy.FormAutofillUtils.getOSAuthEnabled(
"extensions.formautofill.creditcards.reauth.optout"
);
let ccCorrectPrefDisabled = !lazy.FormAutofillUtils.getOSAuthEnabled(
lazy.FormAutofillUtils.AUTOFILL_CREDITCARDS_REAUTH_PREF
);
let ccPrevReauthPrefValue = Services.prefs.getBoolPref(
"extensions.formautofill.reauth.enabled",
false
);
let userHadEnabledCreditCardReauth =
// If we've run beta migration, and neither typo nor correct pref
// indicate disablement, the user enabled the pref:
(hasRunBetaMigration && !ccTypoDisabled && !ccCorrectPrefDisabled) ||
// Or if we never ran beta migration and the bool pref is set:
ccPrevReauthPrefValue;
lazy.FormAutofillUtils.setOSAuthEnabled(
lazy.FormAutofillUtils.AUTOFILL_CREDITCARDS_REAUTH_PREF,
userHadEnabledCreditCardReauth
);
if (!hasRunBetaMigration) {
const passwordsPrevReauthPrefValue = Services.prefs.getBoolPref(
"signon.management.page.os-auth.enabled",
false
);
lazy.LoginHelper.setOSAuthEnabled(
lazy.LoginHelper.OS_AUTH_FOR_PASSWORDS_PREF,
passwordsPrevReauthPrefValue
);
}
}
Services.prefs.clearUserPref("extensions.formautofill.reauth.enabled");
Services.prefs.clearUserPref("signon.management.page.os-auth.enabled");
Services.prefs.clearUserPref(
"extensions.formautofill.creditcards.reauth.optout"
);
}
if (existingDataVersion < 148) {
// The Firefox Translations addon is now a built-in Firefox feature.
let addonPromise;
try {
addonPromise = lazy.AddonManager.getAddonByID(
"firefox-translations-addon@mozilla.org"
);
} catch (error) {
// This always throws in xpcshell as the AddonManager is not initialized.
if (!Services.env.exists("XPCSHELL_TEST_PROFILE_DIR")) {
console.error(
"Could not access the AddonManager to upgrade the profile."
);
}
}
addonPromise?.then(addon => addon?.uninstall()).catch(console.error);
}
if (existingDataVersion < 149) {
// remove permissions used by deleted nsContentManager
[
"other",
"script",
"image",
"stylesheet",
"object",
"document",
"subdocument",
"refresh",
"xbl",
"ping",
"xmlhttprequest",
"objectsubrequest",
"dtd",
"font",
"websocket",
"csp_report",
"xslt",
"beacon",
"fetch",
"manifest",
"speculative",
].forEach(type => {
Services.perms.removeByType(type);
});
}
if (existingDataVersion < 150) {
Services.prefs.clearUserPref("toolkit.telemetry.pioneerId");
}
if (existingDataVersion < 151) {
// Existing Firefox users should have the usage reporting upload
// preference "inherit" the general data reporting preference.
lazy.UsageReporting.adoptDataReportingPreference();
}
if (
existingDataVersion < 152 &&
Services.prefs.getBoolPref("sidebar.revamp") &&
!Services.prefs.getBoolPref("browser.ml.chat.enabled")
) {
let tools = Services.prefs.getCharPref("sidebar.main.tools");
if (tools?.includes("aichat")) {
let updatedTools = tools
.split(",")
.filter(t => t != "aichat")
.join(",");
Services.prefs.setCharPref("sidebar.main.tools", updatedTools);
}
}
if (
existingDataVersion < 153 &&
Services.prefs.getBoolPref("sidebar.revamp") &&
!Services.prefs.prefHasUserValue("sidebar.main.tools")
) {
// This pref will now be a user set branch but we want to preserve the previous
// default value for existing sidebar.revamp users who hadn't changed it.
Services.prefs.setCharPref(
"sidebar.main.tools",
"aichat,syncedtabs,history"
);
}
if (existingDataVersion < 154) {
// Remove mibbit handler.
// The handler service will do this. We need to wait with migrating
// until the handler service has started up, so just set a pref here.
const kPref = "browser.handlers.migrations";
// We might have set up another migration further up. Create an array,
// and drop empty strings resulting from the `split`:
let migrations = Services.prefs
.getCharPref(kPref, "")
.split(",")
.filter(x => !!x);
migrations.push("mibbit");
Services.prefs.setCharPref(kPref, migrations.join(","));
}
// Update the migration version.
Services.prefs.setIntPref("browser.migration.version", newVersion);
},
};

View file

@ -0,0 +1,495 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
let lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
BrowserInitState: "resource:///modules/BrowserGlue.sys.mjs",
BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.sys.mjs",
FormAutofillUtils: "resource://gre/modules/shared/FormAutofillUtils.sys.mjs",
LoginHelper: "resource://gre/modules/LoginHelper.sys.mjs",
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
OsEnvironment: "resource://gre/modules/OsEnvironment.sys.mjs",
PlacesDBUtils: "resource://gre/modules/PlacesDBUtils.sys.mjs",
ShellService: "resource:///modules/ShellService.sys.mjs",
TelemetryReportingPolicy:
"resource://gre/modules/TelemetryReportingPolicy.sys.mjs",
UsageReporting: "resource://gre/modules/UsageReporting.sys.mjs",
});
/**
* Used to collect various bits of telemetry during browser startup.
*
*/
export let StartupTelemetry = {
// Some tasks are expensive because they involve significant disk IO, and
// may also write information to disk. If we submit the telemetry that may
// happen anyway, but if we don't then this is undesirable, so those tasks are
// only run if we will submit the results.
// Why run any telemetry code at all if we don't submit the data? Because
// local and autoland builds usually do not submit telemetry, but we still
// want to be able to run automated tests to check the code _worked_.
get _willUseExpensiveTelemetry() {
return (
AppConstants.MOZ_TELEMETRY_REPORTING &&
Services.prefs.getBoolPref(
"datareporting.healthreport.uploadEnabled",
false
)
);
},
_runIdleTasks(tasks, profilerMarker) {
for (let task of tasks) {
ChromeUtils.idleDispatch(async () => {
if (!Services.startup.shuttingDown) {
let startTime = Cu.now();
try {
await task();
} catch (ex) {
console.error(ex);
} finally {
ChromeUtils.addProfilerMarker(
profilerMarker,
startTime,
task.toSource()
);
}
}
});
}
},
browserIdleStartup() {
let tasks = [
// FOG doesn't need to be initialized _too_ early because it has a pre-init buffer.
() => this.initFOG(),
() => this.contentBlocking(),
() => this.dataSanitization(),
() => this.pipEnabled(),
() => this.sslKeylogFile(),
() => this.osAuthEnabled(),
() => this.startupConditions(),
() => this.httpsOnlyState(),
() => this.globalPrivacyControl(),
];
if (this._willUseExpensiveTelemetry) {
tasks.push(() => lazy.PlacesDBUtils.telemetry());
}
if (AppConstants.platform == "win") {
tasks.push(
() => this.pinningStatus(),
() => this.isDefaultHandler()
);
} else if (AppConstants.platform == "macosx") {
tasks.push(() => this.macDockStatus());
}
this._runIdleTasks(tasks, "startupTelemetryIdleTask");
},
/**
* Use this function as an entry point to collect telemetry that we hope
* to collect once per session, at any arbitrary point in time, and
*
* **which we are okay with sometimes not running at all.**
*
* See BrowserGlue.sys.mjs's _scheduleBestEffortUserIdleTasks for more
* details.
*/
bestEffortIdleStartup() {
let tasks = [
() => this.primaryPasswordEnabled(),
() => this.trustObjectCount(),
() => lazy.OsEnvironment.reportAllowedAppSources(),
];
if (AppConstants.platform == "win" && this._willUseExpensiveTelemetry) {
tasks.push(
() => lazy.BrowserUsageTelemetry.reportProfileCount(),
() => lazy.BrowserUsageTelemetry.reportInstallationTelemetry()
);
}
this._runIdleTasks(tasks, "startupTelemetryLateIdleTask");
},
/**
* Initialize Firefox-on-Glean.
*
* This is at the top because it's a bit different from the other code here
* which is strictly collecting specific metrics.
*/
async initFOG() {
// Handle Usage Profile ID. Similar logic to what's happening in
// `TelemetryControllerParent` for the client ID. Must be done before
// initializing FOG so that ping enabled/disabled states are correct
// before Glean takes actions.
await lazy.UsageReporting.ensureInitialized();
// If needed, delay initializing FOG until policy interaction is
// completed. See comments in `TelemetryReportingPolicy`.
await lazy.TelemetryReportingPolicy.ensureUserIsNotified();
Services.fog.initializeFOG();
// Register Glean to listen for experiment updates releated to the
// "gleanInternalSdk" feature defined in the t/c/nimbus/FeatureManifest.yaml
// This feature is intended for internal Glean use only. For features wishing
// to set a remote metric configuration, please use the "glean" feature for
// the purpose of setting the data-control-plane features via Server Knobs.
lazy.NimbusFeatures.gleanInternalSdk.onUpdate(() => {
let cfg = lazy.NimbusFeatures.gleanInternalSdk.getVariable(
"gleanMetricConfiguration"
);
Services.fog.applyServerKnobsConfig(JSON.stringify(cfg));
});
// Register Glean to listen for experiment updates releated to the
// "glean" feature defined in the t/c/nimbus/FeatureManifest.yaml
lazy.NimbusFeatures.glean.onUpdate(() => {
let cfg = lazy.NimbusFeatures.glean.getVariable(
"gleanMetricConfiguration"
);
Services.fog.applyServerKnobsConfig(JSON.stringify(cfg));
});
},
startupConditions() {
let nowSeconds = Math.round(Date.now() / 1000);
// Don't include cases where we don't have the pref. This rules out the first install
// as well as the first run of a build since this was introduced. These could by some
// definitions be referred to as "cold" startups, but probably not since we likely
// just wrote many of the files we use to disk. This way we should approximate a lower
// bound to the number of cold startups rather than an upper bound.
let lastCheckSeconds = Services.prefs.getIntPref(
"browser.startup.lastColdStartupCheck",
nowSeconds
);
Services.prefs.setIntPref(
"browser.startup.lastColdStartupCheck",
nowSeconds
);
try {
let secondsSinceLastOSRestart =
Services.startup.secondsSinceLastOSRestart;
let isColdStartup =
nowSeconds - secondsSinceLastOSRestart > lastCheckSeconds;
Glean.startup.isCold.set(isColdStartup);
Glean.startup.secondsSinceLastOsRestart.set(secondsSinceLastOSRestart);
} catch (ex) {
if (ex.name !== "NS_ERROR_NOT_IMPLEMENTED") {
console.error(ex);
}
}
},
contentBlocking() {
let tpEnabled = Services.prefs.getBoolPref(
"privacy.trackingprotection.enabled"
);
Glean.contentblocking.trackingProtectionEnabled[
tpEnabled ? "true" : "false"
].add();
let tpPBEnabled = Services.prefs.getBoolPref(
"privacy.trackingprotection.pbmode.enabled"
);
Glean.contentblocking.trackingProtectionPbmDisabled[
!tpPBEnabled ? "true" : "false"
].add();
let cookieBehavior = Services.prefs.getIntPref(
"network.cookie.cookieBehavior"
);
Glean.contentblocking.cookieBehavior.accumulateSingleSample(cookieBehavior);
let fpEnabled = Services.prefs.getBoolPref(
"privacy.trackingprotection.fingerprinting.enabled"
);
let cmEnabled = Services.prefs.getBoolPref(
"privacy.trackingprotection.cryptomining.enabled"
);
let categoryPref;
switch (
Services.prefs.getStringPref("browser.contentblocking.category", null)
) {
case "standard":
categoryPref = 0;
break;
case "strict":
categoryPref = 1;
break;
case "custom":
categoryPref = 2;
break;
default:
// Any other value is unsupported.
categoryPref = 3;
break;
}
Glean.contentblocking.fingerprintingBlockingEnabled.set(fpEnabled);
Glean.contentblocking.cryptominingBlockingEnabled.set(cmEnabled);
Glean.contentblocking.category.set(categoryPref);
},
dataSanitization() {
Glean.datasanitization.privacySanitizeSanitizeOnShutdown.set(
Services.prefs.getBoolPref("privacy.sanitize.sanitizeOnShutdown")
);
Glean.datasanitization.privacyClearOnShutdownCookies.set(
Services.prefs.getBoolPref("privacy.clearOnShutdown.cookies")
);
Glean.datasanitization.privacyClearOnShutdownHistory.set(
Services.prefs.getBoolPref("privacy.clearOnShutdown.history")
);
Glean.datasanitization.privacyClearOnShutdownFormdata.set(
Services.prefs.getBoolPref("privacy.clearOnShutdown.formdata")
);
Glean.datasanitization.privacyClearOnShutdownDownloads.set(
Services.prefs.getBoolPref("privacy.clearOnShutdown.downloads")
);
Glean.datasanitization.privacyClearOnShutdownCache.set(
Services.prefs.getBoolPref("privacy.clearOnShutdown.cache")
);
Glean.datasanitization.privacyClearOnShutdownSessions.set(
Services.prefs.getBoolPref("privacy.clearOnShutdown.sessions")
);
Glean.datasanitization.privacyClearOnShutdownOfflineApps.set(
Services.prefs.getBoolPref("privacy.clearOnShutdown.offlineApps")
);
Glean.datasanitization.privacyClearOnShutdownSiteSettings.set(
Services.prefs.getBoolPref("privacy.clearOnShutdown.siteSettings")
);
Glean.datasanitization.privacyClearOnShutdownOpenWindows.set(
Services.prefs.getBoolPref("privacy.clearOnShutdown.openWindows")
);
let exceptions = 0;
for (let permission of Services.perms.all) {
// We consider just permissions set for http, https and file URLs.
if (
permission.type == "cookie" &&
permission.capability == Ci.nsICookiePermission.ACCESS_SESSION &&
["http", "https", "file"].some(scheme =>
permission.principal.schemeIs(scheme)
)
) {
exceptions++;
}
}
Glean.datasanitization.sessionPermissionExceptions.set(exceptions);
},
httpsOnlyState() {
const PREF_ENABLED = "dom.security.https_only_mode";
const PREF_WAS_ENABLED = "dom.security.https_only_mode_ever_enabled";
const _checkHTTPSOnlyPref = async () => {
const enabled = Services.prefs.getBoolPref(PREF_ENABLED, false);
const was_enabled = Services.prefs.getBoolPref(PREF_WAS_ENABLED, false);
let value = 0;
if (enabled) {
value = 1;
Services.prefs.setBoolPref(PREF_WAS_ENABLED, true);
} else if (was_enabled) {
value = 2;
}
Glean.security.httpsOnlyModeEnabled.set(value);
};
Services.prefs.addObserver(PREF_ENABLED, _checkHTTPSOnlyPref);
_checkHTTPSOnlyPref();
const PREF_PBM_WAS_ENABLED =
"dom.security.https_only_mode_ever_enabled_pbm";
const PREF_PBM_ENABLED = "dom.security.https_only_mode_pbm";
const _checkHTTPSOnlyPBMPref = async () => {
const enabledPBM = Services.prefs.getBoolPref(PREF_PBM_ENABLED, false);
const was_enabledPBM = Services.prefs.getBoolPref(
PREF_PBM_WAS_ENABLED,
false
);
let valuePBM = 0;
if (enabledPBM) {
valuePBM = 1;
Services.prefs.setBoolPref(PREF_PBM_WAS_ENABLED, true);
} else if (was_enabledPBM) {
valuePBM = 2;
}
Glean.security.httpsOnlyModeEnabledPbm.set(valuePBM);
};
Services.prefs.addObserver(PREF_PBM_ENABLED, _checkHTTPSOnlyPBMPref);
_checkHTTPSOnlyPBMPref();
},
globalPrivacyControl() {
const FEATURE_PREF_ENABLED = "privacy.globalprivacycontrol.enabled";
const FUNCTIONALITY_PREF_ENABLED =
"privacy.globalprivacycontrol.functionality.enabled";
const PREF_WAS_ENABLED = "privacy.globalprivacycontrol.was_ever_enabled";
const _checkGPCPref = async () => {
const feature_enabled = Services.prefs.getBoolPref(
FEATURE_PREF_ENABLED,
false
);
const functionality_enabled = Services.prefs.getBoolPref(
FUNCTIONALITY_PREF_ENABLED,
false
);
const was_enabled = Services.prefs.getBoolPref(PREF_WAS_ENABLED, false);
let value = 0;
if (feature_enabled && functionality_enabled) {
value = 1;
Services.prefs.setBoolPref(PREF_WAS_ENABLED, true);
} else if (was_enabled) {
value = 2;
}
Glean.security.globalPrivacyControlEnabled.set(value);
};
Services.prefs.addObserver(FEATURE_PREF_ENABLED, _checkGPCPref);
Services.prefs.addObserver(FUNCTIONALITY_PREF_ENABLED, _checkGPCPref);
_checkGPCPref();
},
async pinningStatus() {
let shellService = Cc["@mozilla.org/browser/shell-service;1"].getService(
Ci.nsIWindowsShellService
);
let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"].getService(
Ci.nsIWinTaskbar
);
try {
Glean.osEnvironment.isTaskbarPinned.set(
await shellService.isCurrentAppPinnedToTaskbarAsync(
winTaskbar.defaultGroupId
)
);
// Bug 1911343: Pinning regular browsing on MSIX
// causes false positives when checking for private
// browsing.
if (
AppConstants.platform === "win" &&
!Services.sysinfo.getProperty("hasWinPackageId")
) {
Glean.osEnvironment.isTaskbarPinnedPrivate.set(
await shellService.isCurrentAppPinnedToTaskbarAsync(
winTaskbar.defaultPrivateGroupId
)
);
}
} catch (ex) {
console.error(ex);
}
let classification;
let shortcut;
try {
shortcut = Services.appinfo.processStartupShortcut;
classification = shellService.classifyShortcut(shortcut);
} catch (ex) {
console.error(ex);
}
if (!classification) {
if (lazy.BrowserInitState.isLaunchOnLogin) {
classification = "Autostart";
} else if (shortcut) {
classification = "OtherShortcut";
} else {
classification = "Other";
}
}
// Because of how taskbar tabs work, it may be classifed as a taskbar
// shortcut, in which case we want to overwrite it.
if (lazy.BrowserInitState.isTaskbarTab) {
classification = "TaskbarTab";
}
Glean.osEnvironment.launchMethod.set(classification);
},
isDefaultHandler() {
// Report whether Firefox is the default handler for various files types
// and protocols, in particular, ".pdf" and "mailto"
[".pdf", "mailto"].every(x => {
Glean.osEnvironment.isDefaultHandler[x].set(
lazy.ShellService.isDefaultHandlerFor(x)
);
return true;
});
},
macDockStatus() {
// Report macOS Dock status
Glean.osEnvironment.isKeptInDock.set(
Cc["@mozilla.org/widget/macdocksupport;1"].getService(
Ci.nsIMacDockSupport
).isAppInDock
);
},
sslKeylogFile() {
Glean.sslkeylogging.enabled.set(Services.env.exists("SSLKEYLOGFILE"));
},
osAuthEnabled() {
// Manually read these prefs. This treats any non-empty-string
// value as "turned off", irrespective of whether it correctly
// decrypts to the correct value, because we cannot do the
// decryption if the primary password has not yet been provided,
// and for telemetry treating that situation as "turned off"
// seems reasonable.
const osAuthForCc = !Services.prefs.getStringPref(
lazy.FormAutofillUtils.AUTOFILL_CREDITCARDS_REAUTH_PREF,
""
);
const osAuthForPw = !Services.prefs.getStringPref(
lazy.LoginHelper.OS_AUTH_FOR_PASSWORDS_PREF,
""
);
Glean.formautofill.osAuthEnabled.set(osAuthForCc);
Glean.pwmgr.osAuthEnabled.set(osAuthForPw);
},
primaryPasswordEnabled() {
let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"].getService(
Ci.nsIPK11TokenDB
);
let token = tokenDB.getInternalKeyToken();
Glean.primaryPassword.enabled.set(token.hasPassword);
},
trustObjectCount() {
let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
Ci.nsIX509CertDB
);
// countTrustObjects also logs the number of trust objects for telemetry purposes
certdb.countTrustObjects();
},
pipEnabled() {
const TOGGLE_ENABLED_PREF =
"media.videocontrols.picture-in-picture.video-toggle.enabled";
const observe = (subject, topic) => {
const enabled = Services.prefs.getBoolPref(TOGGLE_ENABLED_PREF, false);
Glean.pictureinpicture.toggleEnabled.set(enabled);
// Record events when preferences change
if (topic === "nsPref:changed") {
if (enabled) {
Glean.pictureinpictureSettings.enableSettings.record();
}
}
};
Services.prefs.addObserver(TOGGLE_ENABLED_PREF, observe);
observe();
},
};

View file

@ -1,5 +1,4 @@
[DEFAULT]
skip-if = ["os == 'android'"] # bug 1730213
head = ''
# make the firefox services (eg newtab-service) available to xpcshell
firefox-appdir = "browser"

View file

@ -1,5 +1,4 @@
[DEFAULT]
skip-if = ["os == 'android'"] # bug 1730213
head = "head.js"
firefox-appdir = "browser"

View file

@ -855,8 +855,6 @@ const SHOPPING_MICROSURVEY = {
};
const OPTED_IN_TIME_PREF = "browser.shopping.experience2023.survey.optedInTime";
const ONBOARDING_FIRST_IMPRESSION_TIME_PREF =
"browser.shopping.experience2023.firstImpressionTime";
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
@ -962,19 +960,6 @@ export class AboutWelcomeShoppingChild extends AboutWelcomeChild {
});
}
setOnBoardingImpressionTime() {
const now = Date.now() / 1000;
this.AWSendToParent("SPECIAL_ACTION", {
type: "SET_PREF",
data: {
pref: {
name: ONBOARDING_FIRST_IMPRESSION_TIME_PREF,
value: now,
},
},
});
}
handleEvent(event) {
// Decide when to show/hide onboarding and survey message
const { productUrl, showOnboarding, data, isSupportedSite, isProductPage } =
@ -1067,8 +1052,6 @@ export class AboutWelcomeShoppingChild extends AboutWelcomeChild {
if (this.showMicroSurvey && !this.showOnboarding) {
messageContent = SHOPPING_MICROSURVEY;
this.setShoppingSurveySeen();
} else {
this.setOnBoardingImpressionTime();
}
return Cu.cloneInto(messageContent, this.contentWindow);
}

View file

@ -1,5 +1,4 @@
[DEFAULT]
skip-if = ["os == 'android'"]
firefox-appdir = "browser"
["test_AboutWelcomeAttribution.js"]

View file

@ -65,6 +65,7 @@ Please note that some targeting attributes require stricter controls on the tele
* [primaryResolution](#primaryresolution)
* [profileAgeCreated](#profileagecreated)
* [profileAgeReset](#profileagereset)
* [profileGroupId](#profilegroupid)
* [profileRestartCount](#profilerestartcount)
* [providerCohorts](#providercohorts)
* [recentBookmarks](#recentbookmarks)
@ -1092,3 +1093,8 @@ declare const systemArch: string | null;
### `totalSearches`
Returns the number of times a user has completed a search in the URL Bar. The number is arbitrarily capped at 100.
### `profileGroupId`
Returns the stable profile group ID used for data reporting.

View file

@ -34,6 +34,11 @@ const { ShellService } = ChromeUtils.importESModule(
"resource:///modules/ShellService.sys.mjs"
);
// eslint-disable-next-line mozilla/use-static-import
const { ClientID } = ChromeUtils.importESModule(
"resource://gre/modules/ClientID.sys.mjs"
);
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@ -388,6 +393,12 @@ export const QueryCache = {
FRECENT_SITES_UPDATE_INTERVAL,
ShellService
),
profileGroupId: new CachedTargetingGetter(
"getCachedProfileGroupID",
null,
FRECENT_SITES_UPDATE_INTERVAL,
ClientID
),
},
};
@ -1184,6 +1195,10 @@ const TargetingGetters = {
get totalSearches() {
return lazy.totalSearches;
},
get profileGroupId() {
return QueryCache.getters.profileGroupId.get();
},
};
export const ASRouterTargeting = {

View file

@ -1618,8 +1618,7 @@ const MESSAGES = () => {
{
type: "action",
label: {
string_id:
"shopping-callout-not-opted-in-integrated-reminder-do-not-show",
string_id: "split-dismiss-button-dont-show-option",
},
action: {
type: "SET_PREF",
@ -1631,13 +1630,12 @@ const MESSAGES = () => {
},
dismiss: true,
},
id: "shopping-callout-not-opted-in-integrated-reminder-do-not-show",
id: "split-dismiss-button-dont-show-option",
},
{
type: "action",
label: {
string_id:
"shopping-callout-not-opted-in-integrated-reminder-show-fewer",
string_id: "split-dismiss-button-show-fewer-option",
},
action: {
type: "MULTI_ACTION",
@ -1666,7 +1664,7 @@ const MESSAGES = () => {
},
dismiss: true,
},
id: "shopping-callout-not-opted-in-integrated-reminder-show-fewer",
id: "split-dismiss-button-show-fewer-option",
},
{
type: "separator",
@ -1674,8 +1672,7 @@ const MESSAGES = () => {
{
type: "action",
label: {
string_id:
"shopping-callout-not-opted-in-integrated-reminder-manage-settings",
string_id: "split-dismiss-button-manage-settings-option",
},
action: {
type: "OPEN_ABOUT_PAGE",
@ -1685,7 +1682,7 @@ const MESSAGES = () => {
},
dismiss: true,
},
id: "shopping-callout-not-opted-in-integrated-reminder-manage-settings",
id: "split-dismiss-button-manage-settings-option",
},
],
attached_to: "additional_button",
@ -1887,8 +1884,7 @@ const MESSAGES = () => {
{
type: "action",
label: {
string_id:
"shopping-callout-not-opted-in-integrated-reminder-do-not-show",
string_id: "split-dismiss-button-dont-show-option",
},
action: {
type: "SET_PREF",
@ -1900,13 +1896,12 @@ const MESSAGES = () => {
},
dismiss: true,
},
id: "shopping-callout-not-opted-in-integrated-reminder-do-not-show",
id: "split-dismiss-button-dont-show-option",
},
{
type: "action",
label: {
string_id:
"shopping-callout-not-opted-in-integrated-reminder-show-fewer",
string_id: "split-dismiss-button-show-fewer-option",
},
action: {
type: "MULTI_ACTION",
@ -1935,7 +1930,7 @@ const MESSAGES = () => {
},
dismiss: true,
},
id: "shopping-callout-not-opted-in-integrated-reminder-show-fewer",
id: "split-dismiss-button-show-fewer-option",
},
{
type: "separator",
@ -1943,8 +1938,7 @@ const MESSAGES = () => {
{
type: "action",
label: {
string_id:
"shopping-callout-not-opted-in-integrated-reminder-manage-settings",
string_id: "split-dismiss-button-manage-settings-option",
},
action: {
type: "OPEN_ABOUT_PAGE",
@ -1954,7 +1948,7 @@ const MESSAGES = () => {
},
dismiss: true,
},
id: "shopping-callout-not-opted-in-integrated-reminder-manage-settings",
id: "split-dismiss-button-manage-settings-option",
},
],
attached_to: "additional_button",

View file

@ -1546,7 +1546,7 @@ const MESSAGES = () => [
id: "TEST_NEWTAB_MESSAGE",
template: "newtab_message",
content: {
messageType: "CustomWallpaperHighlight",
messageType: "DownloadMobilePromoHighlight",
},
trigger: {
id: "newtabMessageCheck",

View file

@ -8,6 +8,7 @@ ChromeUtils.defineESModuleGetters(this, {
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
BuiltInThemes: "resource:///modules/BuiltInThemes.sys.mjs",
CFRMessageProvider: "resource:///modules/asrouter/CFRMessageProvider.sys.mjs",
ClientID: "resource://gre/modules/ClientID.sys.mjs",
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
ExperimentFakes: "resource://testing-common/NimbusTestUtils.sys.mjs",
FxAccounts: "resource://gre/modules/FxAccounts.sys.mjs",
@ -2000,3 +2001,22 @@ add_task(async function check_unhandledCampaignAction() {
}
}
});
add_task(async function check_profileGroupIdTargeting() {
const expected = await ClientID.getCachedProfileGroupID();
const result = await ASRouterTargeting.Environment.profileGroupId;
is(typeof result, "string", "profileGroupId should be a string");
is(result, expected, "it should be equal to the profile group id");
const message = {
id: "foo",
targeting: `profileGroupId == "${expected.toString()}"`,
};
is(
await ASRouterTargeting.findMatchingMessage({ messages: [message] }),
message,
"should select correct item by profile group id"
);
});

View file

@ -5,7 +5,7 @@ const { CustomizableUITestUtils } = ChromeUtils.importESModule(
"resource://testing-common/CustomizableUITestUtils.sys.mjs"
);
const { DefaultBrowserCheck } = ChromeUtils.importESModule(
"resource:///modules/BrowserGlue.sys.mjs"
"moz-src:///browser/components/DefaultBrowserCheck.sys.mjs"
);
const PDF_TEST_URL =

View file

@ -96,10 +96,7 @@ add_task(async function test_messagesLoaded_reach_experiment() {
],
}
);
Assert.ok(
await ExperimentTestUtils.validateExperiment(recipe),
"Valid recipe"
);
await ExperimentTestUtils.validateExperiment(recipe);
await client.db.importChanges({}, Date.now(), [recipe], { clear: true });
await SpecialPowers.pushPrefEnv({

View file

@ -1,7 +1,6 @@
[DEFAULT]
head = "head.js"
firefox-appdir = "browser"
skip-if = ["os == 'android'"]
prefs = [
"browser.backup.log=true",
]

View file

@ -47,12 +47,6 @@ Classes = [
'esModule': 'resource:///modules/BrowserGlue.sys.mjs',
'constructor': 'BrowserGlue',
},
{
'cid': '{d8903bf6-68d5-4e97-bcd1-e4d3012f721a}',
'contract_ids': ['@mozilla.org/content-permission/prompt;1'],
'esModule': 'resource:///modules/BrowserGlue.sys.mjs',
'constructor': 'ContentPermissionPrompt',
},
]
if (buildconfig.substs.get('MOZ_DEBUG') or

View file

@ -3,6 +3,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @ts-check
/**
* Contains elements of the Content Analysis UI, which are integrated into
@ -99,7 +100,7 @@ export const ContentAnalysis = {
/**
* @typedef {object} RequestInfo
* @property {BrowsingContext} browsingContext - browsing context where the request was sent from
* @property {CanonicalBrowsingContext} browsingContext - browsing context where the request was sent from
* @property {ResourceNameOrOperationType} resourceNameOrOperationType - name of the operation
*/
@ -162,6 +163,7 @@ export const ContentAnalysis = {
Services.obs.addObserver(this, "dlp-request-made");
Services.obs.addObserver(this, "dlp-response");
Services.obs.addObserver(this, "quit-application");
Services.obs.addObserver(this, "quit-application-granted");
Services.obs.addObserver(this, "quit-application-requested");
},
@ -204,7 +206,7 @@ export const ContentAnalysis = {
null,
null,
null,
{ value: 0 }
{ value: false }
);
if (buttonSelected === 1) {
// Cancel the quit operation
@ -218,7 +220,10 @@ export const ContentAnalysis = {
}
break;
}
case "quit-application": {
// Note that we do this in quit-application-granted instead of quit-application
// because otherwise we can get a shutdownhang if WARN dialogs are showing and
// the user quits via keyboard or the hamburger menu (bug 1959966)
case "quit-application-granted": {
// We're quitting, so respond false to all WARN dialogs.
let requestTokensToCancel = this.warnDialogRequestTokens;
// Clear this first so the handler showing the dialog will know not
@ -230,6 +235,9 @@ export const ContentAnalysis = {
false
);
}
break;
}
case "quit-application": {
this.uninitialize();
break;
}
@ -310,7 +318,7 @@ export const ContentAnalysis = {
* Shows the panel that indicates that DLP is active.
*
* @param {Element} element The toolbarbutton the user has clicked on
* @param {panelUI} panelUI Maintains state for the main menu panel
* @param {*} panelUI Maintains state for the main menu panel
*/
async showPanel(element, panelUI) {
element.ownerDocument.l10n.setAttributes(
@ -367,7 +375,7 @@ export const ContentAnalysis = {
* _SHOW_DIALOGS and _SHOW_NOTIFICATIONS.
*
* @param {string} aMessage - Message to show
* @param {BrowsingContext} aBrowsingContext - BrowsingContext to show the dialog in.
* @param {CanonicalBrowsingContext} aBrowsingContext - BrowsingContext to show the dialog in.
* @param {number} aTimeout - timeout for closing the native notification. 0 indicates it is
* not automatically closed.
* @returns {NotificationInfo?} - information about the native notification, if it has been shown.
@ -405,7 +413,7 @@ export const ContentAnalysis = {
/**
* Whether the notification should block browser interaction.
*
* @param {number} aAnalysisType The type of DLP analysis being done.
* @param {nsIContentAnalysisRequest.AnalysisType} aAnalysisType The type of DLP analysis being done.
* @returns {boolean}
*/
_shouldShowBlockingNotification(aAnalysisType) {
@ -451,7 +459,7 @@ export const ContentAnalysis = {
/**
* Gets a name or operation type from a request
*
* @param {object} aRequest The nsIContentAnalysisRequest
* @param {nsIContentAnalysisRequest} aRequest The nsIContentAnalysisRequest
* @param {boolean} aStandalone Whether the message is going to be used on its own
* line. This is used to add more context to the message
* if a file is being uploaded rather than just the name
@ -482,7 +490,7 @@ export const ContentAnalysis = {
*
* @param {nsIContentAnalysisRequest} aRequest
* @param {ResourceNameOrOperationType} aResourceNameOrOperationType
* @param {BrowsingContext} aBrowsingContext
* @param {CanonicalBrowsingContext} aBrowsingContext
*/
_queueSlowCAMessage(
aRequest,
@ -553,7 +561,7 @@ export const ContentAnalysis = {
/**
* Gets all the requests that are still in progress.
*
* @returns {Iterable<RequestInfo>} Information about the requests that are still in progress
* @returns {IteratorObject<RequestInfo>} Information about the requests that are still in progress
*/
_getAllSlowCARequestInfos() {
return this.userActionToBusyDialogMap
@ -566,10 +574,10 @@ export const ContentAnalysis = {
* Show a message to the user to indicate that a CA request is taking
* a long time.
*
* @param {string} aOperation Name of the operation
* @param {nsIContentAnalysisRequest.AnalysisType} aOperation The operation
* @param {nsIContentAnalysisRequest} aRequest The request that is taking a long time
* @param {string} aBodyMessage Message to show in the body of the alert
* @param {BrowsingContext} aBrowsingContext BrowsingContext to show the alert in
* @param {CanonicalBrowsingContext} aBrowsingContext BrowsingContext to show the alert in
*/
_showSlowCAMessage(aOperation, aRequest, aBodyMessage, aBrowsingContext) {
if (!this._shouldShowBlockingNotification(aOperation)) {
@ -712,8 +720,6 @@ export const ContentAnalysis = {
// This is also be called if the tab/window is closed while a request is in progress,
// in which case we need to cancel the request.
if (this.requestTokenToRequestInfo.delete(aRequestToken)) {
// TODO: Is this useful? I think no.
this._removeSlowCAMessage({}, aRequestToken);
this._removeSlowCAMessage(aUserActionId, aRequestToken);
lazy.gContentAnalysis.cancelRequestsByRequestToken(aRequestToken);
}
@ -726,14 +732,14 @@ export const ContentAnalysis = {
/**
* Show a message to the user to indicate the result of a CA request.
*
* @param {object} aResourceNameOrOperationType
* @param {BrowsingContext} aBrowsingContext
* @param {ResourceNameOrOperationType} aResourceNameOrOperationType
* @param {CanonicalBrowsingContext} aBrowsingContext
* @param {string} aRequestToken
* @param {string} aUserActionId
* @param {number} aCAResult
* @param {bool} aIsAgentResponse
* @param {boolean} aIsAgentResponse
* @param {number} aRequestCancelError
* @returns {NotificationInfo?} a notification object (if shown)
* @returns {Promise<NotificationInfo?>} a notification object (if shown)
*/
async _showCAResult(
aResourceNameOrOperationType,
@ -785,7 +791,7 @@ export const ContentAnalysis = {
),
null,
null,
{}
false
);
allow = result.get("buttonNumClicked") === 0;
} catch {

View file

@ -0,0 +1,14 @@
{
"extends": "../../../../tools/@types/tsconfig.json",
"include": ["*.mjs", "types/*.ts"],
"compilerOptions": {
"checkJs": true,
"plugins": [
{
"transform": "../../../../tools/ts/plugins/checkRootOnly.js",
"transformProgram": true
}
]
}
}

View file

@ -1,6 +1,5 @@
[DEFAULT]
head = ''
skip-if = ["os == 'android'"] # bug 1730213
firefox-appdir = "browser"
["test_unified_extensions_migration.js"]

View file

@ -1,11 +1,9 @@
[DEFAULT]
skip-if = ["os == 'android'"] # bug 1730213
head = "head.js"
firefox-appdir = "browser"
support-files = ["../../../../../netwerk/test/unit/http2-ca.pem"]
["test_DNSLookup.js"]
skip-if = ["debug"] # Bug 1617845
["test_LookupAggregator.js"]

View file

@ -1,7 +1,6 @@
[DEFAULT]
head = "head.js"
firefox-appdir = "browser"
skip-if = ["os == 'android'"] # bug 1730213
["test_DownloadLastDir_basics.js"]

View file

@ -1987,7 +1987,6 @@ export var Policies = {
"security.mixed_content.block_active_content",
"security.mixed_content.block_display_content",
"security.mixed_content.upgrade_display_content",
"security.osclientcerts.assume_rsa_pss_support",
"security.osclientcerts.autoload",
"security.OCSP.enabled",
"security.OCSP.require",
@ -2642,9 +2641,22 @@ export var Policies = {
param.FeatureRecommendations,
param.Locked
);
// We use the mostRecentTargetLanguages pref to control the
// translations panel intro. Setting a language value simulates a
// first translation, which skips the intro panel for users with
// FeatureRecommendations disabled.
const topWebPreferredLanguage = Services.prefs
.getComplexValue("intl.accept_languages", Ci.nsIPrefLocalizedString)
.data.split(/\s*,\s*/g)[0];
const preferredLanguage = topWebPreferredLanguage.length
? topWebPreferredLanguage
: Services.locale.appLocaleAsBCP47;
PoliciesUtils.setDefaultPref(
"browser.translations.panelShown",
!param.FeatureRecommendations,
"browser.translations.mostRecentTargetLanguages",
param.FeatureRecommendations ? "" : preferredLanguage,
param.Locked
);
}

View file

@ -1,5 +1,4 @@
[DEFAULT]
skip-if = ["os == 'android'"] # bug 1730213
firefox-appdir = "browser"
head = "head.js"
support-files = [

View file

@ -309,3 +309,353 @@ add_task(async function testOptionalPermissionsDialogShowsFullDomainsList() {
await extension.unload();
}
});
add_task(async function testOptionalPermissionsDialogWithDataCollection() {
await SpecialPowers.pushPrefEnv({
set: [["extensions.dataCollectionPermissions.enabled", true]],
});
const createTestExtension = ({
id,
optional_permissions = undefined,
data_collection_permissions = undefined,
}) => {
return ExtensionTestUtils.loadExtension({
manifest: {
// Set the generated id as a name to make it easier to recognize the
// test case from dialog screenshots (e.g. in the screenshot captured
// when the test hits a failure).
name: id,
version: "1.0",
optional_permissions,
browser_specific_settings: {
gecko: { id, data_collection_permissions },
},
},
files: {
"extpage.html": `<!DOCTYPE html><script src="extpage.js"></script>`,
"extpage.js"() {
browser.test.onMessage.addListener(async msg => {
if (msg !== "request-perms") {
browser.test.fail(`Got unexpected test message ${msg}`);
return;
}
const {
browser_specific_settings: {
gecko: { data_collection_permissions },
},
optional_permissions,
} = browser.runtime.getManifest();
let perms = {
data_collection: data_collection_permissions.optional,
};
if (optional_permissions.length) {
perms.permissions = optional_permissions;
}
browser.test.withHandlingUserInput(() => {
browser.permissions.request(perms);
browser.test.sendMessage("perms-requested");
});
});
browser.test.sendMessage("ready");
},
},
});
};
const TEST_CASES = [
{
title: "With an optional data collection permission",
data_collection_permissions: {
optional: ["healthInfo"],
},
verifyDialog(popupContentEl, { extensionId }) {
Assert.equal(
popupContentEl.querySelector(".popup-notification-description")
.textContent,
PERMISSION_L10N.formatValueSync(
"webext-perms-optional-data-collection-only-text",
{ extension: extensionId }
),
"Expected header string without perms"
);
Assert.equal(
popupContentEl.permsListEl.childElementCount,
1,
"Expected a single entry in the list"
);
Assert.equal(
popupContentEl.permsListEl.textContent,
PERMISSION_L10N.formatValueSync(
"webext-perms-description-data-some-optional",
{
permissions: "health information",
}
),
"Expected formatted data collection permission string"
);
Assert.ok(
popupContentEl.hasAttribute("learnmoreurl"),
"Expected a learn more link"
);
},
},
{
title: "With multiple optional data collection permissions",
data_collection_permissions: {
optional: ["healthInfo", "bookmarksInfo"],
},
verifyDialog(popupContentEl, { extensionId }) {
Assert.equal(
popupContentEl.querySelector(".popup-notification-description")
.textContent,
PERMISSION_L10N.formatValueSync(
"webext-perms-optional-data-collection-only-text",
{ extension: extensionId }
),
"Expected header string without perms"
);
Assert.equal(
popupContentEl.permsListEl.childElementCount,
1,
"Expected a single entry in the list"
);
Assert.equal(
popupContentEl.permsListEl.textContent,
PERMISSION_L10N.formatValueSync(
"webext-perms-description-data-some-optional",
{
permissions: "health information, bookmarks",
}
),
"Expected formatted data collection permission string"
);
Assert.ok(
popupContentEl.hasAttribute("learnmoreurl"),
"Expected a learn more link"
);
},
},
{
title: "With technical and interaction data",
data_collection_permissions: {
optional: ["technicalAndInteraction"],
},
verifyDialog(popupContentEl, { extensionId }) {
Assert.equal(
popupContentEl.querySelector(".popup-notification-description")
.textContent,
PERMISSION_L10N.formatValueSync(
"webext-perms-optional-data-collection-only-text",
{ extension: extensionId }
),
"Expected header string without perms"
);
Assert.equal(
popupContentEl.permsListEl.childElementCount,
1,
"Expected a single entry in the list"
);
Assert.equal(
popupContentEl.permsListEl.textContent,
PERMISSION_L10N.formatValueSync(
"webext-perms-description-data-some-optional",
{
permissions: "technical and interaction data",
}
),
"Expected formatted data collection permission string"
);
Assert.ok(
popupContentEl.hasAttribute("learnmoreurl"),
"Expected a learn more link"
);
},
},
{
title: "With optional API and data collection permissions",
optional_permissions: ["bookmarks"],
data_collection_permissions: {
optional: ["bookmarksInfo"],
},
verifyDialog(popupContentEl, { extensionId }) {
Assert.equal(
popupContentEl.querySelector(".popup-notification-description")
.textContent,
PERMISSION_L10N.formatValueSync(
"webext-perms-optional-data-collection-text",
{ extension: extensionId }
),
"Expected header string with perms"
);
Assert.equal(
popupContentEl.permsListEl.childElementCount,
2,
"Expected two entries in the list"
);
Assert.equal(
popupContentEl.permsListEl.firstChild.textContent,
PERMISSION_L10N.formatValueSync("webext-perms-description-bookmarks"),
"Expected formatted data collection permission string"
);
Assert.equal(
popupContentEl.permsListEl.lastChild.textContent,
PERMISSION_L10N.formatValueSync(
"webext-perms-description-data-some-optional",
{
permissions: "bookmarks",
}
),
"Expected formatted data collection permission string"
);
Assert.ok(
popupContentEl.hasAttribute("learnmoreurl"),
"Expected a learn more link"
);
},
},
{
title: "With non-promptable API and optional data collection permission",
optional_permissions: ["webRequest"],
data_collection_permissions: {
optional: ["healthInfo"],
},
verifyDialog(popupContentEl, { extensionId }) {
Assert.equal(
popupContentEl.querySelector(".popup-notification-description")
.textContent,
PERMISSION_L10N.formatValueSync(
"webext-perms-optional-data-collection-only-text",
{ extension: extensionId }
),
"Expected header string without perms"
);
// We expect a single entry because `webRequest` is non-promptable.
Assert.equal(
popupContentEl.permsListEl.childElementCount,
1,
"Expected a single entry in the list"
);
Assert.equal(
popupContentEl.permsListEl.textContent,
PERMISSION_L10N.formatValueSync(
"webext-perms-description-data-some-optional",
{
permissions: "health information",
}
),
"Expected formatted data collection permission string"
);
Assert.ok(
popupContentEl.hasAttribute("learnmoreurl"),
"Expected a learn more link"
);
},
},
];
for (const {
title,
optional_permissions,
data_collection_permissions,
verifyDialog,
} of TEST_CASES) {
info(title);
const extensionId = `@${title.toLowerCase().replaceAll(/[^\w]+/g, "-")}`;
const extension = createTestExtension({
id: extensionId,
optional_permissions,
data_collection_permissions,
});
await extension.startup();
let extPageURL = `moz-extension://${extension.uuid}/extpage.html`;
await BrowserTestUtils.withNewTab(extPageURL, async () => {
let promiseRequestDisalog = promisePopupNotificationShown(
"addon-webext-permissions"
);
await extension.awaitMessage("ready");
extension.sendMessage("request-perms");
await extension.awaitMessage("perms-requested");
const popupContentEl = await promiseRequestDisalog;
verifyDialog(popupContentEl, { extensionId });
});
await extension.unload();
}
await SpecialPowers.popPrefEnv();
});
add_task(
async function testOptionalPermissionsDialogWithDataCollectionAlreadyGranted() {
await SpecialPowers.pushPrefEnv({
set: [["extensions.dataCollectionPermissions.enabled", true]],
});
const extension = ExtensionTestUtils.loadExtension({
manifest: {
version: "1.0",
browser_specific_settings: {
gecko: {
data_collection_permissions: {
optional: ["healthInfo"],
},
},
},
},
files: {
"extpage.html": `<!DOCTYPE html><script src="extpage.js"></script>`,
"extpage.js"() {
browser.test.onMessage.addListener(async msg => {
if (msg !== "request-perms") {
browser.test.fail(`Got unexpected test message ${msg}`);
return;
}
browser.test.withHandlingUserInput(async () => {
await browser.permissions.request({
data_collection: ["healthInfo"],
});
browser.test.sendMessage("perms-requested");
});
});
browser.test.sendMessage("ready");
},
},
});
await extension.startup();
let extPageURL = `moz-extension://${extension.uuid}/extpage.html`;
await BrowserTestUtils.withNewTab(extPageURL, async () => {
let promiseRequestDisalog = promisePopupNotificationShown(
"addon-webext-permissions"
).then(panel => {
// Grant the permission.
panel.button.click();
});
await extension.awaitMessage("ready");
extension.sendMessage("request-perms");
await extension.awaitMessage("perms-requested");
await promiseRequestDisalog;
extension.sendMessage("request-perms");
await extension.awaitMessage("perms-requested");
});
await extension.unload();
await SpecialPowers.popPrefEnv();
}
);

View file

@ -99,7 +99,7 @@ async function checkTelemetry(source, prevEngine, newEngine) {
category: "search.engine.default",
name: "changed",
extra: {
change_source: source,
change_reason: source,
previous_engine_id: prevEngine.id,
new_engine_id: newEngine.id,
new_display_name: newEngine.name,

View file

@ -1,17 +1,14 @@
[DEFAULT]
skip-if = ["os == 'android'"] # bug 1730213
head = "head.js"
firefox-appdir = "browser"
tags = "webextensions"
dupe-manifest = ""
["test_ext_bookmarks.js"]
skip-if = ["condprof"] # Bug 1769184 - by design for now
["test_ext_browsingData_downloads.js"]
["test_ext_browsingData_passwords.js"]
skip-if = ["tsan"] # Times out, bug 1612707
["test_ext_browsingData_settings.js"]
@ -44,7 +41,6 @@ skip-if = ["tsan"] # Times out, bug 1612707
["test_ext_pkcs11_management.js"]
["test_ext_settings_overrides_defaults.js"]
skip-if = ["condprof"] # Bug 1776135 - by design, modifies search settings at start of test
["test_ext_settings_overrides_search.js"]
@ -53,7 +49,6 @@ skip-if = ["condprof"] # Bug 1776135 - by design, modifies search settings at st
["test_ext_settings_validate.js"]
["test_ext_topSites.js"]
skip-if = ["condprof"] # Bug 1769184 - by design for now
["test_ext_url_overrides_newtab.js"]

View file

@ -400,6 +400,7 @@ export class FxviewTabRowBase extends MozLitElement {
title: { type: String },
timeMsPref: { type: Number },
url: { type: String },
uri: { type: String },
searchQuery: { type: String },
};
@ -423,6 +424,11 @@ export class FxviewTabRowBase extends MozLitElement {
return focusItem;
}
connectedCallback() {
super.connectedCallback();
this.uri = this.url;
}
focus() {
this.currentFocusable.focus();
}

View file

@ -6,12 +6,19 @@ prefs = [
]
["browser_chat_contextmenu.js"]
["browser_chat_nimbus.js"]
["browser_chat_request.js"]
["browser_chat_shortcuts.js"]
["browser_chat_sidebar.js"]
["browser_chat_telemetry.js"]
["browser_genai_actors.js"]
["browser_genai_init.js"]
["browser_link_preview.js"]

View file

@ -2,7 +2,11 @@
firefox-appdir = "browser"
["test_build_chat_prompt.js"]
["test_contextual_prompts.js"]
["test_link_preview_text.js"]
["test_provider_id.js"]
["test_show_chat_entrypoint.js"]

View file

@ -1,7 +1,7 @@
[DEFAULT]
head = "head.js"
firefox-appdir = "browser"
skip-if = ["os != 'win'"]
run-if = ["os == 'win'"]
# These tests must all run sequentially because they use the same registry key.
# It might be possible to get around this requirement by overriding the install
@ -10,7 +10,9 @@ skip-if = ["os != 'win'"]
["test_empty_prefs_list.js"]
run-sequentially = "Uses the Windows registry"
skip-if = ["os == 'win' && msix"] # https://bugzilla.mozilla.org/show_bug.cgi?id=1807932
skip-if = [
"os == 'win' && os_version == '11.26100' && processor == 'x86_64' && msix", # Bug 1807932
]
["test_invalid_name.js"]
run-sequentially = "Uses the Windows registry"

View file

@ -286,7 +286,7 @@ add_task(async function test_show_happy_feature_callout_message() {
let tab = await BrowserTestUtils.openNewForegroundTab(
win.gBrowser,
"about:messagepreview",
false
true
);
let aboutMessagePreviewActor = await getAboutMessagePreviewParent(
@ -330,7 +330,7 @@ add_task(async function test_show_feature_callout_without_trigger() {
let tab = await BrowserTestUtils.openNewForegroundTab(
win.gBrowser,
"about:messagepreview",
false
true
);
let aboutMessagePreviewActor = await getAboutMessagePreviewParent(
@ -374,7 +374,7 @@ add_task(async function test_show_feature_callout_anchors() {
let tab = await BrowserTestUtils.openNewForegroundTab(
win.gBrowser,
"about:messagepreview",
false
true
);
let aboutMessagePreviewActor = await getAboutMessagePreviewParent(
@ -418,7 +418,7 @@ add_task(async function test_show_bad_feature_callout_message() {
let tab = await BrowserTestUtils.openNewForegroundTab(
win.gBrowser,
"about:messagepreview",
false
true
);
let aboutMessagePreviewActor = await getAboutMessagePreviewParent(

View file

@ -985,3 +985,21 @@ browser:
- aoprea@mozilla.com
expires: never
telemetry_mirror: h#BROWSER_ATTRIBUTION_ERRORS
default_at_launch:
type: boolean
lifetime: application
description: |
Whether the shell service identified this app as the default browser.
Checked once near startup.
On Windows, this is operationalized as whether Firefox is the default
HTTP protocol handler and the default HTML file handler.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1950389
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1950389
notification_emails:
- application-update-telemetry-alerts@mozilla.com
data_sensitivity:
- technical
expires: never

View file

@ -2,7 +2,6 @@
head = "head_migration.js"
tags = "condprof os_integration"
firefox-appdir = "browser"
skip-if = ["os == 'android'"] # bug 1730213
prefs = ["browser.migrate.showBookmarksToolbarAfterMigration=true"]
support-files = [
"Library/**",
@ -32,33 +31,21 @@ run-if = ["os == 'linux'"]
["test_Chrome_corrupt_history.js"]
["test_Chrome_credit_cards.js"]
skip-if = [
"os == 'linux'",
"os == 'android'",
"condprof", # bug 1769154 - not realistic for condprof
]
skip-if = ["os == 'linux' && os_version == '18.04' && processor == 'x86_64'"]
["test_Chrome_extensions.js"]
["test_Chrome_formdata.js"]
["test_Chrome_history.js"]
skip-if = ["os != 'mac'"] # Relies on ULibDir
run-if = ["os == 'mac'"] # Relies on ULibDir
["test_Chrome_passwords.js"]
skip-if = [
"os == 'linux'",
"os == 'android'",
"condprof", # bug 1769154 - not realistic for condprof
]
skip-if = ["os == 'linux' && os_version == '18.04' && processor == 'x86_64'"]
["test_Chrome_passwords_emptySource.js"]
skip-if = [
"os == 'linux'",
"os == 'android'",
"condprof", # bug 1769154 - not realistic for condprof
]
support-files = ["LibraryWithNoData/**"]
skip-if = ["os == 'linux' && os_version == '18.04' && processor == 'x86_64'"]
["test_Chrome_permissions.js"]
@ -73,10 +60,11 @@ run-if = ["os == 'win' && bits == 64"] # bug 1392396
["test_IE_history.js"]
run-if = ["os == 'win'"]
skip-if = ["os == 'win' && msix"] # https://bugzilla.mozilla.org/show_bug.cgi?id=1807928
skip-if = [
"os == 'win' && os_version == '11.26100' && processor == 'x86_64' && msix", # Bug 1807928
]
["test_MigrationUtils_timedRetry.js"]
skip-if = ["os == 'mac' && !debug"] #Bug 1558330
["test_PasswordFileMigrator.js"]

View file

@ -47,6 +47,7 @@ DIRS += [
"newtab",
"originattributes",
"pagedata",
"permissions",
"places",
"pocket",
"preferences",
@ -104,6 +105,13 @@ EXTRA_JS_MODULES += [
"distribution.sys.mjs",
]
MOZ_SRC_FILES += [
"DefaultBrowserCheck.sys.mjs",
"DesktopActorRegistry.sys.mjs",
"ProfileDataUpgrader.sys.mjs",
"StartupTelemetry.sys.mjs",
]
if CONFIG["MOZ_DEBUG"] or CONFIG["MOZ_DEV_EDITION"] or CONFIG["NIGHTLY_BUILD"]:
EXTRA_JS_MODULES += [
"StartupRecorder.sys.mjs",

View file

@ -1503,6 +1503,22 @@ pocket:
description: >
If click belongs in a section, the numeric position of the section
type: string
title: &title
description: >
Title of the article
type: string
url: &url
description: >
Url of the article
type: string
time_sensitive: &time_sensitive
description: >
Indicates whether the article is time sensitive. This is passed down from merino
type: boolean
publisher: &publisher
description: >
derived publisher name of article
type: string
is_section_followed: *is_section_followed
send_in_pings:
- newtab
@ -1570,6 +1586,10 @@ pocket:
If click belongs in a section, the numeric position of the section
type: string
is_section_followed: *is_section_followed
title: *title
url: *url
time_sensitive: *time_sensitive
publisher: *publisher
send_in_pings:
- newtab
@ -1612,6 +1632,10 @@ pocket:
If click belongs in a section, the numeric position of the section
type: string
is_section_followed: *is_section_followed
title: *title
url: *url
time_sensitive: *time_sensitive
publisher: *publisher
send_in_pings:
- newtab
@ -1822,9 +1846,556 @@ pocket:
If event belongs in a section, the numeric position of the section
type: string
is_section_followed: *is_section_followed
title: *title
url: *url
time_sensitive: *time_sensitive
publisher: *publisher
send_in_pings:
- newtab
newtab_content:
coarse_os:
type: string
description: >
The name of the operating system. Possible values:
Android, iOS, Linux, Windows, or macOS
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_sensitivity:
- technical
notification_emails:
- nbarrett@mozilla.com
expires: never
lifetime: application
send_in_pings:
- newtab-content
coarse_os_version:
type: string
description: |
The user-visible version of the operating system (e.g. "1.2.3").
If the version detection fails, this metric gets set to 0.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_sensitivity:
- technical
notification_emails:
- nbarrett@mozilla.com
expires: never
lifetime: application
send_in_pings:
- newtab-content
utc_offset:
type: quantity
description: >
<0-24> positive UTC offset, rounded to the nearest integer number greater than 0.
(If less than 0, then add 24.)
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_sensitivity:
- technical
notification_emails:
- nbarrett@mozilla.com
expires: never
send_in_pings:
- newtab-content
lifetime: application
unit: utc offset
surface_id:
type: string
description: >
Surface id sent to the client from merino api
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_sensitivity:
- technical
notification_emails:
- nbarrett@mozilla.com
expires: never
send_in_pings:
- newtab-content
- newtab
lifetime: application
followed_sections:
type: string_list
description: >
Optional metric: section ids followed by user in alphabetical order, if possible,
ordered by most recently followed. Max 2 sections
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_sensitivity:
- interaction
notification_emails:
- nbarrett@mozilla.com
expires: never
send_in_pings:
- newtab-content
lifetime: application
# The `newtab-content` ping does not include any info sections, and
# therefore needs to capture experiments and rollouts independently. This
# data layout agrees with the `nimbus-targeting-context` ping for ease of
# analysis.
active_experiments:
type: object
description: >
slugs of actively enrolled experiments
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_sensitivity:
- interaction
notification_emails:
- nbarrett@mozilla.com
send_in_pings:
- newtab-content
expires: never
structure:
type: array
items:
type: string
active_rollouts:
type: object
description: >
slugs of actively enrolled rollouts
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_sensitivity:
- interaction
notification_emails:
- nbarrett@mozilla.com
send_in_pings:
- newtab-content
expires: never
structure:
type: array
items:
type: string
enrollments_map:
type: object
description: >
Information about historic enrollments, including the branches enrolled.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
expires: never
notification_emails:
- nbarrett@mozilla.com
send_in_pings:
- newtab-content
data_sensitivity:
- technical
structure:
type: array
items:
type: object
properties:
experimentSlug:
type: string
branchSlug:
type: string
impression:
type: event
description: >
Recorded when a pocket tile is visible to the user.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1786612
- https://bugzilla.mozilla.org/show_bug.cgi?id=1817105
- https://bugzilla.mozilla.org/show_bug.cgi?id=1854245
- https://bugzilla.mozilla.org/show_bug.cgi?id=1862670
- https://bugzilla.mozilla.org/show_bug.cgi?id=1937200
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1786612
- https://bugzilla.mozilla.org/show_bug.cgi?id=1817105#c11
- https://bugzilla.mozilla.org/show_bug.cgi?id=1854245
data_sensitivity:
- interaction
notification_emails:
- anicholson@mozilla.com
- chutten@mozilla.com
- mmccorquodale@mozilla.com
- najiang@mozilla.com
- lina@mozilla.com
- sdowne@mozilla.com
expires: never
extra_keys:
newtab_visit_id: *newtab_visit_id
is_sponsored: *is_sponsored_pocket
format: *format
position: *pocket_position
recommendation_id: *recommendation_id
tile_id: *pocket_tile_id
scheduled_corpus_item_id: *scheduled_corpus_item_id
corpus_item_id: *corpus_item_id
received_rank: *received_rank
recommended_at: *recommended_at
topic:
description: The topic of the recommendation. Like "entertainment".
type: string
selected_topics:
description: >
The list of topics the user selected
type: string
is_list_card:
description: >
Where the article card is in the list component
type: boolean
section:
description: >
If click belongs in a section, the name of the section
type: string
section_position:
description: >
If click belongs in a section, the numberic position of the section
type: string
is_section_followed:
description: >
If click belongs in a section, if that section is followed
type: boolean
title: *title
url: *url
time_sensitive: *time_sensitive
publisher: *publisher
send_in_pings:
- newtab-content
click:
type: event
description: >
Recorded when a pocket tile is clicked.
Only happens on click. Not on middle-click. Not on "Open in new Tab"-like
options in the context menu.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_sensitivity:
- interaction
notification_emails:
- mcrawford@mozilla.cocm
expires: never
extra_keys:
newtab_visit_id: *newtab_visit_id
is_sponsored: *is_sponsored_pocket
format: *format
position: *pocket_position
recommendation_id: *recommendation_id
tile_id: *pocket_tile_id
scheduled_corpus_item_id: *scheduled_corpus_item_id
corpus_item_id: *corpus_item_id
received_rank: *received_rank
recommended_at: *recommended_at
matches_selected_topic:
description: >
Returns value based on if a the topic of the pocket recommendation matches one
of the user-selected topic categories
type: string
topic:
description: The topic of the recommendation. Like "entertainment".
type: string
selected_topics:
description: >
The list of topics the user selected
type: string
is_list_card:
description: >
Where the article card is in the list component
type: boolean
section:
description: >
If click belongs in a section, the name of the section
type: string
section_position:
description: >
If click belongs in a section, the numberic position of the section
type: string
is_section_followed:
description: >
If click belongs in a section, if that section is followed
type: boolean
title: *title
url: *url
time_sensitive: *time_sensitive
publisher: *publisher
send_in_pings:
- newtab-content
dismiss:
type: event
description: >
Recorded when a pocket tile is dismissed.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1786612
- https://bugzilla.mozilla.org/show_bug.cgi?id=1937200
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1786612
data_sensitivity:
- interaction
notification_emails:
- sdowne@mozilla.com
- mcrawford@mozilla.com
expires: never
extra_keys:
newtab_visit_id: *newtab_visit_id
is_sponsored: *is_sponsored_pocket
format: *format
position: *pocket_position
recommendation_id: *recommendation_id
tile_id: *pocket_tile_id
scheduled_corpus_item_id: *scheduled_corpus_item_id
corpus_item_id: *corpus_item_id
received_rank: *received_rank
recommended_at: *recommended_at
section:
description: >
If click belongs in a section, the name of the section
type: string
section_position:
description: >
If click belongs in a section, the numberic position of the section
type: string
is_section_followed:
description: >
If click belongs in a section, if that section is followed
type: boolean
title: *title
url: *url
time_sensitive: *time_sensitive
publisher: *publisher
send_in_pings:
- newtab-content
thumb_voting_interaction:
type: event
description: >
Recorded when a thumbs up/down on a tile is clicked.
Only happens on click. Not on middle-click. Not on "Open in new Tab"-like
options in the context menu.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1902099
- https://bugzilla.mozilla.org/show_bug.cgi?id=1937200
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1902099
data_sensitivity:
- interaction
notification_emails:
- mcrawford@mozilla.com
expires: never
extra_keys:
newtab_visit_id: *newtab_visit_id
recommendation_id: *recommendation_id
tile_id: *pocket_tile_id
thumbs_up:
description: >
If the user clicked thumbs up.
type: boolean
thumbs_down:
description: >
If the user clicked thumbs down.
type: boolean
scheduled_corpus_item_id: *scheduled_corpus_item_id
corpus_item_id: *corpus_item_id
received_rank: *received_rank
recommended_at: *recommended_at
topic:
description: The topic of the recommendation. Like "entertainment".
type: string
is_list_card:
description: >
Where the article card is in the list component
type: boolean
section:
description: >
If event belongs in a section, the name of the section
type: string
section_position:
description: >
If event belongs in a section, the numberic position of the section
type: string
is_section_followed:
description: >
If event belongs in a section, if that section is followed
type: boolean
title: *title
url: *url
time_sensitive: *time_sensitive
publisher: *publisher
send_in_pings:
- newtab-content
sections_impression:
type: event
description: >
Recorded when a section is viewport and triggers an impression event
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1927916
- https://bugzilla.mozilla.org/show_bug.cgi?id=1938215
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1927916
data_sensitivity:
- interaction
notification_emails:
- nbarrett@mozilla.com
- mcrawford@mozilla.com
expires: never
extra_keys:
newtab_visit_id: *newtab_visit_id
section:
description: >
section that had impression event
type: string
section_position:
description: >
position of section on newtab
type: string
is_section_followed:
description: >
If click belongs in a section, if that section is followed
type: boolean
send_in_pings:
- newtab-content
sections_follow_section:
type: event
description: >
Recorded when a section is followed
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1932191
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1932191
data_sensitivity:
- interaction
notification_emails:
- mcrawford@mozilla.com
expires: never
extra_keys:
newtab_visit_id: *newtab_visit_id
section:
description: >
section that had impression event
type: string
section_position:
description: >
position of section on newtab
type: string
event_source:
description: >
Where the source of the event originated ("button", "context menu", etc.)
type: string
send_in_pings:
- newtab-content
sections_unfollow_section:
type: event
description: >
Recorded when a section is unfollowed
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1932191
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1932191
data_sensitivity:
- interaction
notification_emails:
- mcrawford@mozilla.com
expires: never
extra_keys:
newtab_visit_id: *newtab_visit_id
section:
description: >
section that had impression event
type: string
section_position:
description: >
position of section on newtab
type: string
event_source:
description: >
Where the source of the event originated ("button", "context menu", etc.)
type: string
send_in_pings:
- newtab-content
sections_block_section:
type: event
description: >
Recorded when a section is blocked
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1932191
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1932191
data_sensitivity:
- interaction
notification_emails:
- mcrawford@mozilla.com
expires: never
extra_keys:
newtab_visit_id: *newtab_visit_id
section:
description: >
section that had impression event
type: string
section_position:
description: >
position of section on newtab
type: string
event_source:
description: >
Where the source of the event originated ("button", "context menu", etc.)
type: string
send_in_pings:
- newtab-content
sections_unblock_section:
type: event
description: >
Recorded when a section is unblocked
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1940566
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1940566
data_sensitivity:
- interaction
notification_emails:
- mcrawford@mozilla.com
expires: never
extra_keys:
newtab_visit_id: *newtab_visit_id
section:
description: >
section that had unblock event
type: string
section_position:
description: >
position of section on newtab
type: string
event_source:
description: >
Where the source of the event originated ("button", "context menu", etc.)
type: string
send_in_pings:
- newtab-content
top_sites: # Replacement for PingCentre "topsites-impression|click" pings.
ping_type:
type: string

View file

@ -30,6 +30,32 @@ newtab:
- anicholson@mozilla.com
- najiang@mozilla.com
newtab-content:
description: |
Private Newtab-related instrumentation sent over OHTTP
Can be disabled via the `newtabPingEnabled` variable of the `glean` Nimbus
feature, or the `browser.newtabpage.ping.enabled` pref.
metadata:
include_info_sections: false
include_client_id: false
uploader_capabilities:
- ohttp
reasons:
newtab_session_end: |
The newtab visit ended.
Could be by navigation, being closed, etc.
component_init: |
The newtab component init'd,
and the newtab and homepage settings have been categorized.
This is mostly to ensure we hear at least once from clients configured to
not show a newtab UI.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1956331
notification_emails:
- nbarrett@mozilla.com
top-sites:
description: |
A ping representing a single event happening with or to a TopSite.

View file

@ -1,6 +1,5 @@
[DEFAULT]
firefox-appdir = "browser"
skip-if = ["os == 'android'"] # bug 1730213
head = "../../../../extensions/newtab/test/xpcshell/head.js"
["test_AboutNewTab.js"]

View file

@ -1,6 +1,5 @@
[DEFAULT]
firefox-appdir = "browser"
skip-if = ["os == 'android'"] # bug 1730213
support-files = ["head.js"]
head = "head.js"

View file

@ -0,0 +1,133 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
Integration: "resource://gre/modules/Integration.sys.mjs",
PermissionUI: "resource:///modules/PermissionUI.sys.mjs",
});
/**
* ContentPermissionIntegration is responsible for showing the user
* simple permission prompts when content requests additional
* capabilities.
*
* While there are some built-in permission prompts, createPermissionPrompt
* can also be overridden by system add-ons or tests to provide new ones.
*
* This override ability is provided by Integration.sys.mjs. See
* PermissionUI.sys.mjs for an example of how to provide a new prompt
* from an add-on.
*/
const ContentPermissionIntegration = {
/**
* Creates a PermissionPrompt for a given permission type and
* nsIContentPermissionRequest.
*
* @param {string} type
* The type of the permission request from content. This normally
* matches the "type" field of an nsIContentPermissionType, but it
* can be something else if the permission does not use the
* nsIContentPermissionRequest model. Note that this type might also
* be different from the permission key used in the permissions
* database.
* Example: "geolocation"
* @param {nsIContentPermissionRequest} request
* The request for a permission from content.
* @returns {PermissionPrompt?} (see PermissionUI.sys.mjs),
* or undefined if the type cannot be handled.
*/
createPermissionPrompt(type, request) {
switch (type) {
case "geolocation": {
return new lazy.PermissionUI.GeolocationPermissionPrompt(request);
}
case "xr": {
return new lazy.PermissionUI.XRPermissionPrompt(request);
}
case "desktop-notification": {
return new lazy.PermissionUI.DesktopNotificationPermissionPrompt(
request
);
}
case "persistent-storage": {
return new lazy.PermissionUI.PersistentStoragePermissionPrompt(request);
}
case "midi": {
return new lazy.PermissionUI.MIDIPermissionPrompt(request);
}
case "storage-access": {
return new lazy.PermissionUI.StorageAccessPermissionPrompt(request);
}
}
return undefined;
},
};
export function ContentPermissionPrompt() {}
ContentPermissionPrompt.prototype = {
classID: Components.ID("{d8903bf6-68d5-4e97-bcd1-e4d3012f721a}"),
QueryInterface: ChromeUtils.generateQI(["nsIContentPermissionPrompt"]),
/**
* This implementation of nsIContentPermissionPrompt.prompt ensures
* that there's only one nsIContentPermissionType in the request,
* and that it's of type nsIContentPermissionType. Failing to
* satisfy either of these conditions will result in this method
* throwing NS_ERRORs. If the combined ContentPermissionIntegration
* cannot construct a prompt for this particular request, an
* NS_ERROR_FAILURE will be thrown.
*
* Any time an error is thrown, the nsIContentPermissionRequest is
* cancelled automatically.
*
* @param {nsIContentPermissionRequest} request
* The request that we're to show a prompt for.
*/
prompt(request) {
if (request.element && request.element.fxrPermissionPrompt) {
// For Firefox Reality on Desktop, switch to a different mechanism to
// prompt the user since fewer permissions are available and since many
// UI dependencies are not availabe.
request.element.fxrPermissionPrompt(request);
return;
}
let type;
try {
// Only allow exactly one permission request here.
let types = request.types.QueryInterface(Ci.nsIArray);
if (types.length != 1) {
throw Components.Exception(
"Expected an nsIContentPermissionRequest with only 1 type.",
Cr.NS_ERROR_UNEXPECTED
);
}
type = types.queryElementAt(0, Ci.nsIContentPermissionType).type;
let combinedIntegration = lazy.Integration.contentPermission.getCombined(
ContentPermissionIntegration
);
let permissionPrompt = combinedIntegration.createPermissionPrompt(
type,
request
);
if (!permissionPrompt) {
throw Components.Exception(
`Failed to handle permission of type ${type}`,
Cr.NS_ERROR_FAILURE
);
}
permissionPrompt.prompt();
} catch (ex) {
console.error(ex);
request.cancel();
throw ex;
}
},
};

View file

@ -0,0 +1,14 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
Classes = [
{
'cid': '{d8903bf6-68d5-4e97-bcd1-e4d3012f721a}',
'contract_ids': ['@mozilla.org/content-permission/prompt;1'],
'esModule': 'moz-src:///browser/components/permissions/ContentPermissionPrompt.sys.mjs',
'constructor': 'ContentPermissionPrompt',
},
]

View file

@ -0,0 +1,16 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPCOM_MANIFESTS += [
"components.conf",
]
MOZ_SRC_FILES += [
"ContentPermissionPrompt.sys.mjs",
]
with Files("**"):
BUG_COMPONENT = ("Firefox", "Site Permissions")

View file

@ -907,8 +907,8 @@ export var PlacesUIUtils = {
) {
if (
aNode &&
lazy.PlacesUtils.nodeIsURI(aNode) &&
this.checkURLSecurity(aNode, aWindow)
this.checkURLSecurity(aNode, aWindow) &&
this.isURILike(aNode)
) {
let isBookmark = lazy.PlacesUtils.nodeIsBookmark(aNode);
@ -940,6 +940,16 @@ export var PlacesUIUtils = {
}
},
/**
* Determines whether a node contains a uri
*
* @param {nsINavHistoryResultNode | DOMElement} aNode A result node.
* @returns {boolean} whether the node contains a uri
*/
isURILike(aNode) {
return lazy.PlacesUtils.nodeIsURI(aNode) || !!aNode.uri;
},
/**
* Helper for guessing scheme from an url string.
* Used to avoid nsIURI overhead in frequently called UI functions. This is not
@ -1391,11 +1401,19 @@ export var PlacesUIUtils = {
placesContextShowing(event) {
let menupopup = event.target;
if (menupopup.id != "placesContext") {
if (
!["placesContext", "sidebar-history-context-menu"].includes(menupopup.id)
) {
// Ignore any popupshowing events from submenus
return;
}
if (menupopup.id == "sidebar-history-context-menu") {
PlacesUIUtils.lastContextMenuTriggerNode =
menupopup.triggerNode.triggerNode;
return;
}
PlacesUIUtils.lastContextMenuTriggerNode = menupopup.triggerNode;
if (Services.prefs.getBoolPref("browser.tabs.loadBookmarksInTabs", false)) {
@ -1443,7 +1461,13 @@ export var PlacesUIUtils = {
menupopup._view.destroyContextMenu();
}
if (menupopup.id == "placesContext") {
if (
[
"sidebar-history-context-menu",
"placesContext",
"sidebar-synced-tabs-context-menu",
].includes(menupopup.id)
) {
PlacesUIUtils.lastContextMenuTriggerNode = null;
PlacesUIUtils.lastContextMenuCommand = null;
}
@ -1467,9 +1491,14 @@ export var PlacesUIUtils = {
return;
}
let view = this.getViewForNode(triggerNode);
this._openNodeIn(view.selectedNode, "tab", view.ownerWindow, {
this._openNodeIn(
view?.selectedNode || triggerNode,
"tab",
view?.ownerWindow || triggerNode.ownerGlobal.top,
{
userContextId,
});
}
);
},
openSelectionInTabs(event) {

View file

@ -1,7 +1,6 @@
[DEFAULT]
head = "head_bookmarks.js"
firefox-appdir = "browser"
skip-if = ["os == 'android'"] # bug 1730213
support-files = [
"bookmarks.glue.html",
"bookmarks.glue.json",

View file

@ -7,7 +7,6 @@
* Note: we display the icon box for every item whether or not it has an icon
* so the labels of all the items align vertically.
*/
.actionsMenu > menupopup > menuitem > .menu-iconic-left {
.actionsMenu > menupopup > menuitem > .menu-icon {
display: flex;
min-width: 16px;
}

View file

@ -74,7 +74,6 @@
<link rel="localization" href="preview/translations.ftl"/>
<link rel="localization" href="preview/enUS-searchFeatures.ftl"/>
<link rel="localization" href="preview/backupSettings.ftl"/>
<link rel="localization" href="preview/reportContentTemporary.ftl"/>
<link rel="localization" href="security/certificates/certManager.ftl"/>
<link rel="localization" href="security/certificates/deviceManager.ftl"/>
<link rel="localization" href="toolkit/updates/history.ftl"/>

View file

@ -272,7 +272,7 @@ add_task(async function test_setDefaultEngine() {
category: "search.engine.default",
name: "changed",
extra: {
change_source: "user",
change_reason: "user",
previous_engine_id: engine1.telemetryId,
new_engine_id: "other-engine2",
new_display_name: "engine2",
@ -319,7 +319,7 @@ add_task(async function test_setPrivateDefaultEngine() {
category: "search.engine.private",
name: "changed",
extra: {
change_source: "user",
change_reason: "user",
previous_engine_id: engine2.telemetryId,
new_engine_id: "other-engine1",
new_display_name: "engine1",

View file

@ -97,6 +97,8 @@ skip-if = ["verify"]
["browser_privatebrowsing_opendir.js"]
["browser_privatebrowsing_permissions.js"]
["browser_privatebrowsing_placesTitleNoUpdate.js"]
["browser_privatebrowsing_placestitle.js"]

View file

@ -0,0 +1,99 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that permissions don't persist across private browsing sessions by
// opening a new pbm window, gaining a notification permission, reopening the
// pbm window, and then checking the notification permission.
const TEST_URL = "https://example.com";
const TEST_PRINCIPAL = Services.scriptSecurityManager.createContentPrincipal(
Services.io.newURI(TEST_URL),
{ privateBrowsingId: 1 }
);
async function checkNotificationPermission(tab, isAllowExpected) {
// Check permission manager
let permission = Services.perms.testExactPermissionFromPrincipal(
TEST_PRINCIPAL,
"desktop-notification"
);
is(
permission,
isAllowExpected
? Services.perms.ALLOW_ACTION
: Services.perms.UNKNOWN_ACTION,
`Permission ${isAllowExpected ? "should" : "should not"} exist in permission manager`
);
// Check Notification API directly
await SpecialPowers.spawn(tab, [isAllowExpected], _isAllowExpected => {
is(
content.Notification.permission,
_isAllowExpected ? "granted" : "default",
`The notification API ${_isAllowExpected ? "should" : "should not"} allow notification`
);
});
}
add_task(async () => {
// Explicit user interaction would make this test more complicated than
// nescessary
await SpecialPowers.pushPrefEnv({
set: [["dom.webnotifications.requireuserinteraction", false]],
});
// Create a private browsing window
let privateWindow = await BrowserTestUtils.openNewBrowserWindow({
private: true,
});
let privateTab = privateWindow.gBrowser.selectedBrowser;
info("Checking permissions before test");
await checkNotificationPermission(privateTab, false);
// Open test site
BrowserTestUtils.startLoadingURIString(privateTab, TEST_URL);
await BrowserTestUtils.browserLoaded(privateTab);
// Gain "notification" permission on test site in private tab
let popupShown = BrowserTestUtils.waitForEvent(
privateWindow.PopupNotifications.panel,
"popupshown"
);
await SpecialPowers.spawn(privateTab, [], () => {
content.Notification.requestPermission();
});
await popupShown;
let notification = privateWindow.PopupNotifications.panel.firstElementChild;
let popupHidden = BrowserTestUtils.waitForEvent(
privateWindow.PopupNotifications.panel,
"popuphidden"
);
notification.button.click();
await popupHidden;
info("Checking permissions after permission was granted");
await checkNotificationPermission(privateTab, true);
// Close private window
await BrowserTestUtils.closeWindow(privateWindow);
// Open new private window
privateWindow = await BrowserTestUtils.openNewBrowserWindow({
private: true,
});
privateTab = privateWindow.gBrowser.selectedBrowser;
// Open example site again
BrowserTestUtils.startLoadingURIString(privateTab, TEST_URL);
await BrowserTestUtils.browserLoaded(privateTab);
info("Checking permissions after pbm window got reopened");
await checkNotificationPermission(privateTab, false);
// Close private window again
await BrowserTestUtils.closeWindow(privateWindow);
});

View file

@ -17,5 +17,9 @@ prefs = [
["test_selectable_profile_service_exists.js"]
["test_selectable_profiles_lifecycle.js"]
skip-if = [
"os == 'mac' && os_version == '10.15' && processor == 'x86_64'", # Bug 1952514
"os == 'mac' && os_version == '14.70' && processor == 'x86_64'", # Bug 1952514
]
["test_shared_prefs_lifecycles_methods.js"]

View file

@ -0,0 +1,483 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const PREFS_CHANGING_CATEGORY = new Set([
"network.cookie.cookieBehavior",
"network.cookie.cookieBehavior.pbmode",
"network.http.referer.disallowCrossSiteRelaxingDefault",
"network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation",
"privacy.partition.network_state.ocsp_cache",
"privacy.query_stripping.enabled",
"privacy.query_stripping.enabled.pbmode",
"privacy.fingerprintingProtection",
"privacy.fingerprintingProtection.pbmode",
]);
/**
* @class ContentBlockingPrefs
*
* Manages how the content blocking and anti-tracking preferences relate to the
* broad Tracking Protection categories (standard, strict and custom).
*
* @typedef {"standard"|"strict"|"custom"} CBCategory
*/
export let ContentBlockingPrefs = {
PREF_CB_CATEGORY: "browser.contentblocking.category",
PREF_STRICT_DEF: "browser.contentblocking.features.strict",
switchingCategory: false,
setPrefExpectations() {
// The prefs inside CATEGORY_PREFS are initial values.
// If the pref remains null, then it will expect the default value.
// The "standard" category is defined as expecting default values of the
// listed prefs. The "strict" category lists all prefs that will be set
// according to the strict feature pref.
this.CATEGORY_PREFS = {
strict: {
"network.cookie.cookieBehavior": null,
"network.cookie.cookieBehavior.pbmode": null,
"privacy.trackingprotection.pbmode.enabled": null,
"privacy.trackingprotection.enabled": null,
"privacy.trackingprotection.socialtracking.enabled": null,
"privacy.trackingprotection.fingerprinting.enabled": null,
"privacy.trackingprotection.cryptomining.enabled": null,
"privacy.trackingprotection.emailtracking.enabled": null,
"privacy.trackingprotection.emailtracking.pbmode.enabled": null,
"privacy.trackingprotection.consentmanager.skip.enabled": null,
"privacy.trackingprotection.consentmanager.skip.pbmode.enabled": null,
"privacy.annotate_channels.strict_list.enabled": null,
"network.http.referer.disallowCrossSiteRelaxingDefault": null,
"network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation":
null,
"privacy.partition.network_state.ocsp_cache": null,
"privacy.query_stripping.enabled": null,
"privacy.query_stripping.enabled.pbmode": null,
"privacy.fingerprintingProtection": null,
"privacy.fingerprintingProtection.pbmode": null,
"network.cookie.cookieBehavior.optInPartitioning": null,
"privacy.bounceTrackingProtection.mode": null,
},
standard: {
"network.cookie.cookieBehavior": null,
"network.cookie.cookieBehavior.pbmode": null,
"privacy.trackingprotection.pbmode.enabled": null,
"privacy.trackingprotection.enabled": null,
"privacy.trackingprotection.socialtracking.enabled": null,
"privacy.trackingprotection.fingerprinting.enabled": null,
"privacy.trackingprotection.cryptomining.enabled": null,
"privacy.trackingprotection.emailtracking.enabled": null,
"privacy.trackingprotection.emailtracking.pbmode.enabled": null,
"privacy.trackingprotection.consentmanager.skip.enabled": null,
"privacy.trackingprotection.consentmanager.skip.pbmode.enabled": null,
"privacy.annotate_channels.strict_list.enabled": null,
"network.http.referer.disallowCrossSiteRelaxingDefault": null,
"network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation":
null,
"privacy.partition.network_state.ocsp_cache": null,
"privacy.query_stripping.enabled": null,
"privacy.query_stripping.enabled.pbmode": null,
"privacy.fingerprintingProtection": null,
"privacy.fingerprintingProtection.pbmode": null,
"network.cookie.cookieBehavior.optInPartitioning": null,
"privacy.bounceTrackingProtection.mode": null,
},
};
let type = "strict";
let rulesArray = Services.prefs
.getStringPref(this.PREF_STRICT_DEF)
.split(",");
for (let item of rulesArray) {
switch (item) {
case "tp":
this.CATEGORY_PREFS[type]["privacy.trackingprotection.enabled"] =
true;
break;
case "-tp":
this.CATEGORY_PREFS[type]["privacy.trackingprotection.enabled"] =
false;
break;
case "tpPrivate":
this.CATEGORY_PREFS[type][
"privacy.trackingprotection.pbmode.enabled"
] = true;
break;
case "-tpPrivate":
this.CATEGORY_PREFS[type][
"privacy.trackingprotection.pbmode.enabled"
] = false;
break;
case "fp":
this.CATEGORY_PREFS[type][
"privacy.trackingprotection.fingerprinting.enabled"
] = true;
break;
case "-fp":
this.CATEGORY_PREFS[type][
"privacy.trackingprotection.fingerprinting.enabled"
] = false;
break;
case "cryptoTP":
this.CATEGORY_PREFS[type][
"privacy.trackingprotection.cryptomining.enabled"
] = true;
break;
case "-cryptoTP":
this.CATEGORY_PREFS[type][
"privacy.trackingprotection.cryptomining.enabled"
] = false;
break;
case "stp":
this.CATEGORY_PREFS[type][
"privacy.trackingprotection.socialtracking.enabled"
] = true;
break;
case "-stp":
this.CATEGORY_PREFS[type][
"privacy.trackingprotection.socialtracking.enabled"
] = false;
break;
case "emailTP":
this.CATEGORY_PREFS[type][
"privacy.trackingprotection.emailtracking.enabled"
] = true;
break;
case "-emailTP":
this.CATEGORY_PREFS[type][
"privacy.trackingprotection.emailtracking.enabled"
] = false;
break;
case "emailTPPrivate":
this.CATEGORY_PREFS[type][
"privacy.trackingprotection.emailtracking.pbmode.enabled"
] = true;
break;
case "-emailTPPrivate":
this.CATEGORY_PREFS[type][
"privacy.trackingprotection.emailtracking.pbmode.enabled"
] = false;
break;
case "consentmanagerSkip":
this.CATEGORY_PREFS[type][
"privacy.trackingprotection.consentmanager.skip.enabled"
] = true;
break;
case "-consentmanagerSkip":
this.CATEGORY_PREFS[type][
"privacy.trackingprotection.consentmanager.skip.enabled"
] = false;
break;
case "consentmanagerSkipPrivate":
this.CATEGORY_PREFS[type][
"privacy.trackingprotection.consentmanager.skip.pbmode.enabled"
] = true;
break;
case "-consentmanagerSkipPrivate":
this.CATEGORY_PREFS[type][
"privacy.trackingprotection.consentmanager.skip.pbmode.enabled"
] = false;
break;
case "lvl2":
this.CATEGORY_PREFS[type][
"privacy.annotate_channels.strict_list.enabled"
] = true;
break;
case "-lvl2":
this.CATEGORY_PREFS[type][
"privacy.annotate_channels.strict_list.enabled"
] = false;
break;
case "rp":
this.CATEGORY_PREFS[type][
"network.http.referer.disallowCrossSiteRelaxingDefault"
] = true;
break;
case "-rp":
this.CATEGORY_PREFS[type][
"network.http.referer.disallowCrossSiteRelaxingDefault"
] = false;
break;
case "rpTop":
this.CATEGORY_PREFS[type][
"network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation"
] = true;
break;
case "-rpTop":
this.CATEGORY_PREFS[type][
"network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation"
] = false;
break;
case "ocsp":
this.CATEGORY_PREFS[type][
"privacy.partition.network_state.ocsp_cache"
] = true;
break;
case "-ocsp":
this.CATEGORY_PREFS[type][
"privacy.partition.network_state.ocsp_cache"
] = false;
break;
case "qps":
this.CATEGORY_PREFS[type]["privacy.query_stripping.enabled"] = true;
break;
case "-qps":
this.CATEGORY_PREFS[type]["privacy.query_stripping.enabled"] = false;
break;
case "qpsPBM":
this.CATEGORY_PREFS[type]["privacy.query_stripping.enabled.pbmode"] =
true;
break;
case "-qpsPBM":
this.CATEGORY_PREFS[type]["privacy.query_stripping.enabled.pbmode"] =
false;
break;
case "fpp":
this.CATEGORY_PREFS[type]["privacy.fingerprintingProtection"] = true;
break;
case "-fpp":
this.CATEGORY_PREFS[type]["privacy.fingerprintingProtection"] = false;
break;
case "fppPrivate":
this.CATEGORY_PREFS[type]["privacy.fingerprintingProtection.pbmode"] =
true;
break;
case "-fppPrivate":
this.CATEGORY_PREFS[type]["privacy.fingerprintingProtection.pbmode"] =
false;
break;
case "cookieBehavior0":
this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] =
Ci.nsICookieService.BEHAVIOR_ACCEPT;
break;
case "cookieBehavior1":
this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] =
Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN;
break;
case "cookieBehavior2":
this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] =
Ci.nsICookieService.BEHAVIOR_REJECT;
break;
case "cookieBehavior3":
this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] =
Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN;
break;
case "cookieBehavior4":
this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] =
Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
break;
case "cookieBehavior5":
this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] =
Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
break;
case "cookieBehaviorPBM0":
this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] =
Ci.nsICookieService.BEHAVIOR_ACCEPT;
break;
case "cookieBehaviorPBM1":
this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] =
Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN;
break;
case "cookieBehaviorPBM2":
this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] =
Ci.nsICookieService.BEHAVIOR_REJECT;
break;
case "cookieBehaviorPBM3":
this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] =
Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN;
break;
case "cookieBehaviorPBM4":
this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] =
Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
break;
case "cookieBehaviorPBM5":
this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] =
Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
break;
case "3pcd":
this.CATEGORY_PREFS[type][
"network.cookie.cookieBehavior.optInPartitioning"
] = true;
break;
case "-3pcd":
this.CATEGORY_PREFS[type][
"network.cookie.cookieBehavior.optInPartitioning"
] = false;
break;
case "btp":
this.CATEGORY_PREFS[type]["privacy.bounceTrackingProtection.mode"] =
Ci.nsIBounceTrackingProtection.MODE_ENABLED;
break;
case "-btp":
// We currently consider MODE_ENABLED_DRY_RUN the "off" state. See
// nsIBounceTrackingProtection.idl for details.
this.CATEGORY_PREFS[type]["privacy.bounceTrackingProtection.mode"] =
Ci.nsIBounceTrackingProtection.MODE_ENABLED_DRY_RUN;
break;
default:
console.error(`Error: Unknown rule observed ${item}`);
}
}
},
/**
* Checks if CB prefs match perfectly with one of our pre-defined categories.
*
* @param {CBCategory} category
*/
prefsMatch(category) {
// The category pref must be either unset, or match.
if (
Services.prefs.prefHasUserValue(this.PREF_CB_CATEGORY) &&
Services.prefs.getStringPref(this.PREF_CB_CATEGORY) != category
) {
return false;
}
for (let pref in this.CATEGORY_PREFS[category]) {
let value = this.CATEGORY_PREFS[category][pref];
if (value == null) {
if (Services.prefs.prefHasUserValue(pref)) {
return false;
}
} else {
let prefType = Services.prefs.getPrefType(pref);
if (
(prefType == Services.prefs.PREF_BOOL &&
Services.prefs.getBoolPref(pref) != value) ||
(prefType == Services.prefs.PREF_INT &&
Services.prefs.getIntPref(pref) != value) ||
(prefType == Services.prefs.PREF_STRING &&
Services.prefs.getStringPref(pref) != value)
) {
return false;
}
}
}
return true;
},
matchCBCategory() {
if (this.switchingCategory) {
return;
}
// If PREF_CB_CATEGORY is not set match users to a Content Blocking category. Check if prefs fit
// perfectly into strict or standard, otherwise match with custom. If PREF_CB_CATEGORY has previously been set,
// a change of one of these prefs necessarily puts us in "custom".
if (this.prefsMatch("standard")) {
Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "standard");
} else if (this.prefsMatch("strict")) {
Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "strict");
} else {
Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "custom");
}
// If there is a custom policy which changes a related pref, then put the user in custom so
// they still have access to other content blocking prefs, and to keep our default definitions
// from changing.
let policy = Services.policies.getActivePolicies();
if (policy && (policy.EnableTrackingProtection || policy.Cookies)) {
Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "custom");
}
},
updateCBCategory() {
if (
this.switchingCategory ||
!Services.prefs.prefHasUserValue(this.PREF_CB_CATEGORY)
) {
return;
}
// Turn on switchingCategory flag, to ensure that when the individual prefs that change as a result
// of the category change do not trigger yet another category change.
this.switchingCategory = true;
let value = Services.prefs.getStringPref(this.PREF_CB_CATEGORY);
this.setPrefsToCategory(value);
this.switchingCategory = false;
},
/**
* Sets all user-exposed content blocking preferences to values that match the selected category.
*
* @param {CBCategory} category
*/
setPrefsToCategory(category) {
// Leave prefs as they were if we are switching to "custom" category.
if (category == "custom") {
return;
}
for (let pref in this.CATEGORY_PREFS[category]) {
let value = this.CATEGORY_PREFS[category][pref];
if (!Services.prefs.prefIsLocked(pref)) {
if (value == null) {
Services.prefs.clearUserPref(pref);
} else {
switch (Services.prefs.getPrefType(pref)) {
case Services.prefs.PREF_BOOL:
Services.prefs.setBoolPref(pref, value);
break;
case Services.prefs.PREF_INT:
Services.prefs.setIntPref(pref, value);
break;
case Services.prefs.PREF_STRING:
Services.prefs.setStringPref(pref, value);
break;
}
}
}
}
},
setPrefExpectationsAndUpdate() {
this.setPrefExpectations();
this.updateCBCategory();
},
observe(subject, topic, data) {
if (topic != "nsPref:changed") {
return;
}
if (
data.startsWith("privacy.trackingprotection") ||
PREFS_CHANGING_CATEGORY.has(data)
) {
this.matchCBCategory();
}
if (data.startsWith("privacy.trackingprotection")) {
this.setPrefExpectations();
} else if (data == this.PREF_CB_CATEGORY) {
this.updateCBCategory();
} else if (data == "browser.contentblocking.features.strict") {
this.setPrefExpectationsAndUpdate();
}
},
init() {
this.setPrefExpectationsAndUpdate();
this.matchCBCategory();
for (let prefix of PREF_PREFIXES_TO_OBSERVE) {
Services.prefs.addObserver(prefix, this);
}
},
uninit() {
for (let prefix of PREF_PREFIXES_TO_OBSERVE) {
Services.prefs.removeObserver(prefix, this);
}
},
};
const PREF_PREFIXES_TO_OBSERVE = new Set([
"privacy.trackingprotection",
"network.cookie.cookieBehavior",
"network.http.referer.disallowCrossSiteRelaxingDefault",
"privacy.partition.network_state.ocsp_cache",
"privacy.query_stripping.enabled",
"privacy.fingerprintingProtection",
ContentBlockingPrefs.PREF_CB_CATEGORY,
ContentBlockingPrefs.PREF_STRICT_DEF,
]);
ContentBlockingPrefs.QueryInterface = ChromeUtils.generateQI([Ci.nsIObserver]);

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