Update On Wed Apr 9 15:26:42 CEST 2025

This commit is contained in:
github-action[bot] 2025-04-09 15:26:42 +02:00
parent 9798769c58
commit 35b863fd86
1555 changed files with 67352 additions and 39669 deletions

269
Cargo.lock generated
View file

@ -16,18 +16,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.0"
@ -189,38 +177,6 @@ dependencies = [
"libc",
]
[[package]]
name = "askama"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47cbc3cf73fa8d9833727bbee4835ba5c421a0d65b72daf9a7b5d0e0f9cfb57e"
dependencies = [
"askama_derive",
"askama_escape",
]
[[package]]
name = "askama_derive"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c22fbe0413545c098358e56966ff22cdd039e10215ae213cfbd65032b119fc94"
dependencies = [
"basic-toml",
"mime",
"mime_guess",
"nom",
"proc-macro2",
"quote",
"serde",
"syn",
]
[[package]]
name = "askama_escape"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
[[package]]
name = "async-task"
version = "4.3.0"
@ -467,7 +423,7 @@ dependencies = [
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"rustc-hash 1.999.999",
"shlex",
"syn",
]
@ -1836,7 +1792,7 @@ dependencies = [
[[package]]
name = "error-support"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=d773da92641d92930b7308300e9fc2746a05ce6a#d773da92641d92930b7308300e9fc2746a05ce6a"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
dependencies = [
"error-support-macros",
"lazy_static",
@ -1848,7 +1804,7 @@ dependencies = [
[[package]]
name = "error-support-macros"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=d773da92641d92930b7308300e9fc2746a05ce6a#d773da92641d92930b7308300e9fc2746a05ce6a"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
dependencies = [
"proc-macro2",
"quote",
@ -1965,7 +1921,7 @@ dependencies = [
[[package]]
name = "firefox-versioning"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=d773da92641d92930b7308300e9fc2746a05ce6a#d773da92641d92930b7308300e9fc2746a05ce6a"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
dependencies = [
"serde_json",
"thiserror 1.999.999",
@ -2017,7 +1973,7 @@ dependencies = [
"fluent-syntax",
"intl-memoizer",
"intl_pluralrules",
"rustc-hash",
"rustc-hash 1.999.999",
"self_cell",
"smallvec",
"unic-langid",
@ -2034,7 +1990,7 @@ dependencies = [
"fluent-bundle",
"futures",
"once_cell",
"rustc-hash",
"rustc-hash 1.999.999",
"unic-langid",
]
@ -2128,6 +2084,12 @@ dependencies = [
"xpcom",
]
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foreign-types"
version = "0.5.0"
@ -2618,9 +2580,9 @@ dependencies = [
[[package]]
name = "glean"
version = "63.1.0"
version = "64.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2afa6754943cac5243099efd0d26e89cc8e06f1585776ba14ab0c6ee99e1f71"
checksum = "251b9cb685554b96dcf785dba69ce90447006dd6d9229db783336c981c3777e1"
dependencies = [
"crossbeam-channel",
"glean-core",
@ -2632,9 +2594,9 @@ dependencies = [
[[package]]
name = "glean-core"
version = "63.1.0"
version = "64.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53cd53bb7a3b89b17d3989e95dd808b137ff47c504d1d19f14cb0d820cc2f42e"
checksum = "a49d1d62648ddeed8cb996373046ea45de93f1d1ff956aba054b9304bc305753"
dependencies = [
"android_logger",
"bincode",
@ -2770,7 +2732,7 @@ checksum = "9c08c1f623a8d0b722b8b99f821eb0ba672a1618f0d3b16ddbee1cedd2dd8557"
dependencies = [
"bitflags 2.9.0",
"gpu-descriptor-types",
"hashbrown 0.14.5",
"hashbrown 0.14.999",
]
[[package]]
@ -2843,17 +2805,25 @@ dependencies = [
name = "hashbrown"
version = "0.13.999"
dependencies = [
"hashbrown 0.14.5",
"hashbrown 0.15.2",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
version = "0.14.999"
dependencies = [
"hashbrown 0.15.2",
]
[[package]]
name = "hashbrown"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
dependencies = [
"ahash",
"allocator-api2",
"equivalent",
"foldhash",
"serde",
]
@ -2863,7 +2833,7 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
dependencies = [
"hashbrown 0.14.5",
"hashbrown 0.14.999",
]
[[package]]
@ -3241,12 +3211,12 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.5.0"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
dependencies = [
"equivalent",
"hashbrown 0.14.5",
"hashbrown 0.15.2",
"serde",
]
@ -3264,7 +3234,7 @@ dependencies = [
[[package]]
name = "interrupt-support"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=d773da92641d92930b7308300e9fc2746a05ce6a#d773da92641d92930b7308300e9fc2746a05ce6a"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
dependencies = [
"lazy_static",
"parking_lot",
@ -3346,9 +3316,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.5"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jexl-eval"
@ -3484,7 +3454,7 @@ dependencies = [
"futures",
"pin-project-lite",
"replace_with",
"rustc-hash",
"rustc-hash 1.999.999",
"unic-langid",
]
@ -4536,12 +4506,12 @@ dependencies = [
"cfg_aliases",
"codespan-reporting",
"half 2.5.0",
"hashbrown 0.14.5",
"hashbrown 0.14.999",
"hexf-parse",
"indexmap",
"log",
"num-traits",
"rustc-hash",
"rustc-hash 1.999.999",
"serde",
"spirv",
"strum",
@ -4970,7 +4940,7 @@ checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
[[package]]
name = "payload-support"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=d773da92641d92930b7308300e9fc2746a05ce6a#d773da92641d92930b7308300e9fc2746a05ce6a"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
dependencies = [
"serde",
"serde_derive",
@ -5107,6 +5077,14 @@ dependencies = [
"bindgen 0.69.4",
]
[[package]]
name = "pkcs11testmodule-static"
version = "0.1.0"
dependencies = [
"mozilla-central-workspace-hack",
"pkcs11-bindings",
]
[[package]]
name = "pkg-config"
version = "0.3.26"
@ -5466,7 +5444,7 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
[[package]]
name = "relevancy"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=d773da92641d92930b7308300e9fc2746a05ce6a#d773da92641d92930b7308300e9fc2746a05ce6a"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
dependencies = [
"anyhow",
"base64 0.21.999",
@ -5491,7 +5469,7 @@ dependencies = [
[[package]]
name = "remote_settings"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=d773da92641d92930b7308300e9fc2746a05ce6a#d773da92641d92930b7308300e9fc2746a05ce6a"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
dependencies = [
"anyhow",
"camino",
@ -5536,6 +5514,45 @@ dependencies = [
"cache-padded",
]
[[package]]
name = "rinja"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5"
dependencies = [
"itoa",
"rinja_derive",
]
[[package]]
name = "rinja_derive"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d9ed0146aef6e2825f1b1515f074510549efba38d71f4554eec32eb36ba18b"
dependencies = [
"basic-toml",
"memchr",
"mime",
"mime_guess",
"proc-macro2",
"quote",
"rinja_parser",
"rustc-hash 2.1.1",
"serde",
"syn",
]
[[package]]
name = "rinja_parser"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f9a866e2e00a7a1fb27e46e9e324a6f7c0e7edc4543cae1d38f4e4a100c610"
dependencies = [
"memchr",
"nom",
"serde",
]
[[package]]
name = "rkv"
version = "0.19.0"
@ -5688,9 +5705,16 @@ checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "rustc-hash"
version = "1.1.0"
version = "1.999.999"
dependencies = [
"rustc-hash 2.1.1",
]
[[package]]
name = "rustc-hash"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]]
name = "rustc_version"
@ -5776,7 +5800,7 @@ dependencies = [
[[package]]
name = "search"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=d773da92641d92930b7308300e9fc2746a05ce6a#d773da92641d92930b7308300e9fc2746a05ce6a"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
dependencies = [
"error-support",
"firefox-versioning",
@ -6066,7 +6090,7 @@ dependencies = [
[[package]]
name = "sql-support"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=d773da92641d92930b7308300e9fc2746a05ce6a#d773da92641d92930b7308300e9fc2746a05ce6a"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
dependencies = [
"interrupt-support",
"lazy_static",
@ -6265,7 +6289,7 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "suggest"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=d773da92641d92930b7308300e9fc2746a05ce6a#d773da92641d92930b7308300e9fc2746a05ce6a"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
dependencies = [
"anyhow",
"chrono",
@ -6317,7 +6341,7 @@ dependencies = [
[[package]]
name = "sync-guid"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=d773da92641d92930b7308300e9fc2746a05ce6a#d773da92641d92930b7308300e9fc2746a05ce6a"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
dependencies = [
"base64 0.21.999",
"rand",
@ -6328,7 +6352,7 @@ dependencies = [
[[package]]
name = "sync15"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=d773da92641d92930b7308300e9fc2746a05ce6a#d773da92641d92930b7308300e9fc2746a05ce6a"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
dependencies = [
"anyhow",
"error-support",
@ -6368,7 +6392,7 @@ dependencies = [
[[package]]
name = "tabs"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=d773da92641d92930b7308300e9fc2746a05ce6a#d773da92641d92930b7308300e9fc2746a05ce6a"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
dependencies = [
"anyhow",
"error-support",
@ -6694,7 +6718,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46"
dependencies = [
"rustc-hash",
"rustc-hash 1.999.999",
]
[[package]]
@ -6712,7 +6736,7 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "types"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=d773da92641d92930b7308300e9fc2746a05ce6a#d773da92641d92930b7308300e9fc2746a05ce6a"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
dependencies = [
"rusqlite",
"serde",
@ -6802,9 +6826,9 @@ checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
[[package]]
name = "uniffi"
version = "0.28.2"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51ce6280c581045879e11b400bae14686a819df22b97171215d15549efa04ddb"
checksum = "fe34585ac0275accf6c284d0080cc2840f3898c551cda869ec291b5a4218712c"
dependencies = [
"anyhow",
"cargo_metadata",
@ -6819,12 +6843,12 @@ name = "uniffi-bindgen-gecko-js"
version = "0.1.0"
dependencies = [
"anyhow",
"askama",
"camino",
"cargo_metadata",
"clap",
"extend",
"heck",
"rinja",
"serde",
"textwrap",
"toml",
@ -6924,12 +6948,11 @@ dependencies = [
[[package]]
name = "uniffi_bindgen"
version = "0.28.2"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e9f25730c9db2e878521d606f54e921edb719cdd94d735e7f97705d6796d024"
checksum = "1a792af1424cc8b3c43b44c1a6cb7935ed1fbe5584a74f70e8bab9799740266d"
dependencies = [
"anyhow",
"askama",
"camino",
"cargo_metadata",
"fs-err",
@ -6938,6 +6961,7 @@ dependencies = [
"heck",
"once_cell",
"paste",
"rinja",
"serde",
"textwrap",
"toml",
@ -6948,9 +6972,9 @@ dependencies = [
[[package]]
name = "uniffi_build"
version = "0.28.2"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88dba57ac699bd8ec53d6a352c8dd0e479b33f698c5659831bb1e4ce468c07bd"
checksum = "00c4138211f2ae951018fcce6a978e1fcd1a47c3fd0bc0d5472a520520060db1"
dependencies = [
"anyhow",
"camino",
@ -6958,36 +6982,33 @@ dependencies = [
]
[[package]]
name = "uniffi_checksum_derive"
version = "0.28.2"
name = "uniffi_core"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c801f0f05b06df456a2da4c41b9c2c4fdccc6b9916643c6c67275c4c9e4d07"
checksum = "c18baace68a52666d33d12d73ca335ecf27a302202cefb53b1f974512bb72417"
dependencies = [
"anyhow",
"bytes",
"once_cell",
"static_assertions",
]
[[package]]
name = "uniffi_internal_macros"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9902d4ed16c65e6c0222241024dd0bfeed07ea3deb7c470eb175e5f5ef406cd"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "uniffi_core"
version = "0.28.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61049e4db6212d0ede80982adf0e1d6fa224e6118387324c5cfbe3083dfb2252"
dependencies = [
"anyhow",
"bytes",
"log",
"once_cell",
"paste",
"static_assertions",
]
[[package]]
name = "uniffi_macros"
version = "0.28.2"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b40fd2249e0c5dcbd2bfa3c263db1ec981f7273dca7f4132bf06a272359a586c"
checksum = "9d82c82ef945c51082d8763635334b994e63e77650f09d0fae6d28dd08b1de83"
dependencies = [
"bincode",
"camino",
"fs-err",
"once_cell",
@ -7001,21 +7022,20 @@ dependencies = [
[[package]]
name = "uniffi_meta"
version = "0.28.2"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9ad57039b4fafdbf77428d74fff40e0908e5a1731e023c19cfe538f6d4a8ed6"
checksum = "8d6027b971c2aa86350dd180aee9819729c7b99bacd381534511ff29d2c09cea"
dependencies = [
"anyhow",
"bytes",
"siphasher",
"uniffi_checksum_derive",
"uniffi_internal_macros",
]
[[package]]
name = "uniffi_testing"
version = "0.28.2"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21fa171d4d258dc51bbd01893cc9608c1b62273d2f9ea55fb64f639e77824567"
checksum = "6301bcb50098dabcd304485318ba73f0f4db5e5d9d3c385c60b967810344ce90"
dependencies = [
"anyhow",
"camino",
@ -7026,14 +7046,13 @@ dependencies = [
[[package]]
name = "uniffi_udl"
version = "0.28.2"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52299e247419e7e2934bef2f94d7cccb0e6566f3248b1d48b160d8f369a2668"
checksum = "52300b7a4ab02dc159a038a13d5bfe27aefbad300d91b0b501b3dda094c1e0a2"
dependencies = [
"anyhow",
"textwrap",
"uniffi_meta",
"uniffi_testing",
"weedle2",
]
@ -7099,7 +7118,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "viaduct"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=d773da92641d92930b7308300e9fc2746a05ce6a#d773da92641d92930b7308300e9fc2746a05ce6a"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
dependencies = [
"ffi-support",
"log",
@ -7269,7 +7288,7 @@ dependencies = [
[[package]]
name = "webext-storage"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=d773da92641d92930b7308300e9fc2746a05ce6a#d773da92641d92930b7308300e9fc2746a05ce6a"
source = "git+https://github.com/mozilla/application-services?rev=8e84c588a5cc2686973c5026ecd240d6275d7e94#8e84c588a5cc2686973c5026ecd240d6275d7e94"
dependencies = [
"anyhow",
"error-support",
@ -7417,7 +7436,7 @@ dependencies = [
"bytemuck",
"cfg_aliases",
"document-features",
"hashbrown 0.14.5",
"hashbrown 0.14.999",
"indexmap",
"log",
"naga",
@ -7425,7 +7444,7 @@ dependencies = [
"parking_lot",
"profiling",
"ron",
"rustc-hash",
"rustc-hash 1.999.999",
"serde",
"smallvec",
"thiserror 2.0.9",
@ -7468,7 +7487,7 @@ dependencies = [
"gpu-alloc",
"gpu-allocator",
"gpu-descriptor",
"hashbrown 0.14.5",
"hashbrown 0.14.999",
"libc",
"libloading",
"log",

View file

@ -11,8 +11,9 @@ members = [
"netwerk/test/http3server",
"security/manager/ssl/ipcclientcerts",
"security/manager/ssl/osclientcerts",
"security/manager/ssl/trust_anchors",
"security/manager/ssl/tests/unit/pkcs11testmodule",
"security/manager/ssl/tests/unit/test_trust_anchors",
"security/manager/ssl/trust_anchors",
"security/mls/mls_gk",
"testing/geckodriver",
"toolkit/components/uniffi-bindgen-gecko-js",
@ -61,12 +62,12 @@ rust-version = "1.82.0"
[workspace.dependencies]
# Shared across multiple UniFFI consumers.
uniffi = "0.28.2"
uniffi_bindgen = "0.28.2"
uniffi = "0.29.1"
uniffi_bindgen = "0.29.1"
# Shared across multiple application-services consumers.
rusqlite = "0.31.0"
# Shared across multiple glean consumers.
glean = "=63.1.0"
glean = "=64.0.1"
# Explicitly specify what our profiles use. The opt-level setting here is
# a total fiction; see the setup of MOZ_RUST_DEFAULT_FLAGS for what the
@ -176,11 +177,17 @@ goblin = { path = "build/rust/goblin" }
# Implement getrandom 0.2 in terms of 0.3
getrandom = { path = "build/rust/getrandom" }
# Patch rustc-hash 1.1.0 to 2.1.1
rustc-hash = { path = "build/rust/rustc-hash" }
# Patch memoffset from 0.8.0 to 0.9.0 since it's compatible and it avoids duplication
memoffset = { path = "build/rust/memoffset" }
# Patch `hashbrown` 0.12.* to depend on 0.14.*
hashbrown = { path = "build/rust/hashbrown" }
# Patch `hashbrown` 0.13.* to depend on 0.15.*
hashbrown_0_13 = { package = "hashbrown", path = "build/rust/hashbrown-0.13" }
# Patch `hashbrown` 0.14.* to depend on 0.15.*
hashbrown_0_14 = { package = "hashbrown", path = "build/rust/hashbrown" }
# Patch `socket2` 0.4 to 0.5
socket2 = { path = "build/rust/socket2" }
@ -244,14 +251,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 = "d773da92641d92930b7308300e9fc2746a05ce6a" }
relevancy = { git = "https://github.com/mozilla/application-services", rev = "d773da92641d92930b7308300e9fc2746a05ce6a" }
search = { git = "https://github.com/mozilla/application-services", rev = "d773da92641d92930b7308300e9fc2746a05ce6a" }
sql-support = { git = "https://github.com/mozilla/application-services", rev = "d773da92641d92930b7308300e9fc2746a05ce6a" }
suggest = { git = "https://github.com/mozilla/application-services", rev = "d773da92641d92930b7308300e9fc2746a05ce6a" }
sync15 = { git = "https://github.com/mozilla/application-services", rev = "d773da92641d92930b7308300e9fc2746a05ce6a" }
tabs = { git = "https://github.com/mozilla/application-services", rev = "d773da92641d92930b7308300e9fc2746a05ce6a" }
viaduct = { git = "https://github.com/mozilla/application-services", rev = "d773da92641d92930b7308300e9fc2746a05ce6a" }
webext-storage = { git = "https://github.com/mozilla/application-services", rev = "d773da92641d92930b7308300e9fc2746a05ce6a" }
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" }
allocator-api2 = { path = "third_party/rust/allocator-api2" }

View file

@ -1264,9 +1264,10 @@ already_AddRefed<AccAttributes> LocalAccessible::NativeAttributes() {
auto GetMargin = [&](mozilla::Side aSide) -> CSSCoord {
// This is here only to guarantee that we do the same as getComputedStyle
// does, so that we don't hit precision errors in tests.
const auto& margin = f->StyleMargin()->GetMargin(aSide);
if (margin.ConvertsToLength()) {
return margin.AsLengthPercentage().ToLengthInCSSPixels();
const auto margin =
f->StyleMargin()->GetMargin(aSide, f->StyleDisplay()->mPosition);
if (margin->ConvertsToLength()) {
return margin->AsLengthPercentage().ToLengthInCSSPixels();
}
nscoord coordVal = f->GetUsedMargin().Side(aSide);

View file

@ -43,16 +43,15 @@ support-files = [
["test_focus_autocomplete.xhtml"]
# Disabled on Linux and Windows due to frequent failures - bug 695019, bug 890795
skip-if = [
"os == 'win'",
"os == 'linux'",
"os == 'linux' && os_version == '18.04' && processor == 'x86_64'",
"os == 'linux' && os_version == '22.04' && processor == 'x86_64'",
"os == 'win' && os_version == '11.26100' && processor == 'x86_64'",
"os == 'win' && os_version == '11.26100' && processor == 'x86'",
]
["test_focus_canvas.html"]
["test_focus_contextmenu.xhtml"]
skip-if = [
"os == 'win' && os_version == '11.26100' && opt", # Bug 1865763
]
["test_focus_controls.html"]
@ -73,7 +72,6 @@ skip-if = [
["test_focus_selects.html"]
["test_focus_tabbox.xhtml"]
skip-if = ["true"]
["test_focus_tree.xhtml"]
@ -97,13 +95,10 @@ skip-if = ["true"]
["test_selection.html"]
skip-if = [
"os == 'mac'",
"os == 'mac' && os_version == '14.70' && processor == 'x86_64'",
]
["test_selection.xhtml"]
skip-if = [
"os == 'mac'",
]
["test_selection_aria.html"]
@ -123,5 +118,5 @@ skip-if = [
["test_valuechange.html"]
skip-if = [
"os == 'mac'",
"os == 'mac' && os_version == '14.70' && processor == 'x86_64'",
]

View file

@ -12,10 +12,11 @@ support-files = [
["test_docload_iframe.html"]
["test_docload_root.html"]
skip-if = ["os == 'mac'"] # bug 1456997
skip-if = [
"os == 'mac' && os_version == '14.70' && processor == 'x86_64'", # Bug 1456997
]
["test_docload_shutdown.html"]
skip-if = [
"os == 'mac'", # bug 1456997
"display == 'wayland'", # bug 1850412
"os == 'mac' && os_version == '14.70' && processor == 'x86_64'", # Bug 1456997
]

View file

@ -20,9 +20,6 @@ support-files = [
["test_list.html"]
["test_markup.html"]
skip-if = [
"win11_2009 && debug", # Bug 1296784
]
["test_svg.html"]

View file

@ -2,9 +2,6 @@
support-files = "!/accessible/tests/mochitest/*.js"
["test_embeds.xhtml"]
skip-if = [
"os == 'linux' && !debug", # bug 1411145
]
["test_general.html"]

View file

@ -55,6 +55,3 @@ support-files = [
["test_visibility.html"]
["test_visibility.xhtml"]
skip-if = [
"asan", # Bug 1199631
]

View file

@ -10,7 +10,13 @@ support-files = [
"!/dom/media/test/bug461281.ogg"]
["test_applicationacc.xhtml"]
skip-if = ["true"] # Bug 561508
skip-if = [
"os == 'linux' && os_version == '18.04' && processor == 'x86_64'", # Bug 561508
"os == 'linux' && os_version == '22.04' && processor == 'x86_64'", # Bug 561508
"os == 'mac' && os_version == '14.70' && processor == 'x86_64'", # Bug 561508
"os == 'win' && os_version == '11.26100' && processor == 'x86_64'", # Bug 561508
"os == 'win' && os_version == '11.26100' && processor == 'x86'", # Bug 561508
]
["test_aria_display_contents.html"]
@ -85,10 +91,6 @@ skip-if = ["true"] # Bug 561508
["test_tabbox.xhtml"]
["test_tabbrowser.xhtml"]
skip-if = [
"os == 'linux' && debug", # Bug 1389365
"os == 'win' && ccov", # Bug 1423218
]
["test_table.html"]

View file

@ -19,7 +19,9 @@
#endif
#endif
pref("browser.hiddenWindowChromeURL", "chrome://browser/content/hiddenWindowMac.xhtml");
#ifdef XP_MACOSX
pref("browser.hiddenWindowChromeURL", "chrome://browser/content/hiddenWindowMac.xhtml");
#endif
// Set add-ons abuse report related prefs specific to Firefox Desktop.
pref("extensions.abuseReport.enabled", true);
@ -383,8 +385,6 @@ pref("browser.theme.colorway-closet", true);
#if defined(MOZ_WIDGET_GTK)
pref("browser.theme.native-theme", true);
#elif defined(XP_MACOSX) && defined(NIGHTLY_BUILD)
pref("browser.theme.native-theme", true);
#else
pref("browser.theme.native-theme", false);
#endif
@ -1059,7 +1059,13 @@ pref("browser.tabs.groups.enabled", true);
#else
pref("browser.tabs.groups.enabled", false);
#endif
#ifdef NIGHTLY_BUILD
pref("browser.tabs.groups.smart.enabled", true);
#else
pref("browser.tabs.groups.smart.enabled", false);
#endif
pref("browser.tabs.groups.smart.optin", false);
pref("browser.tabs.dragDrop.createGroup.delayMS", 240);

View file

@ -375,6 +375,7 @@ var gBrowserInit = {
Services.obs.addObserver(gXPInstallObserver, "addon-install-failed");
Services.obs.addObserver(gXPInstallObserver, "addon-install-confirmation");
Services.obs.addObserver(gKeywordURIFixup, "keyword-uri-fixup");
Services.obs.addObserver(gLocaleChangeObserver, "intl:app-locales-changed");
BrowserOffline.init();
CanvasPermissionPromptHelper.init();
@ -1103,6 +1104,10 @@ var gBrowserInit = {
"addon-install-confirmation"
);
Services.obs.removeObserver(gKeywordURIFixup, "keyword-uri-fixup");
Services.obs.removeObserver(
gLocaleChangeObserver,
"intl:app-locales-changed"
);
MenuTouchModeObserver.uninit();
BrowserOffline.uninit();

View file

@ -219,12 +219,6 @@ var gIdentityHandler = {
"identity-popup-remove-cert-exception": () => {
this.removeCertException();
},
"identity-popup-disable-mixed-content-blocking": () => {
this.disableMixedContentProtection();
},
"identity-popup-enable-mixed-content-blocking": () => {
this.enableMixedContentProtection();
},
"identity-popup-more-info": event => {
this.handleMoreInfoClick(event);
},
@ -490,49 +484,6 @@ var gIdentityHandler = {
Services.focus.clearFocus(window);
},
disableMixedContentProtection() {
// Use telemetry to measure how often unblocking happens
const kMIXED_CONTENT_UNBLOCK_EVENT = 2;
Glean.mixedContent.unblockCounter.accumulateSingleSample(
kMIXED_CONTENT_UNBLOCK_EVENT
);
SitePermissions.setForPrincipal(
gBrowser.contentPrincipal,
"mixed-content",
SitePermissions.ALLOW,
SitePermissions.SCOPE_SESSION
);
// Reload the page with the content unblocked
BrowserCommands.reloadWithFlags(
Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE
);
if (this._popupInitialized) {
PanelMultiView.hidePopup(this._identityPopup);
}
},
// This is needed for some tests which need the permission reset, but which
// then reuse the browser and would race between the reload and the next
// load.
enableMixedContentProtectionNoReload() {
this.enableMixedContentProtection(false);
},
enableMixedContentProtection(reload = true) {
SitePermissions.removeFromPrincipal(
gBrowser.contentPrincipal,
"mixed-content"
);
if (reload) {
BrowserCommands.reload();
}
if (this._popupInitialized) {
PanelMultiView.hidePopup(this._identityPopup);
}
},
removeCertException() {
if (!this._uriHasHost) {
console.error(

View file

@ -341,6 +341,10 @@ if (AppConstants.ENABLE_WEBDRIVER) {
ChromeUtils.defineLazyGetter(this, "RTL_UI", () => {
return Services.locale.isAppLocaleRTL;
});
function gLocaleChangeObserver() {
delete window.RTL_UI;
window.RTL_UI = Services.locale.isAppLocaleRTL;
}
ChromeUtils.defineLazyGetter(this, "gBrandBundle", () => {
return Services.strings.createBundle(
@ -1248,11 +1252,15 @@ var gKeywordURIFixup = {
},
};
Services.uriFixup.checkHost(
fixedURI,
onLookupCompleteListener,
contentPrincipal.originAttributes
);
try {
Services.uriFixup.checkHost(
fixedURI,
onLookupCompleteListener,
contentPrincipal.originAttributes
);
} catch (ex) {
// Ignore errors.
}
},
observe(fixupInfo) {

View file

@ -228,6 +228,7 @@
"Marionette",
"RemoteAgent",
"RTL_UI",
"gLocaleChangeObserver",
"gBrandBundle",
"gBrowserBundle",
"gCustomizeMode",

View file

@ -32,8 +32,8 @@ add_task(async function test_user_namespaces() {
const expectedBackgroundColor = content.window.matchMedia(
"(prefers-color-scheme: dark)"
).matches
? "rgb(105, 15, 34)"
: "rgb(255, 232, 232)";
? "oklch(0.34 0.14 15)"
: "oklch(0.97 0.05 15)";
return !![...tr]
.filter(

View file

@ -498,24 +498,20 @@ add_task(async function testArrowsInPanelMultiView() {
// Test that right/left arrows move in the expected direction for RTL locales.
add_task(async function testArrowsRtl() {
AddOldMenuSideButtons();
await SpecialPowers.pushPrefEnv({ set: [["intl.l10n.pseudo", "bidi"]] });
// window.RTL_UI doesn't update in existing windows when this pref is changed,
// so we need to test in a new window.
let win = await BrowserTestUtils.openNewBrowserWindow();
startFromUrlBar(win);
await expectFocusAfterKey("Tab", afterUrlBarButton, false, win);
EventUtils.synthesizeKey("KEY_ArrowRight", {}, win);
await BrowserTestUtils.enableRtlLocale();
startFromUrlBar(window);
await expectFocusAfterKey("Tab", afterUrlBarButton);
EventUtils.synthesizeKey("KEY_ArrowRight", {});
is(
win.document.activeElement.id,
document.activeElement.id,
afterUrlBarButton,
"ArrowRight at end of button group does nothing"
);
await expectFocusAfterKey("ArrowLeft", "library-button", false, win);
await expectFocusAfterKey("ArrowLeft", "library-button");
if (!sidebarRevampEnabled) {
await expectFocusAfterKey("ArrowLeft", "sidebar-button", false, win);
await expectFocusAfterKey("ArrowLeft", "sidebar-button");
}
await BrowserTestUtils.closeWindow(win);
await SpecialPowers.popPrefEnv();
await BrowserTestUtils.disableRtlLocale();
RemoveOldMenuSideButtons();
});

View file

@ -55,6 +55,12 @@ const knownUnshownImages = [
// This file is not loaded on Windows 7/8.
intermittentNotLoaded: ["win"],
},
{
file: "chrome://global/skin/icons/highlights.svg",
platforms: ["win", "linux", "macosx"],
intermittentShown: ["win", "linux"],
},
];
add_task(async function () {

View file

@ -9,47 +9,6 @@ support-files = [
https_first_disabled = true
support-files = ["open-self-from-frame.html"]
["browser_bug822367.js"]
tags = "mcb"
support-files = [
"file_bug822367_1.html",
"file_bug822367_1.js",
"file_bug822367_2.html",
"file_bug822367_3.html",
"file_bug822367_4.html",
"file_bug822367_4.js",
"file_bug822367_4B.html",
"file_bug822367_5.html",
"file_bug822367_6.html",
]
["browser_bug902156.js"]
tags = "mcb"
support-files = [
"file_bug902156.js",
"file_bug902156_1.html",
"file_bug902156_2.html",
"file_bug902156_3.html",
]
["browser_bug906190.js"]
tags = "mcb"
support-files = [
"file_bug906190_1.html",
"file_bug906190_2.html",
"file_bug906190_3_4.html",
"file_bug906190_redirected.html",
"file_bug906190.js",
"file_bug906190.sjs",
]
["browser_bug1045809.js"]
tags = "mcb"
support-files = [
"file_bug1045809_1.html",
"file_bug1045809_2.html",
]
["browser_check_identity_state.js"]
skip-if = ["os == 'win' && !debug && msix"] # Bug 1895285
https_first_disabled = true
@ -157,7 +116,7 @@ support-files = ["test-mixedcontent-securityerrors.html"]
tags = "mcb"
support-files = [
"file_mixedPassiveContent.html",
"file_bug1045809_1.html",
"file_mixedActiveContent_1.html",
]
["browser_mixed_passive_content_indicator.js"]

View file

@ -1,105 +0,0 @@
// Test that the Mixed Content Doorhanger Action to re-enable protection works
const PREF_ACTIVE = "security.mixed_content.block_active_content";
const PREF_INSECURE = "security.insecure_connection_icon.enabled";
const TEST_URL =
getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"https://example.com"
) + "file_bug1045809_1.html";
var origBlockActive;
add_task(async function () {
registerCleanupFunction(function () {
Services.prefs.setBoolPref(PREF_ACTIVE, origBlockActive);
gBrowser.removeCurrentTab();
});
// Store original preferences so we can restore settings after testing
origBlockActive = Services.prefs.getBoolPref(PREF_ACTIVE);
// Make sure mixed content blocking is on
Services.prefs.setBoolPref(PREF_ACTIVE, true);
let tab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser));
// Check with insecure lock disabled
await SpecialPowers.pushPrefEnv({ set: [[PREF_INSECURE, false]] });
await runTests(tab);
// Check with insecure lock disabled
await SpecialPowers.pushPrefEnv({ set: [[PREF_INSECURE, true]] });
await runTests(tab);
});
async function runTests(tab) {
// Test 1: mixed content must be blocked
await promiseTabLoadEvent(tab, TEST_URL);
await test1(gBrowser.getBrowserForTab(tab));
await promiseTabLoadEvent(tab);
// Test 2: mixed content must NOT be blocked
await test2(gBrowser.getBrowserForTab(tab));
// Test 3: mixed content must be blocked again
await promiseTabLoadEvent(tab);
await test3(gBrowser.getBrowserForTab(tab));
}
async function test1(gTestBrowser) {
await assertMixedContentBlockingState(gTestBrowser, {
activeLoaded: false,
activeBlocked: true,
passiveLoaded: false,
});
await SpecialPowers.spawn(gTestBrowser, [], function () {
let iframe = content.document.getElementsByTagName("iframe")[0];
SpecialPowers.spawn(iframe, [], () => {
let container = content.document.getElementById("mixedContentContainer");
is(container, null, "Mixed Content is NOT to be found in Test1");
});
});
// Disable Mixed Content Protection for the page (and reload)
gIdentityHandler.disableMixedContentProtection();
}
async function test2(gTestBrowser) {
await assertMixedContentBlockingState(gTestBrowser, {
activeLoaded: true,
activeBlocked: false,
passiveLoaded: false,
});
await SpecialPowers.spawn(gTestBrowser, [], function () {
let iframe = content.document.getElementsByTagName("iframe")[0];
SpecialPowers.spawn(iframe, [], () => {
let container = content.document.getElementById("mixedContentContainer");
isnot(container, null, "Mixed Content is to be found in Test2");
});
});
// Re-enable Mixed Content Protection for the page (and reload)
gIdentityHandler.enableMixedContentProtection();
}
async function test3(gTestBrowser) {
await assertMixedContentBlockingState(gTestBrowser, {
activeLoaded: false,
activeBlocked: true,
passiveLoaded: false,
});
await SpecialPowers.spawn(gTestBrowser, [], function () {
let iframe = content.document.getElementsByTagName("iframe")[0];
SpecialPowers.spawn(iframe, [], () => {
let container = content.document.getElementById("mixedContentContainer");
is(container, null, "Mixed Content is NOT to be found in Test3");
});
});
}

View file

@ -1,254 +0,0 @@
/*
* User Override Mixed Content Block - Tests for Bug 822367
*/
const PREF_DISPLAY = "security.mixed_content.block_display_content";
const PREF_DISPLAY_UPGRADE = "security.mixed_content.upgrade_display_content";
const PREF_ACTIVE = "security.mixed_content.block_active_content";
// We alternate for even and odd test cases to simulate different hosts
const HTTPS_TEST_ROOT = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"https://example.com"
);
const HTTPS_TEST_ROOT_2 = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"https://test1.example.com"
);
var gTestBrowser = null;
add_task(async function test() {
await SpecialPowers.pushPrefEnv({
set: [
[PREF_DISPLAY, true],
[PREF_DISPLAY_UPGRADE, false],
[PREF_ACTIVE, true],
],
});
var newTab = BrowserTestUtils.addTab(gBrowser);
gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser;
newTab.linkedBrowser.stop();
// Mixed Script Test
var url = HTTPS_TEST_ROOT + "file_bug822367_1.html";
BrowserTestUtils.startLoadingURIString(gTestBrowser, url);
await BrowserTestUtils.browserLoaded(gTestBrowser, false, url);
});
// Mixed Script Test
add_task(async function MixedTest1A() {
await assertMixedContentBlockingState(gTestBrowser, {
activeLoaded: false,
activeBlocked: true,
passiveLoaded: false,
});
gTestBrowser.ownerGlobal.gIdentityHandler.disableMixedContentProtection();
await BrowserTestUtils.browserLoaded(gTestBrowser);
});
add_task(async function MixedTest1B() {
await SpecialPowers.spawn(gTestBrowser, [], async function () {
await ContentTaskUtils.waitForCondition(
() => content.document.getElementById("p1").innerHTML == "hello",
"Waited too long for mixed script to run in Test 1"
);
});
gTestBrowser.ownerGlobal.gIdentityHandler.enableMixedContentProtectionNoReload();
});
// Mixed Display Test - Doorhanger should not appear
add_task(async function MixedTest2() {
var url = HTTPS_TEST_ROOT_2 + "file_bug822367_2.html";
BrowserTestUtils.startLoadingURIString(gTestBrowser, url);
await BrowserTestUtils.browserLoaded(gTestBrowser, false, url);
await assertMixedContentBlockingState(gTestBrowser, {
activeLoaded: false,
activeBlocked: false,
passiveLoaded: false,
});
});
// Mixed Script and Display Test - User Override should cause both the script and the image to load.
add_task(async function MixedTest3() {
var url = HTTPS_TEST_ROOT + "file_bug822367_3.html";
BrowserTestUtils.startLoadingURIString(gTestBrowser, url);
await BrowserTestUtils.browserLoaded(gTestBrowser, false, url);
});
add_task(async function MixedTest3A() {
await assertMixedContentBlockingState(gTestBrowser, {
activeLoaded: false,
activeBlocked: true,
passiveLoaded: false,
});
gTestBrowser.ownerGlobal.gIdentityHandler.disableMixedContentProtection();
await BrowserTestUtils.browserLoaded(gTestBrowser);
});
add_task(async function MixedTest3B() {
await SpecialPowers.spawn(gTestBrowser, [], async function () {
let p1 = ContentTaskUtils.waitForCondition(
() => content.document.getElementById("p1").innerHTML == "hello",
"Waited too long for mixed script to run in Test 3"
);
let p2 = ContentTaskUtils.waitForCondition(
() => content.document.getElementById("p2").innerHTML == "bye",
"Waited too long for mixed image to load in Test 3"
);
await Promise.all([p1, p2]);
});
await assertMixedContentBlockingState(gTestBrowser, {
activeLoaded: true,
activeBlocked: false,
passiveLoaded: true,
});
gTestBrowser.ownerGlobal.gIdentityHandler.enableMixedContentProtectionNoReload();
});
// Location change - User override on one page doesn't propagate to another page after location change.
add_task(async function MixedTest4() {
var url = HTTPS_TEST_ROOT_2 + "file_bug822367_4.html";
BrowserTestUtils.startLoadingURIString(gTestBrowser, url);
await BrowserTestUtils.browserLoaded(gTestBrowser, false, url);
});
let preLocationChangePrincipal = null;
add_task(async function MixedTest4A() {
await assertMixedContentBlockingState(gTestBrowser, {
activeLoaded: false,
activeBlocked: true,
passiveLoaded: false,
});
preLocationChangePrincipal = gTestBrowser.contentPrincipal;
gTestBrowser.ownerGlobal.gIdentityHandler.disableMixedContentProtection();
await BrowserTestUtils.browserLoaded(gTestBrowser);
});
add_task(async function MixedTest4B() {
let url = HTTPS_TEST_ROOT + "file_bug822367_4B.html";
await SpecialPowers.spawn(gTestBrowser, [url], async function (wantedUrl) {
await ContentTaskUtils.waitForCondition(
() => content.document.location == wantedUrl,
"Waited too long for mixed script to run in Test 4"
);
});
});
add_task(async function MixedTest4C() {
await assertMixedContentBlockingState(gTestBrowser, {
activeLoaded: false,
activeBlocked: true,
passiveLoaded: false,
});
await SpecialPowers.spawn(gTestBrowser, [], async function () {
await ContentTaskUtils.waitForCondition(
() => content.document.getElementById("p1").innerHTML == "",
"Mixed script loaded in test 4 after location change!"
);
});
SitePermissions.removeFromPrincipal(
preLocationChangePrincipal,
"mixed-content"
);
});
// Mixed script attempts to load in a document.open()
add_task(async function MixedTest5() {
var url = HTTPS_TEST_ROOT + "file_bug822367_5.html";
BrowserTestUtils.startLoadingURIString(gTestBrowser, url);
await BrowserTestUtils.browserLoaded(gTestBrowser, false, url);
});
add_task(async function MixedTest5A() {
await assertMixedContentBlockingState(gTestBrowser, {
activeLoaded: false,
activeBlocked: true,
passiveLoaded: false,
});
gTestBrowser.ownerGlobal.gIdentityHandler.disableMixedContentProtection();
await BrowserTestUtils.browserLoaded(gTestBrowser);
});
add_task(async function MixedTest5B() {
await SpecialPowers.spawn(gTestBrowser, [], async function () {
await ContentTaskUtils.waitForCondition(
() => content.document.getElementById("p1").innerHTML == "hello",
"Waited too long for mixed script to run in Test 5"
);
});
gTestBrowser.ownerGlobal.gIdentityHandler.enableMixedContentProtectionNoReload();
});
// Mixed script attempts to load in a document.open() that is within an iframe.
add_task(async function MixedTest6() {
var url = HTTPS_TEST_ROOT_2 + "file_bug822367_6.html";
BrowserTestUtils.startLoadingURIString(gTestBrowser, url);
await BrowserTestUtils.browserLoaded(gTestBrowser, false, url);
});
add_task(async function MixedTest6A() {
gTestBrowser.removeEventListener("load", MixedTest6A, true);
let { gIdentityHandler } = gTestBrowser.ownerGlobal;
await TestUtils.waitForCondition(
() =>
gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"),
"Waited too long for control center to get mixed active blocked state"
);
});
add_task(async function MixedTest6B() {
await assertMixedContentBlockingState(gTestBrowser, {
activeLoaded: false,
activeBlocked: true,
passiveLoaded: false,
});
gTestBrowser.ownerGlobal.gIdentityHandler.disableMixedContentProtection();
await BrowserTestUtils.browserLoaded(gTestBrowser);
});
add_task(async function MixedTest6C() {
await SpecialPowers.spawn(gTestBrowser, [], async function () {
function test() {
try {
return (
content.document
.getElementById("f1")
.contentDocument.getElementById("p1").innerHTML == "hello"
);
} catch (e) {
return false;
}
}
await ContentTaskUtils.waitForCondition(
test,
"Waited too long for mixed script to run in Test 6"
);
});
});
add_task(async function MixedTest6D() {
await assertMixedContentBlockingState(gTestBrowser, {
activeLoaded: true,
activeBlocked: false,
passiveLoaded: false,
});
gTestBrowser.ownerGlobal.gIdentityHandler.enableMixedContentProtectionNoReload();
});
add_task(async function cleanup() {
gBrowser.removeCurrentTab();
});

View file

@ -1,171 +0,0 @@
/*
* Description of the Tests for
* - Bug 902156: Persist "disable protection" option for Mixed Content Blocker
*
* 1. Navigate to the same domain via document.location
* - Load a html page which has mixed content
* - Control Center button to disable protection appears - we disable it
* - Load a new page from the same origin using document.location
* - Control Center button should not appear anymore!
*
* 2. Navigate to the same domain via simulateclick for a link on the page
* - Load a html page which has mixed content
* - Control Center button to disable protection appears - we disable it
* - Load a new page from the same origin simulating a click
* - Control Center button should not appear anymore!
*
* 3. Navigate to a differnet domain and show the content is still blocked
* - Load a different html page which has mixed content
* - Control Center button to disable protection should appear again because
* we navigated away from html page where we disabled the protection.
*
* Note, for all tests we set gHttpTestRoot to use 'https'.
*/
const PREF_ACTIVE = "security.mixed_content.block_active_content";
// We alternate for even and odd test cases to simulate different hosts.
const HTTPS_TEST_ROOT_1 = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"https://test1.example.com"
);
const HTTPS_TEST_ROOT_2 = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"https://test2.example.com"
);
add_setup(async function () {
await SpecialPowers.pushPrefEnv({ set: [[PREF_ACTIVE, true]] });
});
add_task(async function test1() {
let url = HTTPS_TEST_ROOT_1 + "file_bug902156_1.html";
await BrowserTestUtils.withNewTab(url, async function (browser) {
await assertMixedContentBlockingState(browser, {
activeLoaded: false,
activeBlocked: true,
passiveLoaded: false,
});
// Disable Mixed Content Protection for the page (and reload)
let browserLoaded = BrowserTestUtils.browserLoaded(browser, false, url);
let { gIdentityHandler } = browser.ownerGlobal;
gIdentityHandler.disableMixedContentProtection();
await browserLoaded;
await SpecialPowers.spawn(browser, [], async function () {
let expected = "Mixed Content Blocker disabled";
await ContentTaskUtils.waitForCondition(
() =>
content.document.getElementById("mctestdiv").innerHTML == expected,
"Error: Waited too long for mixed script to run in Test 1"
);
let actual = content.document.getElementById("mctestdiv").innerHTML;
is(
actual,
"Mixed Content Blocker disabled",
"OK: Executed mixed script in Test 1"
);
});
// The Script loaded after we disabled the page, now we are going to reload the
// page and see if our decision is persistent
url = HTTPS_TEST_ROOT_1 + "file_bug902156_2.html";
browserLoaded = BrowserTestUtils.browserLoaded(browser, false, url);
BrowserTestUtils.startLoadingURIString(browser, url);
await browserLoaded;
// The Control Center button should appear but isMixedContentBlocked should be NOT true,
// because our decision of disabling the mixed content blocker is persistent.
await assertMixedContentBlockingState(browser, {
activeLoaded: true,
activeBlocked: false,
passiveLoaded: false,
});
await SpecialPowers.spawn(browser, [], function () {
let actual = content.document.getElementById("mctestdiv").innerHTML;
is(
actual,
"Mixed Content Blocker disabled",
"OK: Executed mixed script in Test 1"
);
});
gIdentityHandler.enableMixedContentProtection();
});
});
// ------------------------ Test 2 ------------------------------
add_task(async function test2() {
let url = HTTPS_TEST_ROOT_2 + "file_bug902156_2.html";
await BrowserTestUtils.withNewTab(url, async function (browser) {
await assertMixedContentBlockingState(browser, {
activeLoaded: false,
activeBlocked: true,
passiveLoaded: false,
});
// Disable Mixed Content Protection for the page (and reload)
let browserLoaded = BrowserTestUtils.browserLoaded(browser, false, url);
let { gIdentityHandler } = browser.ownerGlobal;
gIdentityHandler.disableMixedContentProtection();
await browserLoaded;
await SpecialPowers.spawn(browser, [], async function () {
let expected = "Mixed Content Blocker disabled";
await ContentTaskUtils.waitForCondition(
() =>
content.document.getElementById("mctestdiv").innerHTML == expected,
"Error: Waited too long for mixed script to run in Test 2"
);
let actual = content.document.getElementById("mctestdiv").innerHTML;
is(
actual,
"Mixed Content Blocker disabled",
"OK: Executed mixed script in Test 2"
);
});
// The Script loaded after we disabled the page, now we are going to reload the
// page and see if our decision is persistent
url = HTTPS_TEST_ROOT_2 + "file_bug902156_1.html";
browserLoaded = BrowserTestUtils.browserLoaded(browser, false, url);
// reload the page using the provided link in the html file
await SpecialPowers.spawn(browser, [], function () {
let mctestlink = content.document.getElementById("mctestlink");
mctestlink.click();
});
await browserLoaded;
// The Control Center button should appear but isMixedContentBlocked should be NOT true,
// because our decision of disabling the mixed content blocker is persistent.
await assertMixedContentBlockingState(browser, {
activeLoaded: true,
activeBlocked: false,
passiveLoaded: false,
});
await SpecialPowers.spawn(browser, [], function () {
let actual = content.document.getElementById("mctestdiv").innerHTML;
is(
actual,
"Mixed Content Blocker disabled",
"OK: Executed mixed script in Test 2"
);
});
gIdentityHandler.enableMixedContentProtection();
});
});
add_task(async function test3() {
let url = HTTPS_TEST_ROOT_1 + "file_bug902156_3.html";
await BrowserTestUtils.withNewTab(url, async function (browser) {
await assertMixedContentBlockingState(browser, {
activeLoaded: false,
activeBlocked: true,
passiveLoaded: false,
});
});
});

View file

@ -1,339 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/*
* Tests the persistence of the "disable protection" option for Mixed Content
* Blocker in child tabs (bug 906190).
*/
requestLongerTimeout(2);
// We use the different urls for testing same origin checks before allowing
// mixed content on child tabs.
const HTTPS_TEST_ROOT_1 = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"https://test1.example.com"
);
const HTTPS_TEST_ROOT_2 = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"https://test2.example.com"
);
/**
* For all tests, we load the pages over HTTPS and test both:
* - |CTRL+CLICK|
* - |RIGHT CLICK -> OPEN LINK IN TAB|
*/
async function doTest(
parentTabSpec,
childTabSpec,
testTaskFn,
waitForMetaRefresh
) {
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: parentTabSpec,
},
async function (browser) {
// As a sanity check, test that active content has been blocked as expected.
await assertMixedContentBlockingState(gBrowser, {
activeLoaded: false,
activeBlocked: true,
passiveLoaded: false,
});
// Disable the Mixed Content Blocker for the page, which reloads it.
let promiseReloaded = BrowserTestUtils.browserLoaded(browser);
let principal = gBrowser.contentPrincipal;
gIdentityHandler.disableMixedContentProtection();
await promiseReloaded;
// Wait for the script in the page to update the contents of the test div.
await SpecialPowers.spawn(
browser,
[childTabSpec],
async childTabSpecContent => {
let testDiv = content.document.getElementById("mctestdiv");
await ContentTaskUtils.waitForCondition(
() => testDiv.innerHTML == "Mixed Content Blocker disabled"
);
// Add the link for the child tab to the page.
let mainDiv = content.document.createElement("div");
mainDiv.innerHTML =
'<p><a id="linkToOpenInNewTab" href="' +
childTabSpecContent +
'">Link</a></p>';
content.document.body.appendChild(mainDiv);
}
);
// Execute the test in the child tabs with the two methods to open it.
for (let openFn of [simulateCtrlClick, simulateContextMenuOpenInTab]) {
let promiseTabLoaded = waitForSomeTabToLoad();
openFn(browser);
await promiseTabLoaded;
gBrowser.selectTabAtIndex(2);
if (waitForMetaRefresh) {
await waitForSomeTabToLoad();
}
await testTaskFn();
gBrowser.removeCurrentTab();
}
SitePermissions.removeFromPrincipal(principal, "mixed-content");
}
);
}
function simulateCtrlClick(browser) {
BrowserTestUtils.synthesizeMouseAtCenter(
"#linkToOpenInNewTab",
{ ctrlKey: true, metaKey: true },
browser
);
}
function simulateContextMenuOpenInTab(browser) {
BrowserTestUtils.waitForEvent(document, "popupshown", false, event => {
// These are operations that must be executed synchronously with the event.
document.getElementById("context-openlinkintab").doCommand();
event.target.hidePopup();
return true;
});
BrowserTestUtils.synthesizeMouseAtCenter(
"#linkToOpenInNewTab",
{ type: "contextmenu", button: 2 },
browser
);
}
// Waits for a load event somewhere in the browser but ignore events coming
// from <xul:browser>s without a tab assigned. That are most likely browsers
// that preload the new tab page.
function waitForSomeTabToLoad() {
return BrowserTestUtils.firstBrowserLoaded(window, true, browser => {
let tab = gBrowser.getTabForBrowser(browser);
return !!tab;
});
}
/**
* Ensure the Mixed Content Blocker is enabled.
*/
add_task(async function test_initialize() {
await SpecialPowers.pushPrefEnv({
set: [
["security.mixed_content.block_active_content", true],
// We need to disable the dFPI heuristic. So, we won't have unnecessary
// 3rd party cookie permission that could affect following tests because
// it will create a permission icon on the URL bar.
["privacy.restrict3rdpartystorage.heuristic.recently_visited", false],
],
});
});
/**
* 1. - Load a html page which has mixed content
* - Doorhanger to disable protection appears - we disable it
* - Load a subpage from the same origin in a new tab simulating a click
* - Doorhanger should >> NOT << appear anymore!
*/
add_task(async function test_same_origin() {
await doTest(
HTTPS_TEST_ROOT_1 + "file_bug906190_1.html",
HTTPS_TEST_ROOT_1 + "file_bug906190_2.html",
async function () {
// The doorhanger should appear but activeBlocked should be >> NOT << true,
// because our decision of disabling the mixed content blocker is persistent
// across tabs.
await assertMixedContentBlockingState(gBrowser, {
activeLoaded: true,
activeBlocked: false,
passiveLoaded: false,
});
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
Assert.equal(
content.document.getElementById("mctestdiv").innerHTML,
"Mixed Content Blocker disabled",
"OK: Executed mixed script"
);
});
}
);
});
/**
* 2. - Load a html page which has mixed content
* - Doorhanger to disable protection appears - we disable it
* - Load a new page from a different origin in a new tab simulating a click
* - Doorhanger >> SHOULD << appear again!
*/
add_task(async function test_different_origin() {
await doTest(
HTTPS_TEST_ROOT_1 + "file_bug906190_2.html",
HTTPS_TEST_ROOT_2 + "file_bug906190_2.html",
async function () {
// The doorhanger should appear and activeBlocked should be >> TRUE <<,
// because our decision of disabling the mixed content blocker should only
// persist if pages are from the same domain.
await assertMixedContentBlockingState(gBrowser, {
activeLoaded: false,
activeBlocked: true,
passiveLoaded: false,
});
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
Assert.equal(
content.document.getElementById("mctestdiv").innerHTML,
"Mixed Content Blocker enabled",
"OK: Blocked mixed script"
);
});
}
);
});
/**
* 3. - Load a html page which has mixed content
* - Doorhanger to disable protection appears - we disable it
* - Load a new page from the same origin in a new tab simulating a click
* - Redirect to another page from the same origin using meta-refresh
* - Doorhanger should >> NOT << appear again!
*/
add_task(async function test_same_origin_metarefresh_same_origin() {
// file_bug906190_3_4.html redirects to page test1.example.com/* using meta-refresh
await doTest(
HTTPS_TEST_ROOT_1 + "file_bug906190_1.html",
HTTPS_TEST_ROOT_1 + "file_bug906190_3_4.html",
async function () {
// The doorhanger should appear but activeBlocked should be >> NOT << true!
await assertMixedContentBlockingState(gBrowser, {
activeLoaded: true,
activeBlocked: false,
passiveLoaded: false,
});
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
Assert.equal(
content.document.getElementById("mctestdiv").innerHTML,
"Mixed Content Blocker disabled",
"OK: Executed mixed script"
);
});
},
true
);
});
/**
* 4. - Load a html page which has mixed content
* - Doorhanger to disable protection appears - we disable it
* - Load a new page from the same origin in a new tab simulating a click
* - Redirect to another page from a different origin using meta-refresh
* - Doorhanger >> SHOULD << appear again!
*/
add_task(async function test_same_origin_metarefresh_different_origin() {
await doTest(
HTTPS_TEST_ROOT_2 + "file_bug906190_1.html",
HTTPS_TEST_ROOT_2 + "file_bug906190_3_4.html",
async function () {
// The doorhanger should appear and activeBlocked should be >> TRUE <<.
await assertMixedContentBlockingState(gBrowser, {
activeLoaded: false,
activeBlocked: true,
passiveLoaded: false,
});
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
Assert.equal(
content.document.getElementById("mctestdiv").innerHTML,
"Mixed Content Blocker enabled",
"OK: Blocked mixed script"
);
});
},
true
);
});
/**
* 5. - Load a html page which has mixed content
* - Doorhanger to disable protection appears - we disable it
* - Load a new page from the same origin in a new tab simulating a click
* - Redirect to another page from the same origin using 302 redirect
*/
add_task(async function test_same_origin_302redirect_same_origin() {
// the sjs files returns a 302 redirect- note, same origins
await doTest(
HTTPS_TEST_ROOT_1 + "file_bug906190_1.html",
HTTPS_TEST_ROOT_1 + "file_bug906190.sjs",
async function () {
// The doorhanger should appear but activeBlocked should be >> NOT << true.
// Currently it is >> TRUE << - see follow up bug 914860
ok(
!gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"),
"OK: Mixed Content is NOT being blocked"
);
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
Assert.equal(
content.document.getElementById("mctestdiv").innerHTML,
"Mixed Content Blocker disabled",
"OK: Executed mixed script"
);
});
}
);
});
/**
* 6. - Load a html page which has mixed content
* - Doorhanger to disable protection appears - we disable it
* - Load a new page from the same origin in a new tab simulating a click
* - Redirect to another page from a different origin using 302 redirect
*/
add_task(async function test_same_origin_302redirect_different_origin() {
// the sjs files returns a 302 redirect - note, different origins
await doTest(
HTTPS_TEST_ROOT_2 + "file_bug906190_1.html",
HTTPS_TEST_ROOT_2 + "file_bug906190.sjs",
async function () {
// The doorhanger should appear and activeBlocked should be >> TRUE <<.
await assertMixedContentBlockingState(gBrowser, {
activeLoaded: false,
activeBlocked: true,
passiveLoaded: false,
});
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
Assert.equal(
content.document.getElementById("mctestdiv").innerHTML,
"Mixed Content Blocker enabled",
"OK: Blocked mixed script"
);
});
}
);
});
/**
* 7. - Test memory leak issue on redirection error. See Bug 1269426.
*/
add_task(async function test_bad_redirection() {
// the sjs files returns a 302 redirect - note, different origins
await doTest(
HTTPS_TEST_ROOT_2 + "file_bug906190_1.html",
HTTPS_TEST_ROOT_2 + "file_bug906190.sjs?bad-redirection=1",
function () {
// Nothing to do. Just see if memory leak is reported in the end.
ok(true, "Nothing to do");
}
);
});

View file

@ -45,12 +45,6 @@ add_task(async function () {
await loadBadCertPage(MIXED_CONTENT_URL);
checkIdentityPopup("security-warning.svg");
// check that the crossed out icon is shown when disabling mixed content protection
gIdentityHandler.disableMixedContentProtection();
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
checkIdentityPopup("security-broken.svg");
// check that a warning is shown even without mixed content
BrowserTestUtils.startLoadingURIString(
gBrowser.selectedBrowser,

View file

@ -22,7 +22,7 @@ const kTestcases = [
expectedIdentityMode: "mixedDisplayContent",
},
{
uri: kBaseURI + "file_bug1045809_1.html",
uri: kBaseURI + "file_mixedActiveContent_1.html",
expectErrorPage: false,
expectedIdentityMode: "mixedActiveBlocked",
},

View file

@ -7,8 +7,6 @@
// loaded) we load the page and check the flags.
// * We change the about:config prefs (mixed active blocked, mixed display
// blocked), reload the page, and check the flags again.
// * We override protection so all mixed content can load and check the
// flags again.
const TEST_URI =
getRootDirectory(gTestPath).replace(
@ -56,16 +54,3 @@ add_task(async function blockMixedActiveContentTest() {
passiveLoaded: false,
});
});
add_task(async function overrideMCB() {
// Disable mixed content blocking (reloads page) and retest
let { gIdentityHandler } = gTestBrowser.ownerGlobal;
gIdentityHandler.disableMixedContentProtection();
await BrowserTestUtils.browserLoaded(gTestBrowser);
await assertMixedContentBlockingState(gTestBrowser, {
activeLoaded: true,
activeBlocked: false,
passiveLoaded: true,
});
});

View file

@ -1,18 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test 1 for Mixed Content Blocker User Override - Mixed Script
https://bugzilla.mozilla.org/show_bug.cgi?id=822367
-->
<head>
<meta charset="utf-8">
<title>Test 1 for Bug 822367</title>
</head>
<body>
<div id="testContent">
<p id="p1"></p>
</div>
<script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_1.js">
</script>
</body>
</html>

View file

@ -1 +0,0 @@
document.getElementById("p1").innerHTML = "hello";

View file

@ -1,16 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test 2 for Mixed Content Blocker User Override - Mixed Display
https://bugzilla.mozilla.org/show_bug.cgi?id=822367
-->
<head>
<meta charset="utf-8">
<title>Test 2 for Bug 822367 - Mixed Display</title>
</head>
<body>
<div id="testContent">
<img src="http://example.com/tests/image/test/mochitest/blue.png">
</div>
</body>
</html>

View file

@ -1,27 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test 3 for Mixed Content Blocker User Override - Mixed Script and Display
https://bugzilla.mozilla.org/show_bug.cgi?id=822367
-->
<head>
<meta charset="utf-8">
<title>Test 3 for Bug 822367</title>
<script>
function foo() {
var x = document.createElement("p");
x.setAttribute("id", "p2");
x.innerHTML = "bye";
document.getElementById("testContent").appendChild(x);
}
</script>
</head>
<body>
<div id="testContent">
<p id="p1"></p>
<img src="http://example.com/tests/image/test/mochitest/blue.png" onload="foo()">
</div>
<script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_1.js">
</script>
</body>
</html>

View file

@ -1,18 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test 4 for Mixed Content Blocker User Override - Mixed Script and Display
https://bugzilla.mozilla.org/show_bug.cgi?id=822367
-->
<head>
<meta charset="utf-8">
<title>Test 4 for Bug 822367</title>
</head>
<body>
<div id="testContent">
<p id="p1"></p>
</div>
<script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_4.js">
</script>
</body>
</html>

View file

@ -1,2 +0,0 @@
document.location =
"https://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_4B.html";

View file

@ -1,18 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test 4B for Mixed Content Blocker User Override - Location Changed
https://bugzilla.mozilla.org/show_bug.cgi?id=822367
-->
<head>
<meta charset="utf-8">
<title>Test 4B Location Change for Bug 822367</title>
</head>
<body>
<div id="testContent">
<p id="p1"></p>
</div>
<script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_1.js">
</script>
</body>
</html>

View file

@ -1,23 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test 5 for Mixed Content Blocker User Override - Mixed Script in document.open()
https://bugzilla.mozilla.org/show_bug.cgi?id=822367
-->
<head>
<meta charset="utf-8">
<title>Test 5 for Bug 822367</title>
<script>
function createDoc() {
var doc = document.open("text/html", "replace");
doc.write('<!DOCTYPE html><html><body><p id="p1">This is some content</p><script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_1.js">\<\/script\>\<\/body>\<\/html>');
doc.close();
}
</script>
</head>
<body>
<div id="testContent">
<img src="https://example.com/tests/image/test/mochitest/blue.png" onload="createDoc()">
</div>
</body>
</html>

View file

@ -1,16 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test 6 for Mixed Content Blocker User Override - Mixed Script in document.open() within an iframe
https://bugzilla.mozilla.org/show_bug.cgi?id=822367
-->
<head>
<meta charset="utf-8">
<title>Test 6 for Bug 822367</title>
</head>
<body>
<div id="testContent">
<iframe name="f1" id="f1" src="https://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_5.html"></iframe>
</div>
</body>
</html>

View file

@ -1,6 +0,0 @@
/*
* Once the mixed content blocker is disabled for the page, this scripts loads
* and updates the text inside the div container.
*/
document.getElementById("mctestdiv").innerHTML =
"Mixed Content Blocker disabled";

View file

@ -1,15 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test 1 for Bug 902156 - See file browser_bug902156.js for description.
https://bugzilla.mozilla.org/show_bug.cgi?id=902156
-->
<head>
<meta charset="utf-8">
<title>Test 1 for Bug 902156</title>
</head>
<body>
<div id="mctestdiv">Mixed Content Blocker enabled</div>
<script src="http://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug902156.js" ></script>
</body>
</html>

View file

@ -1,17 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test 2 for Bug 902156 - See file browser_bug902156.js for description.
https://bugzilla.mozilla.org/show_bug.cgi?id=902156
-->
<head>
<meta charset="utf-8">
<title>Test 2 for Bug 902156</title>
</head>
<body>
<div id="mctestdiv">Mixed Content Blocker enabled</div>
<a href="https://test2.example.com/browser/browser/base/content/test/siteIdentity/file_bug902156_1.html"
id="mctestlink" target="_top">Go to http site</a>
<script src="http://test2.example.com/browser/browser/base/content/test/siteIdentity/file_bug902156.js" ></script>
</body>
</html>

View file

@ -1,15 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test 3 for Bug 902156 - See file browser_bug902156.js for description.
https://bugzilla.mozilla.org/show_bug.cgi?id=902156
-->
<head>
<meta charset="utf-8">
<title>Test 3 for Bug 902156</title>
</head>
<body>
<div id="mctestdiv">Mixed Content Blocker enabled</div>
<script src="http://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug902156.js" ></script>
</body>
</html>

View file

@ -1,6 +0,0 @@
/*
* Once the mixed content blocker is disabled for the page, this scripts loads
* and updates the text inside the div container.
*/
document.getElementById("mctestdiv").innerHTML =
"Mixed Content Blocker disabled";

View file

@ -1,18 +0,0 @@
function handleRequest(request, response) {
var page = "<!DOCTYPE html><html><body>bug 906190</body></html>";
var path =
"https://test1.example.com/browser/browser/base/content/test/siteIdentity/";
var url;
if (request.queryString.includes("bad-redirection=1")) {
url = path + "this_page_does_not_exist.html";
} else {
url = path + "file_bug906190_redirected.html";
}
response.setHeader("Cache-Control", "no-cache", false);
response.setHeader("Content-Type", "text/html", false);
response.setStatusLine(request.httpVersion, "302", "Found");
response.setHeader("Location", url, false);
response.write(page);
}

View file

@ -1,15 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test 1 for Bug 906190 - See file browser_bug902156.js for description.
https://bugzilla.mozilla.org/show_bug.cgi?id=906190
-->
<head>
<meta charset="utf-8">
<title>Test 1 for Bug 906190</title>
</head>
<body>
<div id="mctestdiv">Mixed Content Blocker enabled</div>
<script src="http://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug906190.js" ></script>
</body>
</html>

View file

@ -1,15 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test 2 for Bug 906190 - See file browser_bug902156.js for description.
https://bugzilla.mozilla.org/show_bug.cgi?id=906190
-->
<head>
<meta charset="utf-8">
<title>Test 2 for Bug 906190</title>
</head>
<body>
<div id="mctestdiv">Mixed Content Blocker enabled</div>
<script src="http://test2.example.com/browser/browser/base/content/test/siteIdentity/file_bug906190.js" ></script>
</body>
</html>

View file

@ -1,14 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test 3 and 4 for Bug 906190 - See file browser_bug902156.js for description.
https://bugzilla.mozilla.org/show_bug.cgi?id=906190
-->
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content="0; url=https://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug906190_redirected.html">
<title>Test 3 and 4 for Bug 906190</title>
</head>
<body>
</body>
</html>

View file

@ -1,15 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Redirected Page of Test 3 to 6 for Bug 906190 - See file browser_bug902156.js for description.
https://bugzilla.mozilla.org/show_bug.cgi?id=906190
-->
<head>
<meta charset="utf-8">
<title>Redirected Page for Bug 906190</title>
</head>
<body>
<div id="mctestdiv">Mixed Content Blocker enabled</div>
<script src="http://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug906190.js" ></script>
</body>
</html>

View file

@ -177,6 +177,10 @@ let propNameAllowlist = [
// Bug 1908535 to refactor form components to use this token
{ propName: "--input-space-block", isFromDevTools: false },
// Ignore token properties that follow the pattern --color-[name]-[number]
// This enables us to provide our full color palette for developers.
{ propName: /--color-[a-z]+-\d+/, isFromDevTools: false },
];
// Add suffix to stylesheets' URI so that we always load them here and
@ -442,6 +446,13 @@ function shouldIgnorePropSource(item, prop) {
.some(f => item.sourceName.test(f));
}
function shouldIgnorePropPattern(item, prop) {
if (!item.propName || !(item.propName instanceof RegExp)) {
return false;
}
return item.propName.test(prop);
}
add_task(async function checkAllTheCSS() {
// Since we later in this test use Services.console.getMessageArray(),
// better to not have some messages from previous tests in the array.
@ -567,7 +578,9 @@ add_task(async function checkAllTheCSS() {
for (let item of propNameAllowlist) {
if (
isDevtools == item.isFromDevTools &&
(item.propName == prop || shouldIgnorePropSource(item, prop))
(item.propName == prop ||
shouldIgnorePropPattern(item, prop) ||
shouldIgnorePropSource(item, prop))
) {
item.used = true;
if (

View file

@ -5,6 +5,8 @@
# 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/.
<?csp default-src chrome: ?>
<window id="webextpanels-window"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
@ -30,15 +32,10 @@
<html:link rel="localization" href="browser/translations.ftl"/>
</linkset>
<!-- nsContextMenu.initNavigationItems expects these elements -->
<commandset id="mainCommandset">
<command id="Browser:Back"
oncommand="getPanelBrowser().webNavigation.goBack();"
disabled="true"/>
<command id="Browser:Forward"
oncommand="getPanelBrowser().webNavigation.goForward();"
disabled="true"/>
<command id="Browser:Stop" oncommand="PanelBrowserStop();"/>
<command id="Browser:Reload" oncommand="PanelBrowserReload();"/>
<command id="Browser:Stop" disabled="true" />
<command id="Browser:Reload" disabled="true" />
</commandset>
<popupset id="mainPopupSet">

View file

@ -3698,7 +3698,7 @@ BrowserGlue.prototype = {
_migrateUI() {
// Use an increasing number to keep track of the current migration state.
// Completely unrelated to the current Firefox release number.
const UI_VERSION = 153;
const UI_VERSION = 154;
const BROWSER_DOCURL = AppConstants.BROWSER_CHROME_URL;
if (!Services.prefs.prefHasUserValue("browser.migration.version")) {
@ -4514,6 +4514,21 @@ BrowserGlue.prototype = {
);
}
if (currentUIVersion < 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", UI_VERSION);
},

View file

@ -28,7 +28,6 @@ module.exports = {
"content-src/components/MultiStageScreen.jsx",
"content-src/components/MultiStageProtonScreen.jsx",
"content-src/components/MultiSelect.jsx",
"content-src/components/ReturnToAMO.jsx",
],
rules: {
"jsx-a11y/anchor-has-content": "off",

View file

@ -196,14 +196,6 @@ export class AboutWelcomeChild extends JSWindowActorChild {
async getAWContent() {
let attributionData = await this.sendQuery("AWPage:GET_ATTRIBUTION_DATA");
// Return to AMO gets returned early.
if (attributionData?.template) {
lazy.log.debug("Loading about:welcome with RTAMO attribution data");
return Cu.cloneInto(attributionData, this.contentWindow);
} else if (attributionData?.ua) {
lazy.log.debug("Loading about:welcome with UA attribution");
}
let experimentMetadata =
lazy.ExperimentAPI.getExperimentMetaData({
featureId: "aboutwelcome",

View file

@ -181,6 +181,10 @@ export class AboutWelcomeParent extends JSWindowActorParent {
lazy.AddonManager.removeInstallListener(listener);
resolve("install cancelled");
},
onDownloadCancelled() {
lazy.AddonManager.removeInstallListener(listener);
resolve("install cancelled");
},
onInstallFailed() {
lazy.AddonManager.removeInstallListener(listener);
resolve("install failed");
@ -197,6 +201,7 @@ export class AboutWelcomeParent extends JSWindowActorParent {
await lazy.AboutWelcomeDefaults.getAddonFromRepository(data);
return {
addonId: addonDetails.id,
label: addonDetails.name,
icon: addonDetails.iconURL,
type: addonDetails.type,

View file

@ -6,7 +6,6 @@ import React from "react";
import ReactDOM from "react-dom";
import { AboutWelcomeUtils } from "./lib/aboutwelcome-utils.mjs";
import { MultiStageAboutWelcome } from "./components/MultiStageAboutWelcome";
import { ReturnToAMO } from "./components/ReturnToAMO";
class AboutWelcome extends React.PureComponent {
constructor(props) {
@ -57,21 +56,14 @@ class AboutWelcome extends React.PureComponent {
render() {
const { props } = this;
if (props.template === "return_to_amo") {
return (
<ReturnToAMO
message_id={props.messageId}
type={props.type}
name={props.name}
url={props.url}
iconURL={props.iconURL}
themeScreenshots={props.screenshots}
metricsFlowUri={this.state.metricsFlowUri}
/>
);
}
return (
<MultiStageAboutWelcome
addonId={props.addonId}
addonType={props.type}
addonName={props.name || ""}
addonURL={props.url}
addonIconURL={props.iconURL}
themeScreenshots={props.screenshots}
message_id={props.messageId}
defaultScreens={props.screens}
updateHistory={!props.disableHistoryUpdates}

View file

@ -754,6 +754,33 @@ html {
@include arrow-icon-styles;
}
.loader {
width: 1em;
height: 1em;
border: 3px solid var(--in-content-primary-button-text-color);
border-bottom-color: transparent;
border-radius: 50%;
box-sizing: border-box;
animation: rotation 1s linear infinite;
justify-self: center;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.loaderContainer {
display: flex;
padding: 1.5px 37.5px;
margin: auto;
}
}
.logo-container {

View file

@ -2,72 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
import React, { useState, useEffect } from "react";
import React from "react";
import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils.mjs";
import { Localized } from "./MSLocalized";
export const Loader = () => {
return (
<button className="primary">
<div className="loaderContainer">
<span className="loader" />
</div>
</button>
);
};
export const InstallButton = props => {
// determine if the addon is already installed so the state is
// consistent on refresh or navigation
const [installing, setInstalling] = useState(false);
const [installComplete, setInstallComplete] = useState(false);
const defaultInstallLabel = { string_id: "amo-picker-install-button-label" };
const defaultInstallCompleteLabel = {
string_id: "amo-picker-install-complete-label",
};
useEffect(() => {
setInstallComplete(props.installedAddons?.includes(props.addonId));
}, [props.addonId, props.installedAddons]);
let buttonLabel = installComplete
? props.install_complete_label || defaultInstallCompleteLabel
: props.install_label || defaultInstallLabel;
function onClick(event) {
props.handleAction(event);
// Replace the label with the spinner
setInstalling(true);
window.AWEnsureAddonInstalled(props.addonId).then(value => {
if (value === "complete") {
// Set the label to "Installed"
setInstallComplete(true);
}
// Whether the addon installs or not, we want to remove the spinner
setInstalling(false);
});
}
return (
<div className="install-button-wrapper">
{installing ? (
<Loader />
) : (
<Localized text={buttonLabel}>
<button
id={`install-button-${props.addonId}`}
value={props.index}
onClick={onClick}
disabled={installComplete}
className="primary"
/>
</Localized>
)}
</div>
);
};
import { InstallButton } from "./InstallButton";
export const AddonsPicker = props => {
const { content, installedAddons, layout } = props;

View file

@ -0,0 +1,90 @@
/* 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 React, { useState, useEffect } from "react";
import { Localized } from "./MSLocalized";
export const Loader = () => {
return (
<button className="primary">
<div className="loaderContainer">
<span className="loader" />
</div>
</button>
);
};
export const InstallButton = props => {
// determine if the addon is already installed so the state is
// consistent on refresh or navigation
const [installing, setInstalling] = useState(false);
const [installComplete, setInstallComplete] = useState(false);
const defaultInstallLabel = {
string_id: "amo-picker-install-button-label",
};
function getDefaultInstallCompleteLabel(addonType = "") {
let defaultInstallCompleteLabel;
if (addonType && addonType === "theme") {
defaultInstallCompleteLabel = {
string_id: "return-to-amo-theme-install-complete-label",
};
} else if (addonType && addonType === "extension") {
defaultInstallCompleteLabel = {
string_id: "return-to-amo-extension-install-complete-label",
};
} else {
defaultInstallCompleteLabel = {
string_id: "amo-picker-install-complete-label",
};
}
return defaultInstallCompleteLabel;
}
useEffect(() => {
setInstallComplete(props.installedAddons?.includes(props.addonId));
}, [props.addonId, props.installedAddons]);
let buttonLabel = installComplete
? props.install_complete_label ||
getDefaultInstallCompleteLabel(props.addonType)
: props.install_label || defaultInstallLabel;
function onClick(event) {
props.handleAction(event);
// Replace the label with the spinner
setInstalling(true);
window.AWEnsureAddonInstalled(props.addonId).then(value => {
if (value === "complete") {
// Set the label to "Installed"
setInstallComplete(true);
}
// Whether the addon installs or not, we want to remove the spinner
setInstalling(false);
});
}
return (
<div className="install-button-wrapper">
{installing ? (
<Loader />
) : (
<Localized text={buttonLabel}>
<button
id={`install-button-${props.addonId}`}
value={props.index}
onClick={onClick}
disabled={installComplete}
className="primary"
data-l10n-args={JSON.stringify({
"addon-name": props.addonName ?? "",
})}
/>
</Localized>
)}
</div>
);
};

View file

@ -327,6 +327,13 @@ export const MultiStageAboutWelcome = props => {
}
installedAddons={installedAddons}
setInstalledAddons={setInstalledAddons}
addonId={props.addonId}
addonType={props.addonType}
addonName={props.addonName}
addonURL={props.addonURL}
addonIconURL={props.addonIconURL}
themeScreenshots={props.themeScreenshots}
isRtamo={currentScreen.content.isRtamo}
/>
) : null;
})}
@ -523,6 +530,17 @@ export class WelcomeScreen extends React.PureComponent {
"FXA_SIGNIN_FLOW"
);
}
if (action.type === "INSTALL_ADDON_FROM_URL") {
const url = props.addonURL;
if (!action.data) {
return;
}
// Set add-on url in action.data.url property from JSON
action.data = { ...action.data, url };
AboutWelcomeUtils.handleUserAction(action);
}
// Wait until migration closes to complete the action
const hasMigrate = a =>
a.type === "SHOW_MIGRATION_WIZARD" ||
@ -698,6 +716,13 @@ export class WelcomeScreen extends React.PureComponent {
forceHideStepsIndicator={this.props.forceHideStepsIndicator}
ariaRole={this.props.ariaRole}
aboveButtonStepsIndicator={this.props.aboveButtonStepsIndicator}
addonId={this.props.addonId}
addonType={this.props.addonType}
addonName={this.props.addonName}
addonURL={this.props.addonURL}
addonIconURL={this.props.addonIconURL}
themeScreenshots={this.props.themeScreenshots}
isRtamo={this.props.content.isRtamo}
/>
);
}

View file

@ -17,6 +17,7 @@ import { OnboardingVideo } from "./OnboardingVideo";
import { AdditionalCTA } from "./AdditionalCTA";
import { LinkParagraph } from "./LinkParagraph";
import { ContentTiles } from "./ContentTiles";
import { InstallButton } from "./InstallButton";
export const MultiStageProtonScreen = props => {
const { autoAdvance, handleAction, order } = props;
@ -70,9 +71,12 @@ export const MultiStageProtonScreen = props => {
previousOrder={props.previousOrder}
autoAdvance={props.autoAdvance}
isRtamo={props.isRtamo}
addonId={props.addonId}
addonType={props.addonType}
addonName={props.addonName}
isTheme={props.isTheme}
iconURL={props.iconURL}
addonURL={props.addonURL}
addonIconURL={props.addonIconURL}
themeScreenshots={props.themeScreenshots}
messageId={props.messageId}
negotiatedLanguage={props.negotiatedLanguage}
langPackInstallPhase={props.langPackInstallPhase}
@ -84,7 +88,15 @@ export const MultiStageProtonScreen = props => {
};
export const ProtonScreenActionButtons = props => {
const { content, addonName, activeMultiSelect } = props;
const {
content,
isRtamo,
addonId,
addonType,
addonName,
activeMultiSelect,
installedAddons,
} = props;
const defaultValue = content.checkbox?.defaultValue;
const [isChecked, setIsChecked] = useState(defaultValue || false);
@ -106,6 +118,12 @@ export const ProtonScreenActionButtons = props => {
return null;
}
if (isRtamo) {
content.primary_button.label.string_id = addonType?.includes("theme")
? "return-to-amo-add-theme-label"
: "mr1-return-to-amo-add-extension-label";
}
// If we have a multi-select screen, we want to disable the primary button
// until the user has selected at least one item.
const isPrimaryDisabled = primaryDisabledValue => {
@ -133,28 +151,42 @@ export const ProtonScreenActionButtons = props => {
flow={content.additional_button?.flow}
alignment={content.additional_button?.alignment}
>
<Localized text={content.primary_button?.label}>
<button
ref={buttonRef}
className={`${content.primary_button?.style ?? "primary"}${
content.primary_button?.has_arrow_icon ? " arrow-icon" : ""
}`}
// Whether or not the checkbox is checked determines which action
// should be handled. By setting value here, we indicate to
// this.handleAction() where in the content tree it should take
// the action to execute from.
value={isChecked ? "checkbox" : "primary_button"}
disabled={isPrimaryDisabled(content.primary_button?.disabled)}
onClick={props.handleAction}
data-l10n-args={
addonName
? JSON.stringify({
"addon-name": addonName,
})
: ""
}
{isRtamo ? (
<InstallButton
key={addonId}
addonId={addonId}
addonType={addonType}
addonName={addonName}
index={"primary_button"}
handleAction={props.handleAction}
installedAddons={installedAddons}
install_label={content.primary_button.label}
install_complete_label={content.primary_button.install_complete_label}
/>
</Localized>
) : (
<Localized text={content.primary_button?.label}>
<button
ref={buttonRef}
className={`${content.primary_button?.style ?? "primary"}${
content.primary_button?.has_arrow_icon ? " arrow-icon" : ""
}`}
// Whether or not the checkbox is checked determines which action
// should be handled. By setting value here, we indicate to
// this.handleAction() where in the content tree it should take
// the action to execute from.
value={isChecked ? "checkbox" : "primary_button"}
disabled={isPrimaryDisabled(content.primary_button?.disabled)}
onClick={props.handleAction}
data-l10n-args={
addonName
? JSON.stringify({
"addon-name": addonName,
})
: ""
}
/>
</Localized>
)}
{content.additional_button ? (
<AdditionalCTA content={content} handleAction={props.handleAction} />
) : null}
@ -443,12 +475,30 @@ export class ProtonScreen extends React.PureComponent {
return <>{elements}</>;
}
renderRTAMOIcon(addonType, themeScreenshots, addonIconURL) {
return (
<div className="rtamo-icon">
<img
className={`${addonType?.includes("theme") ? "rtamo-theme-icon" : "brand-logo"}`}
src={
addonType?.includes("theme")
? themeScreenshots[0].url
: addonIconURL
}
loading={AboutWelcomeUtils.getLoadingStrategyFor(addonIconURL)}
alt=""
role="presentation"
/>
</div>
);
}
render() {
const {
autoAdvance,
content,
isRtamo,
isTheme,
addonType,
isFirstScreen,
isLastScreen,
isSingleScreen,
@ -558,19 +608,13 @@ export class ProtonScreen extends React.PureComponent {
? this.renderPicture(content.logo)
: null}
{isRtamo ? (
<div className="rtamo-icon">
<img
className={`${isTheme ? "rtamo-theme-icon" : "brand-logo"}`}
src={this.props.iconURL}
loading={AboutWelcomeUtils.getLoadingStrategyFor(
this.props.iconURL
)}
alt=""
role="presentation"
/>
</div>
) : null}
{isRtamo && !content.fullscreen
? this.renderRTAMOIcon(
addonType,
this.props.themeScreenshots,
this.props.addonIconURL
)
: null}
<div
className="main-content-inner"
@ -581,6 +625,13 @@ export class ProtonScreen extends React.PureComponent {
{content.logo && content.fullscreen
? this.renderPicture(content.logo)
: null}
{isRtamo && content.fullscreen
? this.renderRTAMOIcon(
addonType,
this.props.themeScreenshots,
this.props.addonIconURL
)
: null}
{content.title || content.subtitle ? (
<div
id="multi-stage-message-welcome-text"
@ -607,7 +658,11 @@ export class ProtonScreen extends React.PureComponent {
{content.action_buttons_above_content && (
<ProtonScreenActionButtons
content={content}
isRtamo={this.props.isRtamo}
installedAddons={this.props.installedAddons}
addonId={this.props.addonId}
addonName={this.props.addonName}
addonType={this.props.addonType}
handleAction={this.props.handleAction}
activeMultiSelect={this.props.activeMultiSelect}
/>
@ -637,7 +692,11 @@ export class ProtonScreen extends React.PureComponent {
{!content.action_buttons_above_content && (
<ProtonScreenActionButtons
content={content}
isRtamo={this.props.isRtamo}
installedAddons={this.props.installedAddons}
addonId={this.props.addonId}
addonName={this.props.addonName}
addonType={this.props.addonType}
handleAction={this.props.handleAction}
activeMultiSelect={this.props.activeMultiSelect}
/>

View file

@ -1,105 +0,0 @@
/* 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 React from "react";
import {
AboutWelcomeUtils,
DEFAULT_RTAMO_CONTENT,
} from "../lib/aboutwelcome-utils.mjs";
import { MultiStageProtonScreen } from "./MultiStageProtonScreen";
import { BASE_PARAMS } from "../lib/addUtmParams.mjs";
export class ReturnToAMO extends React.PureComponent {
constructor(props) {
super(props);
this.fetchFlowParams = this.fetchFlowParams.bind(this);
this.handleAction = this.handleAction.bind(this);
}
async fetchFlowParams() {
if (this.props.metricsFlowUri) {
this.setState({
flowParams: await AboutWelcomeUtils.fetchFlowParams(
this.props.metricsFlowUri
),
});
}
}
componentDidUpdate() {
this.fetchFlowParams();
}
handleAction(event) {
const { content, message_id, url, utm_term } = this.props;
let { action, source_id } = content[event.currentTarget.value];
let { type, data } = action;
if (type === "INSTALL_ADDON_FROM_URL") {
if (!data) {
return;
}
// Set add-on url in action.data.url property from JSON
data = { ...data, url };
} else if (type === "SHOW_FIREFOX_ACCOUNTS") {
let params = {
...BASE_PARAMS,
utm_term: `aboutwelcome-${utm_term}-screen`,
};
if (action.addFlowParams && this.state.flowParams) {
params = {
...params,
...this.state.flowParams,
};
}
data = { ...data, extraParams: params };
}
AboutWelcomeUtils.handleUserAction({ type, data });
AboutWelcomeUtils.sendActionTelemetry(message_id, source_id);
}
render() {
const { content, type } = this.props;
if (!content) {
return null;
}
if (content?.primary_button.label) {
content.primary_button.label.string_id = type.includes("theme")
? "return-to-amo-add-theme-label"
: "mr1-return-to-amo-add-extension-label";
}
// For experiments, when needed below rendered UI allows settings hard coded strings
// directly inside JSON except for ReturnToAMOText which picks add-on name and icon from fluent string
return (
<div
className={"outer-wrapper onboardingContainer proton"}
style={content.backdrop ? { background: content.backdrop } : {}}
>
<MultiStageProtonScreen
content={content}
isRtamo={true}
isTheme={type.includes("theme")}
id={this.props.message_id}
order={this.props.order || 0}
totalNumberOfScreens={1}
isSingleScreen={true}
autoAdvance={this.props.auto_advance}
iconURL={
type.includes("theme")
? this.props.themeScreenshots[0]?.url
: this.props.iconURL
}
addonName={this.props.name}
handleAction={this.handleAction}
/>
</div>
);
}
}
ReturnToAMO.defaultProps = DEFAULT_RTAMO_CONTENT;

View file

@ -91,52 +91,3 @@ export const AboutWelcomeUtils = {
}, {});
},
};
export const DEFAULT_RTAMO_CONTENT = {
template: "return_to_amo",
utm_term: "rtamo",
content: {
position: "split",
title: { string_id: "mr1-return-to-amo-subtitle" },
has_noodles: false,
subtitle: {
string_id: "mr1-return-to-amo-addon-title",
},
backdrop:
"var(--mr-welcome-background-color) var(--mr-welcome-background-gradient)",
background:
"url('chrome://activity-stream/content/data/content/assets/mr-rtamo-background-image.svg') no-repeat center",
progress_bar: true,
primary_button: {
label: { string_id: "mr1-return-to-amo-add-extension-label" },
source_id: "ADD_EXTENSION_BUTTON",
action: {
type: "INSTALL_ADDON_FROM_URL",
data: { url: null, telemetrySource: "rtamo" },
},
},
secondary_button: {
label: {
string_id: "onboarding-not-now-button-label",
},
source_id: "RTAMO_START_BROWSING_BUTTON",
action: {
type: "OPEN_AWESOME_BAR",
},
},
secondary_button_top: {
label: {
string_id: "mr1-onboarding-sign-in-button-label",
},
source_id: "RTAMO_FXA_SIGNIN_BUTTON",
action: {
data: {
entrypoint: "activity-stream-firstrun",
where: "tab",
},
type: "SHOW_FIREFOX_ACCOUNTS",
addFlowParams: true,
},
},
},
};

View file

@ -25,8 +25,7 @@ module.exports = ReactDOM;
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ AboutWelcomeUtils: () => (/* binding */ AboutWelcomeUtils),
/* harmony export */ DEFAULT_RTAMO_CONTENT: () => (/* binding */ DEFAULT_RTAMO_CONTENT)
/* harmony export */ AboutWelcomeUtils: () => (/* binding */ AboutWelcomeUtils)
/* harmony export */ });
/* 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,
@ -122,55 +121,6 @@ const AboutWelcomeUtils = {
},
};
const DEFAULT_RTAMO_CONTENT = {
template: "return_to_amo",
utm_term: "rtamo",
content: {
position: "split",
title: { string_id: "mr1-return-to-amo-subtitle" },
has_noodles: false,
subtitle: {
string_id: "mr1-return-to-amo-addon-title",
},
backdrop:
"var(--mr-welcome-background-color) var(--mr-welcome-background-gradient)",
background:
"url('chrome://activity-stream/content/data/content/assets/mr-rtamo-background-image.svg') no-repeat center",
progress_bar: true,
primary_button: {
label: { string_id: "mr1-return-to-amo-add-extension-label" },
source_id: "ADD_EXTENSION_BUTTON",
action: {
type: "INSTALL_ADDON_FROM_URL",
data: { url: null, telemetrySource: "rtamo" },
},
},
secondary_button: {
label: {
string_id: "onboarding-not-now-button-label",
},
source_id: "RTAMO_START_BROWSING_BUTTON",
action: {
type: "OPEN_AWESOME_BAR",
},
},
secondary_button_top: {
label: {
string_id: "mr1-onboarding-sign-in-button-label",
},
source_id: "RTAMO_FXA_SIGNIN_BUTTON",
action: {
data: {
entrypoint: "activity-stream-firstrun",
where: "tab",
},
type: "SHOW_FIREFOX_ACCOUNTS",
addFlowParams: true,
},
},
},
};
/***/ }),
/* 4 */
@ -191,7 +141,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6);
/* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(7);
/* harmony import */ var _SubmenuButton__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(12);
/* harmony import */ var _lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(22);
/* harmony import */ var _lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(23);
/* 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/. */
@ -467,7 +417,14 @@ const MultiStageAboutWelcome = props => {
ariaRole: props.ariaRole,
aboveButtonStepsIndicator: currentScreen.above_button_steps_indicator,
installedAddons: installedAddons,
setInstalledAddons: setInstalledAddons
setInstalledAddons: setInstalledAddons,
addonId: props.addonId,
addonType: props.addonType,
addonName: props.addonName,
addonURL: props.addonURL,
addonIconURL: props.addonIconURL,
themeScreenshots: props.themeScreenshots,
isRtamo: currentScreen.content.isRtamo
}) : null;
})));
};
@ -636,6 +593,18 @@ class WelcomeScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCo
if (action.type === "FXA_SIGNIN_FLOW") {
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, actionResult ? "sign_in" : "sign_in_cancel", "FXA_SIGNIN_FLOW");
}
if (action.type === "INSTALL_ADDON_FROM_URL") {
const url = props.addonURL;
if (!action.data) {
return;
}
// Set add-on url in action.data.url property from JSON
action.data = {
...action.data,
url
};
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction(action);
}
// Wait until migration closes to complete the action
const hasMigrate = a => a.type === "SHOW_MIGRATION_WIZARD" || a.type === "MULTI_ACTION" && a.data?.actions?.some(hasMigrate);
if (hasMigrate(action)) {
@ -785,7 +754,14 @@ class WelcomeScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCo
autoAdvance: this.props.autoAdvance,
forceHideStepsIndicator: this.props.forceHideStepsIndicator,
ariaRole: this.props.ariaRole,
aboveButtonStepsIndicator: this.props.aboveButtonStepsIndicator
aboveButtonStepsIndicator: this.props.aboveButtonStepsIndicator,
addonId: this.props.addonId,
addonType: this.props.addonType,
addonName: this.props.addonName,
addonURL: this.props.addonURL,
addonIconURL: this.props.addonIconURL,
themeScreenshots: this.props.themeScreenshots,
isRtamo: this.props.content.isRtamo
});
}
}
@ -924,6 +900,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _AdditionalCTA__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(11);
/* harmony import */ var _LinkParagraph__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(13);
/* harmony import */ var _ContentTiles__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(14);
/* harmony import */ var _InstallButton__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(16);
/* 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/. */
@ -939,6 +916,7 @@ __webpack_require__.r(__webpack_exports__);
const MultiStageProtonScreen = props => {
const {
autoAdvance,
@ -989,9 +967,12 @@ const MultiStageProtonScreen = props => {
previousOrder: props.previousOrder,
autoAdvance: props.autoAdvance,
isRtamo: props.isRtamo,
addonId: props.addonId,
addonType: props.addonType,
addonName: props.addonName,
isTheme: props.isTheme,
iconURL: props.iconURL,
addonURL: props.addonURL,
addonIconURL: props.addonIconURL,
themeScreenshots: props.themeScreenshots,
messageId: props.messageId,
negotiatedLanguage: props.negotiatedLanguage,
langPackInstallPhase: props.langPackInstallPhase,
@ -1003,8 +984,12 @@ const MultiStageProtonScreen = props => {
const ProtonScreenActionButtons = props => {
const {
content,
isRtamo,
addonId,
addonType,
addonName,
activeMultiSelect
activeMultiSelect,
installedAddons
} = props;
const defaultValue = content.checkbox?.defaultValue;
const [isChecked, setIsChecked] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(defaultValue || false);
@ -1018,6 +1003,9 @@ const ProtonScreenActionButtons = props => {
if (!content.primary_button && !content.secondary_button && !content.additional_button) {
return null;
}
if (isRtamo) {
content.primary_button.label.string_id = addonType?.includes("theme") ? "return-to-amo-add-theme-label" : "mr1-return-to-amo-add-extension-label";
}
// If we have a multi-select screen, we want to disable the primary button
// until the user has selected at least one item.
@ -1041,7 +1029,17 @@ const ProtonScreenActionButtons = props => {
className: `action-buttons ${content.additional_button ? "additional-cta-container" : ""}`,
flow: content.additional_button?.flow,
alignment: content.additional_button?.alignment
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
}, isRtamo ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_InstallButton__WEBPACK_IMPORTED_MODULE_11__.InstallButton, {
key: addonId,
addonId: addonId,
addonType: addonType,
addonName: addonName,
index: "primary_button",
handleAction: props.handleAction,
installedAddons: installedAddons,
install_label: content.primary_button.label,
install_complete_label: content.primary_button.install_complete_label
}) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
text: content.primary_button?.label
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
ref: buttonRef,
@ -1293,12 +1291,23 @@ class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
}
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, elements);
}
renderRTAMOIcon(addonType, themeScreenshots, addonIconURL) {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "rtamo-icon"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
className: `${addonType?.includes("theme") ? "rtamo-theme-icon" : "brand-logo"}`,
src: addonType?.includes("theme") ? themeScreenshots[0].url : addonIconURL,
loading: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getLoadingStrategyFor(addonIconURL),
alt: "",
role: "presentation"
}));
}
render() {
const {
autoAdvance,
content,
isRtamo,
isTheme,
addonType,
isFirstScreen,
isLastScreen,
isSingleScreen,
@ -1348,20 +1357,12 @@ class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
paddingBlock: content.split_content_padding_block ? content.split_content_padding_block : null,
paddingInline: content.split_content_padding_inline ? content.split_content_padding_inline : null
}
}, content.logo && !content.fullscreen ? this.renderPicture(content.logo) : null, isRtamo ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "rtamo-icon"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
className: `${isTheme ? "rtamo-theme-icon" : "brand-logo"}`,
src: this.props.iconURL,
loading: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getLoadingStrategyFor(this.props.iconURL),
alt: "",
role: "presentation"
})) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
}, content.logo && !content.fullscreen ? this.renderPicture(content.logo) : null, isRtamo && !content.fullscreen ? this.renderRTAMOIcon(addonType, this.props.themeScreenshots, this.props.addonIconURL) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "main-content-inner",
style: {
justifyContent: content.split_content_justify_content
}
}, content.logo && content.fullscreen ? this.renderPicture(content.logo) : null, content.title || content.subtitle ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
}, content.logo && content.fullscreen ? this.renderPicture(content.logo) : null, isRtamo && content.fullscreen ? this.renderRTAMOIcon(addonType, this.props.themeScreenshots, this.props.addonIconURL) : null, content.title || content.subtitle ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
id: "multi-stage-message-welcome-text",
className: `welcome-text ${content.title_style || ""}`
}, content.title ? this.renderTitle(content) : null, content.subtitle ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
@ -1375,7 +1376,11 @@ class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
id: "mainContentSubheader"
})) : null, content.action_buttons_above_content && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(ProtonScreenActionButtons, {
content: content,
isRtamo: this.props.isRtamo,
installedAddons: this.props.installedAddons,
addonId: this.props.addonId,
addonName: this.props.addonName,
addonType: this.props.addonType,
handleAction: this.props.handleAction,
activeMultiSelect: this.props.activeMultiSelect
}), content.cta_paragraph ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_CTAParagraph__WEBPACK_IMPORTED_MODULE_5__.CTAParagraph, {
@ -1386,7 +1391,11 @@ class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
handleAction: this.props.handleAction
}) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_ContentTiles__WEBPACK_IMPORTED_MODULE_10__.ContentTiles, this.props), this.renderLanguageSwitcher(), content.above_button_content ? this.renderOrderedContent(content.above_button_content) : null, !hideStepsIndicator && aboveButtonStepsIndicator ? this.renderStepsIndicator() : null, !content.action_buttons_above_content && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(ProtonScreenActionButtons, {
content: content,
isRtamo: this.props.isRtamo,
installedAddons: this.props.installedAddons,
addonId: this.props.addonId,
addonName: this.props.addonName,
addonType: this.props.addonType,
handleAction: this.props.handleAction,
activeMultiSelect: this.props.activeMultiSelect
}),
@ -2057,12 +2066,12 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
/* harmony import */ var _AddonsPicker__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(15);
/* harmony import */ var _SingleSelect__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(16);
/* harmony import */ var _MobileDownloads__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(17);
/* harmony import */ var _MultiSelect__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(18);
/* harmony import */ var _EmbeddedMigrationWizard__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(19);
/* harmony import */ var _ActionChecklist__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(20);
/* harmony import */ var _EmbeddedBrowser__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(21);
/* harmony import */ var _SingleSelect__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(17);
/* harmony import */ var _MobileDownloads__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(18);
/* harmony import */ var _MultiSelect__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(19);
/* harmony import */ var _EmbeddedMigrationWizard__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(20);
/* harmony import */ var _ActionChecklist__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(21);
/* harmony import */ var _EmbeddedBrowser__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(22);
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(3);
/* 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,
@ -2252,14 +2261,13 @@ const ContentTiles = props => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ AddonsPicker: () => (/* binding */ AddonsPicker),
/* harmony export */ InstallButton: () => (/* binding */ InstallButton),
/* harmony export */ Loader: () => (/* binding */ Loader)
/* harmony export */ AddonsPicker: () => (/* binding */ AddonsPicker)
/* harmony export */ });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5);
/* harmony import */ var _InstallButton__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(16);
/* 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/. */
@ -2267,55 +2275,7 @@ __webpack_require__.r(__webpack_exports__);
const Loader = () => {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
className: "primary"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "loaderContainer"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
className: "loader"
})));
};
const InstallButton = props => {
// determine if the addon is already installed so the state is
// consistent on refresh or navigation
const [installing, setInstalling] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
const [installComplete, setInstallComplete] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
const defaultInstallLabel = {
string_id: "amo-picker-install-button-label"
};
const defaultInstallCompleteLabel = {
string_id: "amo-picker-install-complete-label"
};
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
setInstallComplete(props.installedAddons?.includes(props.addonId));
}, [props.addonId, props.installedAddons]);
let buttonLabel = installComplete ? props.install_complete_label || defaultInstallCompleteLabel : props.install_label || defaultInstallLabel;
function onClick(event) {
props.handleAction(event);
// Replace the label with the spinner
setInstalling(true);
window.AWEnsureAddonInstalled(props.addonId).then(value => {
if (value === "complete") {
// Set the label to "Installed"
setInstallComplete(true);
}
// Whether the addon installs or not, we want to remove the spinner
setInstalling(false);
});
}
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "install-button-wrapper"
}, installing ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(Loader, null) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_2__.Localized, {
text: buttonLabel
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
id: `install-button-${props.addonId}`,
value: props.index,
onClick: onClick,
disabled: installComplete,
className: "primary"
})));
};
const AddonsPicker = props => {
const {
content,
@ -2401,7 +2361,7 @@ const AddonsPicker = props => {
handleAuthorClick(e, author.id);
},
className: "author-link"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, author.name)))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(InstallButton, {
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, author.name)))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_InstallButton__WEBPACK_IMPORTED_MODULE_3__.InstallButton, {
key: id,
addonId: id,
handleAction: handleAction,
@ -2425,7 +2385,7 @@ const AddonsPicker = props => {
text: description
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "addon-description"
}))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(InstallButton, {
}))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_InstallButton__WEBPACK_IMPORTED_MODULE_3__.InstallButton, {
key: id,
addonId: id,
handleAction: handleAction,
@ -2440,6 +2400,91 @@ const AddonsPicker = props => {
/* 16 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ InstallButton: () => (/* binding */ InstallButton),
/* harmony export */ Loader: () => (/* binding */ Loader)
/* harmony export */ });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
/* 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 Loader = () => {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
className: "primary"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "loaderContainer"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
className: "loader"
})));
};
const InstallButton = props => {
// determine if the addon is already installed so the state is
// consistent on refresh or navigation
const [installing, setInstalling] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
const [installComplete, setInstallComplete] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
const defaultInstallLabel = {
string_id: "amo-picker-install-button-label"
};
function getDefaultInstallCompleteLabel(addonType = "") {
let defaultInstallCompleteLabel;
if (addonType && addonType === "theme") {
defaultInstallCompleteLabel = {
string_id: "return-to-amo-theme-install-complete-label"
};
} else if (addonType && addonType === "extension") {
defaultInstallCompleteLabel = {
string_id: "return-to-amo-extension-install-complete-label"
};
} else {
defaultInstallCompleteLabel = {
string_id: "amo-picker-install-complete-label"
};
}
return defaultInstallCompleteLabel;
}
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
setInstallComplete(props.installedAddons?.includes(props.addonId));
}, [props.addonId, props.installedAddons]);
let buttonLabel = installComplete ? props.install_complete_label || getDefaultInstallCompleteLabel(props.addonType) : props.install_label || defaultInstallLabel;
function onClick(event) {
props.handleAction(event);
// Replace the label with the spinner
setInstalling(true);
window.AWEnsureAddonInstalled(props.addonId).then(value => {
if (value === "complete") {
// Set the label to "Installed"
setInstallComplete(true);
}
// Whether the addon installs or not, we want to remove the spinner
setInstalling(false);
});
}
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "install-button-wrapper"
}, installing ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(Loader, null) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
text: buttonLabel
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
id: `install-button-${props.addonId}`,
value: props.index,
onClick: onClick,
disabled: installComplete,
className: "primary",
"data-l10n-args": JSON.stringify({
"addon-name": props.addonName ?? ""
})
})));
};
/***/ }),
/* 17 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ SingleSelect: () => (/* binding */ SingleSelect)
@ -2579,7 +2624,7 @@ const SingleSelect = ({
};
/***/ }),
/* 17 */
/* 18 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
@ -2641,7 +2686,7 @@ const MobileDownloads = props => {
};
/***/ }),
/* 18 */
/* 19 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
@ -2828,7 +2873,7 @@ const MultiSelect = ({
};
/***/ }),
/* 19 */
/* 20 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
@ -2899,7 +2944,7 @@ const EmbeddedMigrationWizard = ({
};
/***/ }),
/* 20 */
/* 21 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
@ -3057,7 +3102,7 @@ const ActionChecklist = ({
};
/***/ }),
/* 21 */
/* 22 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
@ -3125,7 +3170,7 @@ const EmbeddedBrowserInner = ({
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (EmbeddedBrowser);
/***/ }),
/* 22 */
/* 23 */
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
@ -3167,125 +3212,6 @@ function addUtmParams(url, utmTerm) {
}
/***/ }),
/* 23 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ ReturnToAMO: () => (/* binding */ ReturnToAMO)
/* harmony export */ });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
/* harmony import */ var _MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6);
/* harmony import */ var _lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(22);
/* 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/. */
class ReturnToAMO extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
constructor(props) {
super(props);
this.fetchFlowParams = this.fetchFlowParams.bind(this);
this.handleAction = this.handleAction.bind(this);
}
async fetchFlowParams() {
if (this.props.metricsFlowUri) {
this.setState({
flowParams: await _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.fetchFlowParams(this.props.metricsFlowUri)
});
}
}
componentDidUpdate() {
this.fetchFlowParams();
}
handleAction(event) {
const {
content,
message_id,
url,
utm_term
} = this.props;
let {
action,
source_id
} = content[event.currentTarget.value];
let {
type,
data
} = action;
if (type === "INSTALL_ADDON_FROM_URL") {
if (!data) {
return;
}
// Set add-on url in action.data.url property from JSON
data = {
...data,
url
};
} else if (type === "SHOW_FIREFOX_ACCOUNTS") {
let params = {
..._lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_3__.BASE_PARAMS,
utm_term: `aboutwelcome-${utm_term}-screen`
};
if (action.addFlowParams && this.state.flowParams) {
params = {
...params,
...this.state.flowParams
};
}
data = {
...data,
extraParams: params
};
}
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.handleUserAction({
type,
data
});
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.sendActionTelemetry(message_id, source_id);
}
render() {
const {
content,
type
} = this.props;
if (!content) {
return null;
}
if (content?.primary_button.label) {
content.primary_button.label.string_id = type.includes("theme") ? "return-to-amo-add-theme-label" : "mr1-return-to-amo-add-extension-label";
}
// For experiments, when needed below rendered UI allows settings hard coded strings
// directly inside JSON except for ReturnToAMOText which picks add-on name and icon from fluent string
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "outer-wrapper onboardingContainer proton",
style: content.backdrop ? {
background: content.backdrop
} : {}
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_2__.MultiStageProtonScreen, {
content: content,
isRtamo: true,
isTheme: type.includes("theme"),
id: this.props.message_id,
order: this.props.order || 0,
totalNumberOfScreens: 1,
isSingleScreen: true,
autoAdvance: this.props.auto_advance,
iconURL: type.includes("theme") ? this.props.themeScreenshots[0]?.url : this.props.iconURL,
addonName: this.props.name,
handleAction: this.handleAction
}));
}
}
ReturnToAMO.defaultProps = _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.DEFAULT_RTAMO_CONTENT;
/***/ })
/******/ ]);
/************************************************************************/
@ -3365,7 +3291,6 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
/* harmony import */ var _components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
/* harmony import */ var _components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(23);
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
/* 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,
@ -3375,7 +3300,6 @@ function _extends() { _extends = Object.assign ? Object.assign.bind() : function
class AboutWelcome extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
constructor(props) {
super(props);
@ -3428,18 +3352,13 @@ class AboutWelcome extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
const {
props
} = this;
if (props.template === "return_to_amo") {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_4__.ReturnToAMO, {
message_id: props.messageId,
type: props.type,
name: props.name,
url: props.url,
iconURL: props.iconURL,
themeScreenshots: props.screenshots,
metricsFlowUri: this.state.metricsFlowUri
});
}
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__.MultiStageAboutWelcome, {
addonId: props.addonId,
addonType: props.type,
addonName: props.name || "",
addonURL: props.url,
addonIconURL: props.iconURL,
themeScreenshots: props.screenshots,
message_id: props.messageId,
defaultScreens: props.screens,
updateHistory: !props.disableHistoryUpdates,

View file

@ -1858,6 +1858,29 @@ html {
.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .secondary-cta .arrow-icon:dir(rtl)::after {
background-image: url("chrome://browser/skin/back.svg");
}
.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .loader {
width: 1em;
height: 1em;
border: 3px solid var(--in-content-primary-button-text-color);
border-bottom-color: transparent;
border-radius: 50%;
box-sizing: border-box;
animation: rotation 1s linear infinite;
justify-self: center;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .loaderContainer {
display: flex;
padding: 1.5px 37.5px;
margin: auto;
}
.onboardingContainer .screen[pos=split] .section-main .main-content .logo-container {
text-align: start;
}

View file

@ -169,6 +169,14 @@ module.exports = function (config) {
"content-src/components/MultiStageProtonScreen.jsx": {
branches: 79.07,
},
// The install actions and dynamic label of the InstallButton are covered
// in the browser test browser_aboutwelcome_multistage_addonspicker.js.
"content-src/components/InstallButton.jsx": {
statements: 60,
lines: 60,
functions: 60,
branches: 30,
},
},
},
},

View file

@ -38,6 +38,53 @@ const MR_ABOUT_WELCOME_DEFAULT = {
backdrop:
"var(--mr-welcome-background-color) var(--mr-welcome-background-gradient)",
screens: [
{
id: "RETURN_TO_AMO",
targeting: "isRTAMO",
content: {
fullscreen: true,
position: "split",
title: { string_id: "mr1-return-to-amo-subtitle" },
isRtamo: true,
subtitle: {
string_id: "mr1-return-to-amo-addon-title",
},
backdrop:
"var(--mr-welcome-background-color) var(--mr-welcome-background-gradient)",
background:
"url('chrome://activity-stream/content/data/content/assets/mr-rtamo-background-image.svg') center center / cover no-repeat",
progress_bar: true,
primary_button: {
label: { string_id: "mr1-return-to-amo-add-extension-label" },
action: {
type: "INSTALL_ADDON_FROM_URL",
data: { url: null, telemetrySource: "rtamo" },
},
},
secondary_button: {
label: {
string_id: "mr2022-onboarding-secondary-skip-button-label",
},
action: {
navigate: true,
},
has_arrow_icon: true,
},
secondary_button_top: {
label: {
string_id: "mr1-onboarding-sign-in-button-label",
},
action: {
data: {
entrypoint: "activity-stream-firstrun",
where: "tab",
},
type: "SHOW_FIREFOX_ACCOUNTS",
addFlowParams: true,
},
},
},
},
{
id: "AW_WELCOME_BACK",
targeting: "isDeviceMigration",
@ -756,6 +803,7 @@ async function getAddonFromRepository(data) {
}
return {
addonId: addonInfo.id,
name: addonInfo.name,
url: addonInfo.sourceURI.spec,
iconURL: addonInfo.icons["64"] || addonInfo.icons["32"],
@ -802,6 +850,7 @@ async function getAttributionContent() {
if (addonInfo) {
return {
...addonInfo,
ua: decodeURIComponent(attribution.ua),
template: "return_to_amo",
};
}

View file

@ -535,7 +535,7 @@ add_task(async function test_aboutwelcome_with_progress_bar() {
for (let [key, val] of Object.entries({
// The filled "completed" element should have
// `background-color: var(--in-content-primary-button-background);`
"background-color": "rgb(0, 97, 224)",
"background-color": "oklch(0.55 0.24 260)",
// Base progress bar step styles.
height: "6px",
"margin-inline": "-1px",

View file

@ -55,6 +55,7 @@ async function openAboutWelcome() {
info(`evaluateScreenTargeting called with args: ${args}`);
// Renders easy setup import screen as first screen to prevent pin/default dialog boxes breaking tests
const falseTargeting = [
"isRTAMO",
"doesAppNeedPin && (unhandledCampaignAction != 'SET_DEFAULT_BROWSER') && 'browser.shell.checkDefaultBrowser'|preferenceValue && !isDefaultBrowser",
"!doesAppNeedPin && (unhandledCampaignAction != 'SET_DEFAULT_BROWSER') && 'browser.shell.checkDefaultBrowser'|preferenceValue && !isDefaultBrowser",
"doesAppNeedPin && (!'browser.shell.checkDefaultBrowser'|preferenceValue || isDefaultBrowser || (unhandledCampaignAction == 'SET_DEFAULT_BROWSER'))",

View file

@ -103,21 +103,6 @@ async function test_screen_content(
);
}
async function onButtonClick(browser, elementId) {
await ContentTask.spawn(
browser,
{ elementId },
async ({ elementId: buttonId }) => {
await ContentTaskUtils.waitForCondition(
() => content.document.querySelector(buttonId),
buttonId
);
let button = content.document.querySelector(buttonId);
button.click();
}
);
}
/**
* Test the RTAMO welcome UI
*/
@ -137,7 +122,7 @@ add_task(async function test_rtamo_aboutwelcome() {
`h2[data-l10n-args='{"addon-name":"${TEST_ADDON_INFO[0].name}"}'`,
"div.rtamo-icon",
"button.primary[data-l10n-id='mr1-return-to-amo-add-extension-label']",
"button[data-l10n-id='onboarding-not-now-button-label']",
"button[data-l10n-id='mr2022-onboarding-secondary-skip-button-label']",
],
// Unexpected selectors:
[
@ -148,12 +133,6 @@ add_task(async function test_rtamo_aboutwelcome() {
]
);
await onButtonClick(
browser,
"button[data-l10n-id='onboarding-not-now-button-label']"
);
Assert.ok(gURLBar.focused, "Focus should be on awesome bar");
let windowGlobalParent = browser.browsingContext.currentWindowGlobal;
let aboutWelcomeActor = windowGlobalParent.getActor("AboutWelcome");
const messageSandbox = sinon.createSandbox();
@ -165,13 +144,32 @@ add_task(async function test_rtamo_aboutwelcome() {
await onButtonClick(browser, "button.primary");
const { callCount } = aboutWelcomeActor.onContentMessage;
Assert.strictEqual(
callCount,
2,
`${callCount} Stub called twice to install extension and send telemetry`
4,
`${callCount} Stub called four times to send telemetry, set url, install extension and verify install`
);
const installExtensionCall = aboutWelcomeActor.onContentMessage.getCall(0);
const sendTelemetryCall = aboutWelcomeActor.onContentMessage.getCall(0);
const setUrlCall = aboutWelcomeActor.onContentMessage.getCall(1);
const installExtensionCall = aboutWelcomeActor.onContentMessage.getCall(2);
Assert.equal(
sendTelemetryCall.args[0],
"AWPage:TELEMETRY_EVENT",
"send event telemetry"
);
Assert.equal(
sendTelemetryCall.args[1].event,
"CLICK_BUTTON",
"Telemetry event sent as INSTALL"
);
Assert.equal(
setUrlCall.args[0],
"AWPage:SPECIAL_ACTION",
"send special action to set extension URL"
);
Assert.equal(
installExtensionCall.args[0],
"AWPage:SPECIAL_ACTION",
@ -187,81 +185,6 @@ add_task(async function test_rtamo_aboutwelcome() {
"https://test.xpi",
"Install add on url"
);
Assert.equal(
installExtensionCall.args[1].data.telemetrySource,
"rtamo",
"Install add on telemetry source"
);
const telemetryCall = aboutWelcomeActor.onContentMessage.getCall(1);
Assert.equal(
telemetryCall.args[0],
"AWPage:TELEMETRY_EVENT",
"send add extension telemetry"
);
Assert.equal(
telemetryCall.args[1].event,
"CLICK_BUTTON",
"Telemetry event sent as INSTALL"
);
Assert.equal(
telemetryCall.args[1].event_context.source,
"ADD_EXTENSION_BUTTON",
"Source of the event is Add Extension Button"
);
Assert.equal(
telemetryCall.args[1].message_id,
"RTAMO_DEFAULT_WELCOME_EXTENSION",
"Message Id sent in telemetry for default RTAMO"
);
sandbox.restore();
});
add_task(async function test_rtamo_over_experiments() {
let sandbox = sinon.createSandbox();
sandbox.stub(AddonRepository, "getAddonsByIDs").resolves(TEST_ADDON_INFO);
let doExperimentCleanup = await ExperimentFakes.enrollWithFeatureConfig({
featureId: "aboutwelcome",
value: { screens: [], enabled: true },
});
let browser = await openRTAMOWelcomePage();
// If addon attribution exist, we should see RTAMO even if enrolled
// in about:welcome experiment
await test_screen_content(
browser,
"Experiment RTAMO UI",
// Expected selectors:
["h2[data-l10n-id='mr1-return-to-amo-addon-title']"],
// Unexpected selectors:
[]
);
doExperimentCleanup();
browser = await openRTAMOWelcomePage();
await test_screen_content(
browser,
"No Experiment RTAMO UI",
// Expected selectors:
[
"div.onboardingContainer",
"h2[data-l10n-id='mr1-return-to-amo-addon-title']",
"div.rtamo-icon",
"button.primary",
"button.secondary",
],
// Unexpected selectors:
[
"main.AW_STEP1",
"main.AW_STEP2",
"main.AW_STEP3",
"div.tiles-container.info",
]
);
sandbox.restore();
});
@ -283,7 +206,7 @@ add_task(async function test_rtamo_primary_button_theme() {
"h2[data-l10n-id='mr1-return-to-amo-addon-title']",
"div.rtamo-icon",
"button.primary[data-l10n-id='return-to-amo-add-theme-label']",
"button[data-l10n-id='onboarding-not-now-button-label']",
"button[data-l10n-id='mr2022-onboarding-secondary-skip-button-label']",
"img.rtamo-theme-icon",
],
// Unexpected selectors:

View file

@ -0,0 +1,49 @@
import React from "react";
import { shallow } from "enzyme";
import { InstallButton } from "content-src/components/InstallButton";
const TEST_ADDON_INFO = [
{
name: "Test Add-on",
id: "d634138d-c276-4fc8-924b-40a0ea21d284",
url: "http://example.com",
icons: { 32: "test.png", 64: "test.png" },
type: "extension",
},
];
describe("InstallButton component", () => {
let sandbox;
let wrapper;
let handleAction;
beforeEach(() => {
sandbox = sinon.createSandbox();
handleAction = sandbox.stub();
wrapper = shallow(
<InstallButton
key={TEST_ADDON_INFO.id}
addonId={TEST_ADDON_INFO.id}
addonType={TEST_ADDON_INFO.type}
addonName={TEST_ADDON_INFO.name}
index={"primary_button"}
handleAction={handleAction}
installedAddons={[]}
/>
);
});
it("should render InstallButton component", () => {
assert.ok(wrapper.exists());
});
it("should render the button with the correct value", () => {
assert.lengthOf(wrapper.find("button[value='primary_button']"), 1);
});
it("should call handleAction method when button is link is clicked", () => {
const btnLink = wrapper.find("button.primary");
btnLink.simulate("click");
assert.calledOnce(handleAction);
});
});

View file

@ -721,7 +721,7 @@ describe("MultiStageAboutWelcomeProton module", () => {
);
});
it("should have a multi action primary button by default", async () => {
const data = await prepConfig({}, ["AW_WELCOME_BACK"]);
const data = await prepConfig({}, ["AW_WELCOME_BACK", "RETURN_TO_AMO"]);
assert.propertyVal(
data.screens[0].content.primary_button.action,
"type",

View file

@ -561,6 +561,9 @@ const TEST_GLOBAL = {
},
Logger: FakeLogger,
getFxAccountsSingleton() {},
AWEnsureAddonInstalled() {
return Promise.resolve({ value: "complete" });
},
AboutNewTab: {},
Glean: {
newtab: {

View file

@ -126,12 +126,9 @@
#vertical-pinned-tabs-container &,
#tabbrowser-tabs[orient="vertical"] & {
position: absolute;
inset-inline-end: 0;
height: calc(100% - 4px);
margin: 0;
margin-inline-end: 3px;
height: auto;
width: 2px;
margin: 3px 0;
}
}

View file

@ -179,17 +179,6 @@
<html:a is="moz-support-link" when-mixedcontent="active-loaded"
and-when-loginforms="secure" class="identity-popup-mcb-learn-more" support-page="mixed-content"/>
</hbox>
<!-- Buttons to enable/disable mixed content blocking. -->
<button id="identity-popup-disable-mixed-content-blocking"
when-mixedcontent="active-blocked"
data-l10n-id="identity-disable-mixed-content-blocking"
class="panel-button"/>
<button id="identity-popup-enable-mixed-content-blocking"
when-mixedcontent="active-loaded"
data-l10n-id="identity-enable-mixed-content-blocking"
class="panel-button"/>
</vbox>
</vbox>

View file

@ -286,15 +286,16 @@ function generateDocumentation() {
let string_mapping = {
BackgroundAppUpdate: "BackgroundAppUpdate2",
Certificates: "CertificatesDescription",
DisableFirefoxAccounts: "DisableFirefoxAccounts1",
DisableMasterPasswordCreation: "DisablePrimaryPasswordCreation",
DisablePocket: "DisablePocket2",
DisableSetDesktopBackground: "DisableSetAsDesktopBackground",
FirefoxHome: "FirefoxHome2",
Permissions: "Permissions2",
SanitizeOnShutdown: "SanitizeOnShutdown2",
WindowsSSO: "Windows10SSO",
SecurityDevices: "SecurityDevices2",
DisableFirefoxAccounts: "DisableFirefoxAccounts1",
SkipTermsOfUse: "SkipTermsOfUse2",
WindowsSSO: "Windows10SSO",
};
for (let policyName in schema.properties) {

View file

@ -338,8 +338,6 @@ https_first_disabled = true
["browser_ext_originControls_internals.js"]
["browser_ext_overflow_underflow_events.js"]
["browser_ext_pageAction_activeTab.js"]
["browser_ext_pageAction_click_types.js"]

View file

@ -1,51 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
add_setup(async function setup() {
await SpecialPowers.pushPrefEnv({
set: [
["layout.overflow-underflow.content.enabled", false],
["layout.overflow-underflow.content.enabled_in_addons", true],
],
});
});
add_task(async function test_overflow_underflow_events_in_sidebar() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
sidebar_action: {
default_panel: "sidebar.html",
},
},
files: {
"sidebar.html": `<!DOCTYPE html>
<html>
<body>
<div id="div" style="height: 10px; overflow-x: hidden">
<div style="height: 10px"></div>
</div>
<script src="sidebar.js"></script>
</body>
</html>`,
"sidebar.js": async function () {
/* global div */
let overflow = new Promise(resolve =>
div.addEventListener("overflow", resolve, { once: true })
);
let underflow = new Promise(resolve =>
div.addEventListener("underflow", resolve, { once: true })
);
div.style.height = "5px";
await overflow;
div.style.height = "10px";
await underflow;
browser.test.notifyPass("overflow/underflow events in sidebar");
},
},
});
await extension.startup();
await extension.awaitFinish();
await extension.unload();
});

View file

@ -10,34 +10,30 @@
*/
add_task(async function () {
info("Setting browser to RTL locale");
await SpecialPowers.pushPrefEnv({ set: [["intl.l10n.pseudo", "bidi"]] });
// window.RTL_UI doesn't update in existing windows when this pref is changed,
// so we need to test in a new window.
let win = await BrowserTestUtils.openNewBrowserWindow();
await switchToWindow(win);
await BrowserTestUtils.enableRtlLocale();
const TEST_ROOT = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"https://example.com"
);
let newTab = win.gBrowser.tabs[0];
let newTab = gBrowser.tabs[0];
let waitForTestTabPromise = BrowserTestUtils.waitForNewTab(
win.gBrowser,
gBrowser,
TEST_ROOT + "file_new_tab_page.html"
);
let testTab = await BrowserTestUtils.openNewForegroundTab(
win.gBrowser,
gBrowser,
TEST_ROOT + "file_new_tab_page.html"
);
await waitForTestTabPromise;
let linkSrcEl = win.document.querySelector("a");
// FIXME (bug 1958909): This uses the wrong document.
let linkSrcEl = document.querySelector("a");
ok(linkSrcEl, "Link exists");
let dropPromise = BrowserTestUtils.waitForEvent(
win.gBrowser.tabContainer,
gBrowser.tabContainer,
"drop"
);
@ -46,11 +42,11 @@ add_task(async function () {
* 1. new tab (auto-generated)
* 2. test tab
*/
is(win.gBrowser.visibleTabs.length, 2, "There should be 2 tabs");
is(gBrowser.visibleTabs.length, 2, "There should be 2 tabs");
// Now open Firefox View tab
info("Opening Firefox View tab");
await openFirefoxViewTab(win);
await openFirefoxViewTab(window);
/**
* There should be 2 visible tabs:
@ -59,16 +55,16 @@ add_task(async function () {
* Firefox View tab is hidden.
*/
is(
win.gBrowser.visibleTabs.length,
gBrowser.visibleTabs.length,
2,
"There should still be 2 visible tabs after opening Firefox View tab"
);
info("Switching to test tab");
await BrowserTestUtils.switchTab(win.gBrowser, testTab);
await BrowserTestUtils.switchTab(gBrowser, testTab);
let waitForDraggedTabPromise = BrowserTestUtils.waitForNewTab(
win.gBrowser,
gBrowser,
"https://example.com/#test"
);
@ -78,8 +74,8 @@ add_task(async function () {
testTab,
[[{ type: "text/plain", data: "https://example.com/#test" }]],
"link",
win,
win,
window,
window,
{
clientX: testTab.getBoundingClientRect().right,
}
@ -99,23 +95,25 @@ add_task(async function () {
* In RTL build, it should appear in the following order:
* <test tab> <link dragged tab> <new tab> | <FxView tab>
*/
is(win.gBrowser.visibleTabs.length, 3, "There should be 3 tabs");
is(gBrowser.visibleTabs.length, 3, "There should be 3 tabs");
is(
win.gBrowser.visibleTabs.indexOf(newTab),
gBrowser.visibleTabs.indexOf(newTab),
0,
"New tab should still be rightmost visible tab"
);
is(
win.gBrowser.visibleTabs.indexOf(draggedTab),
gBrowser.visibleTabs.indexOf(draggedTab),
1,
"Dragged link should positioned at new index"
);
is(
win.gBrowser.visibleTabs.indexOf(testTab),
gBrowser.visibleTabs.indexOf(testTab),
2,
"Test tab should be to the left of dragged tab"
);
BrowserTestUtils.removeTab(draggedTab);
BrowserTestUtils.removeTab(testTab);
BrowserTestUtils.removeTab(FirefoxViewHandler.tab);
await BrowserTestUtils.closeWindow(win);
await SpecialPowers.popPrefEnv();
await BrowserTestUtils.disableRtlLocale();
});

View file

@ -20,5 +20,6 @@ prefs = [
["browser_messagepreview_show_invalidmessage.js"]
["browser_messagepreview_show_privatebrowsing.js"]
skip-if = ["true"] # Bug 1946629
["browser_messagepreview_show_spotlight.js"]

View file

@ -85,7 +85,6 @@ ChromeUtils.defineESModuleGetters(this, {
"resource://gre/modules/ExtensionPreferencesManager.sys.mjs",
ExtensionSettingsStore:
"resource://gre/modules/ExtensionSettingsStore.sys.mjs",
FeatureGate: "resource://featuregates/FeatureGate.sys.mjs",
FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
FirefoxRelay: "resource://gre/modules/FirefoxRelay.sys.mjs",
HomePage: "resource:///modules/HomePage.sys.mjs",

View file

@ -13,16 +13,19 @@ const lazy = {};
// This is used to keep the icon controllers alive for as long as their windows are alive.
const TASKBAR_ICON_CONTROLLERS = new WeakMap();
const PROFILES_PREF_NAME = "browser.profiles.enabled";
const GROUPID_PREF_NAME = "toolkit.telemetry.cachedProfileGroupID";
const DEFAULT_THEME_ID = "default-theme@mozilla.org";
ChromeUtils.defineESModuleGetters(lazy, {
AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
ClientID: "resource://gre/modules/ClientID.sys.mjs",
CryptoUtils: "resource://services-crypto/utils.sys.mjs",
EveryWindow: "resource:///modules/EveryWindow.sys.mjs",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
Sqlite: "resource://gre/modules/Sqlite.sys.mjs",
AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
setTimeout: "resource://gre/modules/Timer.sys.mjs",
Sqlite: "resource://gre/modules/Sqlite.sys.mjs",
TelemetryUtils: "resource://gre/modules/TelemetryUtils.sys.mjs",
});
ChromeUtils.defineLazyGetter(lazy, "profilesLocalization", () => {
@ -122,7 +125,7 @@ class SelectableProfileServiceClass extends EventEmitter {
"datareporting.policy.minimumPolicyVersion",
"datareporting.policy.minimumPolicyVersion.channel-beta",
"datareporting.usage.uploadEnabled",
"toolkit.telemetry.cachedProfileGroupID",
GROUPID_PREF_NAME,
];
// Preferences that were previously shared but should now be ignored.
@ -963,6 +966,18 @@ class SelectableProfileServiceClass extends EventEmitter {
continue;
}
// If the user has disabled then re-enabled data collection in another
// profile in the group, an extra step is needed to ensure each profile
// uses the same profile group ID.
if (
name === GROUPID_PREF_NAME &&
value !== lazy.TelemetryUtils.knownProfileGroupID &&
value !== Services.prefs.getCharPref(GROUPID_PREF_NAME)
) {
await lazy.ClientID.setProfileGroupID(value); // Sets the pref for us.
continue;
}
if (value === null) {
Services.prefs.clearUserPref(name);
} else {

View file

@ -20,6 +20,7 @@ const { SelectableProfileService } = ChromeUtils.importESModule(
export class ProfileSelector extends MozLitElement {
static properties = {
profiles: { type: Array },
showSelector: { type: Boolean },
};
static queries = {
@ -93,6 +94,8 @@ export class ProfileSelector extends MozLitElement {
await this.selectableProfileService.init();
await this.selectableProfileService.maybeSetupDataStore();
this.profiles = await this.selectableProfileService.getAllProfiles();
this.showSelector =
this.selectableProfileService.groupToolkitProfile.showProfileSelector;
if (!this.profiles.length) {
this.selectableProfileService.setShowProfileSelectorWindow(false);
@ -110,10 +113,11 @@ export class ProfileSelector extends MozLitElement {
}
handleCheckboxToggle() {
let state = this.checkbox.checked ? "enabled" : "disabled";
this.showSelector = this.checkbox.checked;
let state = this.showSelector ? "enabled" : "disabled";
Glean.profilesSelectorWindow.showAtStartup.record({ value: state });
this.selectableProfileService.setShowProfileSelectorWindow(
this.checkbox.checked
this.showSelector
);
}
@ -180,9 +184,15 @@ export class ProfileSelector extends MozLitElement {
<moz-checkbox
@click=${this.handleCheckboxToggle}
data-l10n-id="profile-window-checkbox-label-2"
?checked=${this.selectableProfileService.groupToolkitProfile
.showProfileSelector}
></moz-checkbox>`;
?checked=${this.showSelector}
>
${!this.showSelector
? html`<span
slot="description"
data-l10n-id="profile-window-checkbox-subcopy"
></span>`
: null}
</moz-checkbox>`;
}
}

View file

@ -3,6 +3,13 @@
"use strict";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
ClientID: "resource://gre/modules/ClientID.sys.mjs",
setTimeout: "resource://gre/modules/Timer.sys.mjs",
TelemetryUtils: "resource://gre/modules/TelemetryUtils.sys.mjs",
});
// Note: copied from preferences head.js. We can remove this when we migrate
// this test into that component.
async function openPreferencesViaOpenPreferencesAPI(aPane, aOptions) {
@ -178,3 +185,99 @@ add_task(async function testPrivacyInfoHiddenWhenDisabled() {
BrowserTestUtils.removeTab(gBrowser.selectedTab);
await SpecialPowers.popPrefEnv();
});
// If the user disables data collection, then re-enables data collection in
// another profile in the profile group, verify that the new profile group ID
// is correctly set to the value passed in from the database.
add_task(async function testReactivateProfileGroupID() {
if (!AppConstants.MOZ_TELEMETRY_REPORTING) {
ok(true, "Skipping test because telemetry reporting is disabled");
return;
}
await initGroupDatabase();
await SpecialPowers.pushPrefEnv({
set: [["datareporting.healthreport.uploadEnabled", true]],
});
await openPreferencesViaOpenPreferencesAPI("privacy", {
leaveOpen: true,
});
let checkbox = gBrowser.contentDocument.getElementById(
"submitHealthReportBox"
);
ok(
checkbox.checked,
"initially the data reporting checkbox should be checked"
);
let checkboxUpdated = BrowserTestUtils.waitForMutationCondition(
checkbox,
{ attributeFilter: ["checked"] },
() => !checkbox.checked
);
checkbox.click();
await checkboxUpdated;
Assert.ok(
!checkbox.checked,
"checkbox should not be checked after waiting for update"
);
Assert.equal(
Services.prefs.getBoolPref("datareporting.healthreport.uploadEnabled"),
false,
"upload should be disabled after unchecking checkbox"
);
// TODO: what could we explicitly await, rather than resorting to a timeout?
await new Promise(resolve => lazy.setTimeout(resolve, 1000));
Assert.equal(
Services.prefs.getStringPref("toolkit.telemetry.cachedProfileGroupID"),
lazy.TelemetryUtils.knownProfileGroupID,
"after disabling data collection, the profile group ID pref should have the canary value"
);
let groupID = await lazy.ClientID.getProfileGroupID();
Assert.equal(
groupID,
lazy.TelemetryUtils.knownProfileGroupID,
"after disabling data collection, the ClientID profile group ID should have the canary value"
);
// Simulate an update request from another instance that re-enables data
// reporting and sends over a new profile group ID.
let NEW_GROUP_ID = "12345678-b0ba-cafe-face-decafbad0123";
SelectableProfileService._getAllDBPrefs =
SelectableProfileService.getAllDBPrefs;
SelectableProfileService.getAllDBPrefs = () => [
{
name: "datareporting.healthreport.uploadEnabled",
value: true,
type: "boolean",
},
{
name: "toolkit.telemetry.cachedProfileGroupID",
value: NEW_GROUP_ID,
type: "string",
},
];
await SelectableProfileService.loadSharedPrefsFromDatabase();
groupID = await lazy.ClientID.getProfileGroupID();
Assert.equal(
groupID,
NEW_GROUP_ID,
"after re-enabling data collection, the ClientID profile group ID should have the remote value"
);
Assert.equal(
Services.prefs.getStringPref("toolkit.telemetry.cachedProfileGroupID"),
NEW_GROUP_ID,
"after re-enabling data collection, the profile group ID pref should have the remote value"
);
SelectableProfileService.getAllDBPrefs =
SelectableProfileService._getAllDBPrefs;
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

View file

@ -73,6 +73,11 @@ add_task(async function test_selector_window() {
Assert.ok(profileSelector.checkbox.checked, "Checkbox should be checked");
Assert.ok(
!profileSelector.checkbox.querySelector('[slot="description"]'),
"Description slot should not exist when checkbox is checked"
);
let asyncFlushCalled = false;
gProfileService.asyncFlush = () => (asyncFlushCalled = true);
@ -101,6 +106,11 @@ add_task(async function test_selector_window() {
"Profile selector should be disabled"
);
Assert.ok(
profileSelector.checkbox.querySelector('[slot="description"]'),
"Description slot should exist when checkbox is unchecked"
);
await assertGlean(
"profiles",
"selector_window",
@ -124,6 +134,11 @@ add_task(async function test_selector_window() {
"Profile selector should be disabled"
);
Assert.ok(
!profileSelector.checkbox.querySelector('[slot="description"]'),
"Description slot should not exist when checkbox is checked again"
);
profileSelector.selectableProfileService.execProcess = mock;
const profiles = profileSelector.profileCards;

View file

@ -17,8 +17,6 @@ ChromeUtils.defineESModuleGetters(this, {
RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
SEARCH_TELEMETRY_SHARED:
"moz-src:///browser/components/search/SearchSERPTelemetry.sys.mjs",
SearchSERPCategorization:
"resource:///modules/SearchSERPCategorization.sys.mjs",
SearchSERPDomainToCategoriesMap:
"moz-src:///browser/components/search/SearchSERPTelemetry.sys.mjs",
SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs",

View file

@ -4,6 +4,7 @@
"use strict";
ChromeUtils.defineESModuleGetters(this, {
ReviewCheckerParent: "resource:///actors/ReviewCheckerParent.sys.mjs",
ShoppingUtils: "resource:///modules/ShoppingUtils.sys.mjs",
});
@ -111,7 +112,10 @@ async function assertEmptyStateType(browser, messageName) {
!content.document.getElementById("multi-stage-message-root").hidden,
"message is shown"
);
ok(content.document.querySelector(className), "Rendered correct message");
ok(
content.document.querySelector(className),
`Rendered correct message ${className}`
);
});
}
@ -186,6 +190,9 @@ add_task(async function test_showOnboarding_notOptedIn() {
Services.fog.testResetFOG();
await Services.fog.testFlushAllChildren();
let sandbox = sinon.createSandbox();
sandbox.stub(ReviewCheckerParent, "isIgnoredURL").returns(false);
await BrowserTestUtils.withNewTab(
{
url: "about:shoppingsidebar",
@ -215,6 +222,7 @@ add_task(async function test_showOnboarding_notOptedIn() {
info("Failed to get Glean value due to unknown bug. See bug 1862389.");
}
}
sandbox.restore();
});
/**
@ -229,6 +237,9 @@ add_task(async function test_showOnboarding_notOptedIn_supported() {
Services.fog.testResetFOG();
await Services.fog.testFlushAllChildren();
let sandbox = sinon.createSandbox();
sandbox.stub(ReviewCheckerParent, "isIgnoredURL").returns(false);
await BrowserTestUtils.withNewTab(
{
url: "about:shoppingsidebar",
@ -259,6 +270,7 @@ add_task(async function test_showOnboarding_notOptedIn_supported() {
}
}
await SpecialPowers.popPrefEnv();
sandbox.restore();
});
/**
@ -275,6 +287,9 @@ add_task(
Services.fog.testResetFOG();
await Services.fog.testFlushAllChildren();
let sandbox = sinon.createSandbox();
sandbox.stub(ReviewCheckerParent, "isIgnoredURL").returns(false);
await BrowserTestUtils.withNewTab(
{
url: "about:shoppingsidebar",
@ -308,6 +323,7 @@ add_task(
}
}
await SpecialPowers.popPrefEnv();
sandbox.restore();
}
);

View file

@ -18,3 +18,6 @@ EXTRA_JS_MODULES += [
XPCSHELL_TESTS_MANIFESTS += ["tests/unit/xpcshell.toml"]
SPHINX_TREES["docs"] = "docs"
with Files("**"):
BUG_COMPONENT = ("Firefox", "Sidebar")

View file

@ -558,9 +558,7 @@ add_task(async function test_sidebar_tabs_layout() {
});
add_task(async function test_sidebar_position_rtl_ui() {
const sandbox = sinon.createSandbox();
sandbox.stub(window, "RTL_UI").value(true);
await SpecialPowers.pushPrefEnv({ set: [["intl.l10n.pseudo", "bidi"]] });
await BrowserTestUtils.enableRtlLocale();
Services.fog.testResetFOG();
// When RTL is enabled, sidebar is shown on the right by default.
@ -578,8 +576,7 @@ add_task(async function test_sidebar_position_rtl_ui() {
"right"
);
sandbox.restore();
await SpecialPowers.popPrefEnv();
await BrowserTestUtils.disableRtlLocale();
await SidebarController.waitUntilStable();
});

View file

@ -162,7 +162,7 @@
background:
linear-gradient(to left bottom, transparent 50%, rgba(0, 0, 0, 0.4) 0)
no-repeat 100% 0 / 1em 1em,
linear-gradient(-135deg, transparent 0.7em, var(--color-red-05) 0);
linear-gradient(-135deg, transparent 0.7em, var(--color-red-0) 0);
display: flex;
font-size: 14px !important;
height: 85px;
@ -176,21 +176,21 @@
background:
linear-gradient(to left bottom, transparent 50%, rgba(0, 0, 0, 0.4) 0)
no-repeat 100% 0 / 1em 1em,
linear-gradient(-135deg, transparent 0.7em, var(--color-green-05) 0);
linear-gradient(-135deg, transparent 0.7em, var(--color-green-0) 0);
}
&.blue {
background:
linear-gradient(to left bottom, transparent 50%, rgba(0, 0, 0, 0.4) 0)
no-repeat 100% 0 / 1em 1em,
linear-gradient(-135deg, transparent 0.7em, var(--color-blue-05) 0);
linear-gradient(-135deg, transparent 0.7em, var(--color-blue-0) 0);
}
&.orange {
background:
linear-gradient(to left bottom, transparent 50%, rgba(0, 0, 0, 0.4) 0)
no-repeat 100% 0 / 1em 1em,
linear-gradient(-135deg, transparent 0.7em, var(--color-yellow-05) 0);
linear-gradient(-135deg, transparent 0.7em, var(--color-yellow-0) 0);
}
&.big {

View file

@ -54,7 +54,7 @@ const EXPECTED_EMBEDDING_MODEL_OBJECTS = 4;
export const DIM_REDUCTION_METHODS = {};
const MISSING_ANCHOR_IN_CLUSTER_PENALTY = 0.2;
const NEAREST_NEIGHBOR_DEFAULT_THRESHOLD = 0.21;
const NEAREST_NEIGHBOR_DEFAULT_THRESHOLD = 0.275;
const MAX_NN_GROUPED_TABS = 4;
const MAX_SUGGESTED_TABS = 10;
@ -250,7 +250,7 @@ export class SmartTabGroupingManager {
groupLabel = "",
threshold = NEAREST_NEIGHBOR_DEFAULT_THRESHOLD,
precomputedEmbeddings = [],
depth = 1,
depth = 0,
}) {
// get embeddings for all the tabs
const tabData = await this._prepareTabData(allTabs);
@ -258,7 +258,7 @@ export class SmartTabGroupingManager {
if (precomputedEmbeddings.length === 0) {
embeddings = await this._generateEmbeddings(
tabData.map((td, index) => {
let text = td[EMBED_TEXT_KEY];
let text = SmartTabGroupingManager.preprocessText(td[EMBED_TEXT_KEY]);
// augment with group name if it's present
if (groupLabel && groupedIndices.includes(index)) {
text = `${groupLabel.slice(0, 100)}. ${td[EMBED_TEXT_KEY]}`;
@ -772,6 +772,42 @@ export class SmartTabGroupingManager {
return phrase; // return original phrase
}
/**
* Removes trailing domain-related text such as '... - Mail' or '... | News'
* If there's not enough information remaining after, we keep the text as is
* @param {string} text tab title with potential domain information
* @return {string}
*/
static preprocessText(text) {
// Matches 'xyz - Domain' or 'xyz | Domain'
// with a space before and after delimiter
// or if there are multiple delimiters next to each other
const delimiters = /(?<=\s)[|-]+(?=\s)/;
const splitText = text.split(delimiters);
// ensure there's enough info without the last element
const hasEnoughInfo =
!!splitText.length && splitText.slice(0, -1).join(" ").length > 5;
// domain related texts are usually shorter, this takes care of the most common cases
const isPotentialDomainInfo =
splitText.length > 1 && splitText[splitText.length - 1].length < 20;
// If both conditions are met, remove the last chunk, filter out empty strings,
// join on space, trim, and lowercase
if (hasEnoughInfo && isPotentialDomainInfo) {
return splitText
.slice(0, -1) // everything except the last element
.map(t => t.trim())
.filter(Boolean) // remove empty strings
.join(" ") // join with spaces
.trim(); // remove leading/trailing spaces
}
// Otherwise, just return the text
return text;
}
/**
* Postprocessing of raw output from Topic Model ML Engine
* @param {string | undefined} topic Raw topic phrase from topic model or undefined in case of an error

View file

@ -3945,13 +3945,22 @@
// 2) `itemAfter` is in a different tab group
this.moveTabToGroup(tab, tabGroup);
}
} else if (
this.isTab(itemAfter) &&
itemAfter?.group?.tabs[0] == itemAfter
) {
// If there is ambiguity around whether or not a tab should be inserted
// into a group (i.e. because the new tab is being inserted on the
// edges of the group), prefer not to insert the tab into the group.
//
// We only need to handle the case where the tab is being inserted at
// the starting boundary of a group because `insertBefore` called on
// the tab just after a tab group will not add it to the group by
// default.
this.tabContainer.insertBefore(tab, itemAfter.group);
} else {
// Place ungrouped tab before `itemAfter` or its group
// 1) Ungrouped tab between standalone tabs
// 2) Ungrouped tab at the end of the tab strip
// 3) Ungrouped tab right before the next tab group, if the
// next tab is in a group
this.tabContainer.insertBefore(tab, itemAfter?.group ?? itemAfter);
// Place ungrouped tab before `itemAfter` by default
this.tabContainer.insertBefore(tab, itemAfter);
}
this._updateTabsAfterInsert();

View file

@ -191,6 +191,13 @@
"always-show"
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"_sidebarPositionStart",
"sidebar.position_start",
true
);
if (gMultiProcessBrowser) {
this.tabbox.tabpanels.setAttribute("async", "true");
}
@ -1028,29 +1035,8 @@
}
#setMovingTabMode(movingTab) {
if (movingTab == this.#isMovingTab()) {
return;
}
this.toggleAttribute("movingtab", movingTab);
gNavToolbox.toggleAttribute("movingtab", movingTab);
if (movingTab) {
// This is a bit of an escape hatch in case a tab drag & drop session
// wasn't ended properly, leaving behind the movingtab attribute, which
// may break the UI (bug 1954163). We don't get mousemove events while
// dragging tabs, so at that point it should be safe to assume that we
// should not be in drag and drop mode, and clean things up if needed.
requestAnimationFrame(() => {
this.addEventListener(
"mousemove",
() => {
this.finishAnimateTabMove();
},
{ once: true }
);
});
}
}
#isMovingTab() {
@ -1395,7 +1381,10 @@
let crossAxisPos = this.verticalMode ? event.screenX : event.screenY;
let crossAxisStart, crossAxisEnd;
if (this.verticalMode) {
if (RTL_UI) {
if (
(RTL_UI && this._sidebarPositionStart) ||
(!RTL_UI && !this._sidebarPositionStart)
) {
crossAxisStart = window.screenX + rect.right - 1.5 * rect.width;
crossAxisEnd = window.screenX;
} else {

View file

@ -179,3 +179,87 @@ add_task(async function test_tabs_to_suggest_should_exclude_firefox_view() {
"about:firefoxview should be excluded"
);
});
add_task(function test_text_processing_basic_cases() {
// trailing domain-like text should be removed
Assert.equal(
SmartTabGroupingManager.preprocessText("Some Title - Random Mail"),
"Some Title",
"Should remove '- Random Mail' suffix and lowercase result"
);
// trailing domain-like text with '|'
Assert.equal(
SmartTabGroupingManager.preprocessText(
"Another Title | Some Video Website"
),
"Another Title",
"Should remove '| Some Video Website' suffix and lowercase result"
);
// no delimiter
Assert.equal(
SmartTabGroupingManager.preprocessText("Simple Title"),
"Simple Title",
"Should only be lowercased since there's no recognized delimiter"
);
// not enough info in first part
Assert.equal(
SmartTabGroupingManager.preprocessText("AB - Mail"),
"AB - Mail",
"Should not remove '- Mail' because the first part is too short"
);
// should not match for texts such as 'check-in'
Assert.equal(
SmartTabGroupingManager.preprocessText("Check-in for flight"),
"Check-in for flight",
"Should not remove '-in'"
);
});
add_task(function test_text_processing_edge_cases() {
// empty string
Assert.equal(
SmartTabGroupingManager.preprocessText(""),
"",
"Empty string returns empty string"
);
// exactly 20 chars
const domain20Chars = "12345678901234567890"; // 20 characters
Assert.equal(
SmartTabGroupingManager.preprocessText(`My Title - ${domain20Chars}`),
`My Title - ${domain20Chars}`,
"Should not remove suffix because its exactly 20 chars long, not < 20"
);
// multiple delimiters, remove last only
Assert.equal(
SmartTabGroupingManager.preprocessText("Complex - Title - SomethingSmall"),
"Complex Title",
"Should remove only the last '- SomethingSmall', ignoring earlier delimiters"
);
// repeated delimiters
Assert.equal(
SmartTabGroupingManager.preprocessText("Title --- Domain"),
"Title",
"Should remove the last chunk and filter out empty strings"
);
Assert.equal(
SmartTabGroupingManager.preprocessText("Title || Domain"),
"Title",
"Should remove the last chunk with double pipe delimiters too"
);
// long trailing text
const longDomain = "Useful information is present";
Assert.equal(
SmartTabGroupingManager.preprocessText(`Some Title - ${longDomain}`),
`Some Title - ${longDomain}`,
"Should not remove suffix if it's >= 20 characters"
);
});

View file

@ -2169,3 +2169,63 @@ add_task(async function test_bug1936015() {
await SpecialPowers.popPrefEnv();
});
add_task(async function test_bug1957723_addTabsByIndex() {
let initialTab = gBrowser.tabs[0];
let triggeringPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
const tabs = createManyTabs(5);
const tabGroup = gBrowser.addTabGroup([tabs[1], tabs[2], tabs[3]], {
insertBefore: tabs[1],
});
let tab1 = gBrowser.addTab("https://example.com", {
index: 2,
triggeringPrincipal,
});
Assert.equal(
tab1._tPos,
2,
"Tab added at starting index of tab group is in correct position"
);
Assert.equal(
tab1.group,
null,
"Tab added at starting index of tab group is not in group"
);
gBrowser.removeTab(tab1);
let tab2 = gBrowser.addTab("https://example.com", {
index: 4,
triggeringPrincipal,
});
Assert.equal(
tab2._tPos,
4,
"Tab added by index just before end of tab group is in correct position"
);
Assert.equal(
tab2.group.id,
tabGroup.id,
"Tab added by index just before end of tab group is in group"
);
gBrowser.removeTab(tab2);
let tab3 = gBrowser.addTab("https://example.com", {
index: 5,
triggeringPrincipal,
});
Assert.equal(
tab3._tPos,
5,
"Tab added at index just after end of tab group is in correct position"
);
Assert.equal(
tab3.group,
null,
"Tab added at index just after end of tab group is not in group"
);
gBrowser.removeTab(tab3);
gBrowser.removeAllTabsBut(initialTab);
});

View file

@ -34,6 +34,8 @@ support-files = [
["browser_translations_full_page_move_tab_to_new_window.js"]
["browser_translations_full_page_moz_extension.js"]
["browser_translations_full_page_multiple_windows.js"]
["browser_translations_full_page_panel_a11y_focus.js"]

View file

@ -0,0 +1,61 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests a basic panel open, translation, and restoration to the original language.
*/
add_task(async function test_translations_moz_extension() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
web_accessible_resources: ["test_page.html"],
},
files: {
"test_page.html": `<!DOCTYPE html>
<html lang="es">
<body>
<div>
<h1>Don Quijote de La Mancha</h1>
</div>
</body>
</html>`,
},
});
await extension.startup();
const { cleanup, resolveDownloads, runInPage } = await loadTestPage({
page: `moz-extension://${extension.uuid}/test_page.html`,
languagePairs: LANGUAGE_PAIRS,
});
const { button } =
await FullPageTranslationsTestUtils.assertTranslationsButton(
{ button: true, circleArrows: false, locale: false, icon: true },
"The button is available."
);
is(button.getAttribute("data-l10n-id"), "urlbar-translations-button2");
await FullPageTranslationsTestUtils.assertPageIsUntranslated(runInPage);
await FullPageTranslationsTestUtils.openPanel({
expectedFromLanguage: "es",
expectedToLanguage: "en",
onOpenPanel: FullPageTranslationsTestUtils.assertPanelViewDefault,
});
await FullPageTranslationsTestUtils.clickTranslateButton({
downloadHandler: resolveDownloads,
});
await FullPageTranslationsTestUtils.assertPageIsTranslated({
fromLanguage: "es",
toLanguage: "en",
runInPage,
});
await cleanup();
await extension.unload();
});

View file

@ -48,9 +48,6 @@ let globalActionsProviders = [
* A provider that lets the user view all available global actions for a query.
*/
class ProviderGlobalActions extends UrlbarProvider {
// A Map of the last queried actions.
#actions = new Map();
get name() {
return "UrlbarProviderGlobalActions";
}
@ -68,8 +65,7 @@ class ProviderGlobalActions extends UrlbarProvider {
}
async startQuery(queryContext, addCallback) {
this.#actions.clear();
let actionsResults = [];
let searchModeEngine = "";
for (let provider of globalActionsProviders) {
@ -81,12 +77,12 @@ class ProviderGlobalActions extends UrlbarProvider {
// We only allow one action that provides an engine search mode.
continue;
}
this.#actions.set(action.key, action);
actionsResults.push(action);
}
}
}
if (!this.#actions.size) {
if (!actionsResults.length) {
return;
}
@ -94,14 +90,12 @@ class ProviderGlobalActions extends UrlbarProvider {
lazy.UrlbarPrefs.get(TIMES_TO_SHOW_PREF) >
lazy.UrlbarPrefs.get(TIMES_SHOWN_PREF);
let results = [...this.#actions.keys()];
let query = results.includes("matched-contextual-search")
let query = actionsResults.some(a => a.key == "matched-contextual-search")
? ""
: queryContext.searchString;
let payload = {
results,
actionsResults,
dynamicType: DYNAMIC_TYPE_NAME,
inputLength: queryContext.searchString.length,
input: query,
@ -125,12 +119,14 @@ class ProviderGlobalActions extends UrlbarProvider {
onSelection(result, element) {
let key = element.dataset.action;
this.#actions.get(key).onSelection?.(result, element);
let action = result.payload.actionsResults.find(a => a.key == key);
action.onSelection?.(result, element);
}
onEngagement(queryContext, controller, details) {
let key = details.element.dataset.action;
let options = this.#actions.get(key).onPick(queryContext, controller);
let action = details.result.payload.actionsResults.find(a => a.key == key);
let options = action.onPick(queryContext, controller);
if (options?.focusContent) {
details.element.ownerGlobal.gBrowser.selectedBrowser.focus();
}
@ -153,15 +149,14 @@ class ProviderGlobalActions extends UrlbarProvider {
}
getViewTemplate(result) {
let children = result.payload.results.map((key, i) => {
let action = this.#actions.get(key);
let children = result.payload.actionsResults.map((action, i) => {
let btn = {
name: `button-${i}`,
tag: "span",
classList: ["urlbarView-action-btn"],
attributes: {
inputLength: result.payload.inputLength,
"data-action": key,
"data-action": action.key,
role: "button",
},
children: [
@ -211,8 +206,7 @@ class ProviderGlobalActions extends UrlbarProvider {
l10n: { id: "press-tab-label", cacheable: true },
};
}
result.payload.results.forEach((key, i) => {
let action = this.#actions.get(key);
result.payload.actionsResults.forEach((action, i) => {
viewUpdate[`label-${i}`] = {
l10n: { id: action.l10nId, args: action.l10nArgs, cacheable: true },
};

View file

@ -1599,7 +1599,7 @@ export var UrlbarUtils = {
if (result.providerName != "UrlbarProviderGlobalActions") {
return result.payload.action?.key ?? "none";
}
return result.payload.results.map(({ key }) => key).join(",");
return result.payload.actionsResults.map(({ key }) => key).join(",");
},
_getQuickSuggestTelemetryType(result) {

View file

@ -431,7 +431,7 @@ export class UrlbarView {
return;
}
let l10n = { id: "firefox-suggest-feedback-acknowledgment" };
let l10n = { id: "urlbar-feedback-acknowledgment" };
await this.#l10nCache.ensure(l10n);
if (row.result != result) {
return;

View file

@ -53,9 +53,6 @@ urlbar-group-local =
urlbar-group-sponsored =
.label = Sponsored
# A message shown in a result when the user gives feedback on it.
firefox-suggest-feedback-acknowledgment = Thanks for your feedback
# A message that replaces a result when the user dismisses a single suggestion.
firefox-suggest-dismissal-acknowledgment-one = Thanks for your feedback. You wont see this suggestion again.
@ -133,8 +130,6 @@ firefox-suggest-weather-sponsored = { $provider } · Sponsored
## These strings are used as labels of menu items in the result menu.
firefox-suggest-command-show-less-frequently =
.label = Show less frequently
firefox-suggest-command-dont-show-this =
.label = Dont show this
firefox-suggest-command-dont-show-mdn =
@ -143,8 +138,6 @@ firefox-suggest-command-not-relevant =
.label = Not relevant
firefox-suggest-command-not-interested =
.label = Not interested
firefox-suggest-weather-command-inaccurate-location =
.label = Report inaccurate location
firefox-suggest-command-manage-fakespot =
.label = Manage { -fakespot-brand-name } suggestions
firefox-suggest-command-dont-show-this-suggestion =

View file

@ -559,6 +559,7 @@ urlbar:
type: boolean
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852058
- https://bugzilla.mozilla.org/show_bug.cgi?id=1958147
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852058#c2
- https://bugzilla.mozilla.org/show_bug.cgi?id=1866204#c8
@ -569,7 +570,7 @@ urlbar:
- interaction
notification_emails:
- fx-search-telemetry@mozilla.com
expires: 140
expires: 147
pref_max_results:
lifetime: application

View file

@ -128,7 +128,7 @@ export class AddonSuggestions extends SuggestProvider {
commands.push({
name: RESULT_MENU_COMMAND.SHOW_LESS_FREQUENTLY,
l10n: {
id: "firefox-suggest-command-show-less-frequently",
id: "urlbar-result-menu-show-less-frequently",
},
});
}

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