Merge mozilla-central to autoland. a=merge CLOSED TREE

This commit is contained in:
shindli 2018-11-10 23:40:36 +02:00
commit fbde86e1a5
1017 changed files with 24559 additions and 10486 deletions

100
Cargo.lock generated
View File

@ -161,11 +161,11 @@ name = "baldrdash"
version = "0.1.0"
dependencies = [
"bindgen 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-codegen 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-wasm 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-codegen 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-wasm 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"target-lexicon 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -457,57 +457,61 @@ dependencies = [
[[package]]
name = "cranelift-bforest"
version = "0.20.0"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cranelift-entity 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-entity 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cranelift-codegen"
version = "0.20.0"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cranelift-bforest 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-codegen-meta 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-entity 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-bforest 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-codegen-meta 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-entity 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"target-lexicon 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cranelift-codegen-meta"
version = "0.20.0"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cranelift-entity 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cranelift-entity"
version = "0.20.1"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cranelift-frontend"
version = "0.20.0"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cranelift-codegen 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-codegen 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cranelift-wasm"
version = "0.20.1"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cranelift-codegen 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-entity 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-frontend 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-codegen 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-entity 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-frontend 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"target-lexicon 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmparser 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmparser 0.21.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -844,22 +848,22 @@ dependencies = [
[[package]]
name = "failure"
version = "0.1.2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure_derive"
version = "0.1.2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)",
"synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.7 (registry+https://github.com/rust-lang/crates.io-index)",
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -2019,7 +2023,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2068,7 +2072,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayref 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lmdb-rkv 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ordered-float 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2490,22 +2494,22 @@ dependencies = [
[[package]]
name = "synstructure"
version = "0.9.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.7 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "target-lexicon"
version = "0.0.3"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2895,7 +2899,7 @@ dependencies = [
[[package]]
name = "wasmparser"
version = "0.17.2"
version = "0.21.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -3162,12 +3166,12 @@ dependencies = [
"checksum core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f3f46450d6f2397261af420b4ccce23807add2e45fa206410a03d66fb7f050ae"
"checksum cose 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "72fa26cb151d3ae4b70f63d67d0fed57ce04220feafafbae7f503bef7aae590d"
"checksum cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "49726015ab0ca765144fcca61e4a7a543a16b795a777fa53f554da2fffff9a94"
"checksum cranelift-bforest 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e96851b525021dd220259b9f29bf79d83f65b49e4f12b786d545aa929e4cad2"
"checksum cranelift-codegen 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "16f418f1d1e6221812a7d35cff5b9a572dc978c002e33792134bbd50c07cacca"
"checksum cranelift-codegen-meta 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1da3daa0109e7a0b7b322cea666cc223fb6a0d5170e83d23b3d5d2deaddca5f3"
"checksum cranelift-entity 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27412f153f2b517125dea9247ee8859a9ea3923d44384d54420e64fab9314752"
"checksum cranelift-frontend 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "03c44cc7006b375e60e0c7edb6fc81abfbf20158374c03f5d0da981b373860a3"
"checksum cranelift-wasm 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a9d3454bf60ee6c3d1f54d6cf9ed82cfc1a2e7efb9ec1b16666bf2987c88bfa"
"checksum cranelift-bforest 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8c5f8e1ab4f73b59a98531a8013d8ed3ca7edb4e36984cb301d9c06f6892787b"
"checksum cranelift-codegen 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437ec8212686e6cdacfea75aaedb4ab8b013869be1e8693a4cb97a60f135035"
"checksum cranelift-codegen-meta 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4eac16097b96e9f609df735555f2d1658531750fbc3805bca1daca7671aef9eb"
"checksum cranelift-entity 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9be3f82369346201c2e0cff720522e6eb55459e51c916b2199f25cff2058ca96"
"checksum cranelift-frontend 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d5d18ab2bc89a09b4275442a9559dc0f947b9a8ad9ae9ee89452a057df54ced"
"checksum cranelift-wasm 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5906a111814d43d84002ef974eb0c023804fd4d1866b34f43c1bb588a759ad8"
"checksum crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7"
"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
"checksum crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fe8153ef04a7594ded05b427ffad46ddeaf22e63fd48d42b3e1e3bb4db07cae7"
@ -3201,8 +3205,8 @@ dependencies = [
"checksum env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0561146661ae44c579e993456bc76d11ce1e0c7d745e57b2fa7146b6e49fa2ad"
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
"checksum euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "70a2ebdf55fb9d6329046e026329a55ef8fbaae5ea833f56e170beb3125a8a5f"
"checksum failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7efb22686e4a466b1ec1a15c2898f91fa9cb340452496dca654032de20ff95b9"
"checksum failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "946d0e98a50d9831f5d589038d2ca7f8f455b1c21028c0db0e84116a12696426"
"checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7"
"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
"checksum fixedbitset 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "85cb8fec437468d86dc7c83ca7cfc933341d561873275f22dd5eedefa63a6478"
"checksum flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fac2277e84e5e858483756647a9d0aa8d9a2b7cba517fd84325a0aaa69a0909"
@ -3347,9 +3351,9 @@ dependencies = [
"checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59"
"checksum syn 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4b5274d4a0a3d2749d5c158dc64d3403e60554dc61194648787ada5212473d"
"checksum syn 0.15.7 (registry+https://github.com/rust-lang/crates.io-index)" = "455a6ec9b368f8c479b0ae5494d13b22dc00990d2f00d68c9dc6a2dc4f17f210"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
"checksum synstructure 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "98cad891cd238c98e1f0aec9f7c0f620aa696e4e5f7daba56ac67b5e86a6b049"
"checksum synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb9b7550d063ea184027c9b8c20ac167cd36d3e06b3a40bceb9d746dc1a7b7"
"checksum target-lexicon 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a34226bd63b5a26fc909f5f0d7ef4dc55d5851077035e49437e4e14bf567247f"
"checksum target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4af5e2227f0b887d591d3724b796a96eff04226104d872f5b3883fcd427d64b9"
"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
"checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1"
"checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209"
@ -3392,7 +3396,7 @@ dependencies = [
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "63636bd0eb3d00ccb8b9036381b526efac53caf112b7783b730ab3f8e44da369"
"checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3"
"checksum wasmparser 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fed18a63a6796175be2254fccca1da4e8b8fec0abca37ad155aea345feb50798"
"checksum wasmparser 0.21.8 (registry+https://github.com/rust-lang/crates.io-index)" = "202e4cd4d99aa8adb8fe6280e099fdd2e5003c8d09c27de6969ff04dba60ef39"
"checksum webidl 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0f807f7488d680893f7188aa09d7672a3a0a8461975a098a2edf0a52e3fee29"
"checksum which 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4be6cfa54dab45266e98b5d7be2f8ce959ddd49abd141a05d52dce4b07f803bb"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"

View File

@ -81,7 +81,7 @@
"listitem-2-3"]);
queueTraversalSequence(gQueue, docAcc, TraversalRules.Graphic, null,
["image-2", "image-3"]);
["image-1", "image-2", "image-3"]);
queueTraversalSequence(gQueue, docAcc, TraversalRules.Link, null,
["link-0", "link-1", "link-2", "link-3"]);
@ -116,7 +116,7 @@
"1. Scheme", "2. Racket", "3. Clojure",
"4. Standard Lisp", "link-0", " Lisp",
"checkbox-1-5", " LeLisp", "• JavaScript",
"heading-5", "image-2", "image-3",
"heading-5", "image-1", "image-2", "image-3",
"Not actually an image", "link-1", "anchor-1",
"link-2", "anchor-2", "link-3", "3", "1", "4",
"1", "Sunday", "M", "Week 1", "3", "4", "7", "2",

View File

@ -19,8 +19,8 @@
document.getElementsByTagName("img")[0].firstChild.data = "2";
var accTree = {
role: ROLE_TEXT,
children: [ { role: ROLE_TEXT_LEAF } ],
role: ROLE_GRAPHIC,
children: [],
};
testAccessibleTree("the_img", accTree);
SimpleTest.finish();

View File

@ -23,8 +23,8 @@ var CaptivePortalWatcher = {
_waitingForRecheck: false,
get _captivePortalNotification() {
let nb = document.getElementById("high-priority-global-notificationbox");
return nb.getNotificationWithValue(this.PORTAL_NOTIFICATION_VALUE);
return gHighPriorityNotificationBox.getNotificationWithValue(
this.PORTAL_NOTIFICATION_VALUE);
},
get canonicalURL() {
@ -228,9 +228,9 @@ var CaptivePortalWatcher = {
gBrowser.tabContainer.removeEventListener("TabSelect", this);
};
let nb = document.getElementById("high-priority-global-notificationbox");
nb.appendNotification(message, this.PORTAL_NOTIFICATION_VALUE, "",
nb.PRIORITY_INFO_MEDIUM, buttons, closeHandler);
gHighPriorityNotificationBox.appendNotification(
message, this.PORTAL_NOTIFICATION_VALUE, "",
gHighPriorityNotificationBox.PRIORITY_INFO_MEDIUM, buttons, closeHandler);
gBrowser.tabContainer.addEventListener("TabSelect", this);
},

View File

@ -22,7 +22,7 @@ var CompactTheme = {
},
get isThemeCurrentlyApplied() {
let theme = LightweightThemeManager.currentThemeForDisplay;
let theme = LightweightThemeManager.currentThemeWithPersistedData;
return theme && (
theme.id == "firefox-compact-dark@mozilla.org" ||
theme.id == "firefox-compact-light@mozilla.org");

View File

@ -15,11 +15,6 @@ var gDataNotificationInfoBar = {
_DATA_REPORTING_NOTIFICATION: "data-reporting",
get _notificationBox() {
delete this._notificationBox;
return this._notificationBox = document.getElementById("global-notificationbox");
},
get _log() {
let Log = ChromeUtils.import("resource://gre/modules/Log.jsm", {}).Log;
delete this._log;
@ -39,7 +34,7 @@ var gDataNotificationInfoBar = {
},
_getDataReportingNotification(name = this._DATA_REPORTING_NOTIFICATION) {
return this._notificationBox.getNotificationWithValue(name);
return gNotificationBox.getNotificationWithValue(name);
},
_displayDataPolicyInfoBar(request) {
@ -68,11 +63,11 @@ var gDataNotificationInfoBar = {
}];
this._log.info("Creating data reporting policy notification.");
this._notificationBox.appendNotification(
gNotificationBox.appendNotification(
message,
this._DATA_REPORTING_NOTIFICATION,
null,
this._notificationBox.PRIORITY_INFO_HIGH,
gNotificationBox.PRIORITY_INFO_HIGH,
buttons,
event => {
if (event == "removed") {

View File

@ -726,9 +726,7 @@ var gHistorySwipeAnimation = {
* Adds the boxes that contain the arrows used during the swipe animation.
*/
_addBoxes: function HSA__addBoxes() {
let browserStack =
document.getAnonymousElementByAttribute(gBrowser.getPanel(),
"class", "browserStack");
let browserStack = gBrowser.getPanel().querySelector(".browserStack");
this._container = this._createElement("historySwipeAnimationContainer",
"stack");
browserStack.appendChild(this._container);

View File

@ -372,8 +372,7 @@ toolbarpaletteitem {
#main-window[inFullscreen][inDOMFullscreen] #sidebar-box,
#main-window[inFullscreen][inDOMFullscreen] #sidebar-splitter,
#main-window[inFullscreen]:not([OSXLionFullscreen]) toolbar:not([fullscreentoolbar=true]),
#main-window[inFullscreen] #global-notificationbox,
#main-window[inFullscreen] #high-priority-global-notificationbox {
#main-window[inFullscreen] .global-notificationbox {
visibility: collapse;
}

View File

@ -294,6 +294,40 @@ Object.defineProperty(this, "gNavToolbox", {
},
});
// High priority notification bars shown at the top of the window.
Object.defineProperty(this, "gHighPriorityNotificationBox", {
configurable: true,
enumerable: true,
get() {
delete this.gHighPriorityNotificationBox;
let notificationbox = new MozElements.NotificationBox(element => {
element.classList.add("global-notificationbox");
element.setAttribute("notificationside", "top");
document.getElementById("appcontent").append(element);
});
return this.gHighPriorityNotificationBox = notificationbox;
},
});
// Regular notification bars shown at the bottom of the window.
Object.defineProperty(this, "gNotificationBox", {
configurable: true,
enumerable: true,
get() {
delete this.gNotificationBox;
let notificationbox = new MozElements.NotificationBox(element => {
element.classList.add("global-notificationbox");
element.setAttribute("notificationside", "bottom");
document.getElementById("browser-bottombox").appendChild(element);
});
return this.gNotificationBox = notificationbox;
},
});
// Smart getter for the findbar. If you don't wish to force the creation of
// the findbar, check gFindBarInitialized first.
@ -527,8 +561,7 @@ const gStoragePressureObserver = {
}
const NOTIFICATION_VALUE = "storage-pressure-notification";
let notificationBox = document.getElementById("high-priority-global-notificationbox");
if (notificationBox.getNotificationWithValue(NOTIFICATION_VALUE)) {
if (gHighPriorityNotificationBox.getNotificationWithValue(NOTIFICATION_VALUE)) {
// Do not display the 2nd notification when there is already one
return;
}
@ -597,8 +630,9 @@ const gStoragePressureObserver = {
});
}
notificationBox.appendNotification(
msg, NOTIFICATION_VALUE, null, notificationBox.PRIORITY_WARNING_HIGH, buttons, null);
gHighPriorityNotificationBox.appendNotification(
msg, NOTIFICATION_VALUE, null,
gHighPriorityNotificationBox.PRIORITY_WARNING_HIGH, buttons, null);
},
};
@ -3498,40 +3532,24 @@ var PrintPreviewListener = {
this._sidebarCommand = SidebarUI.currentID;
SidebarUI.hide();
var notificationBox = gBrowser.getNotificationBox();
this._chromeState.notificationsOpen = !notificationBox.notificationsHidden;
notificationBox.notificationsHidden = true;
this._chromeState.findOpen = gFindBarInitialized && !gFindBar.hidden;
if (gFindBarInitialized)
gFindBar.close();
var globalNotificationBox = document.getElementById("global-notificationbox");
this._chromeState.globalNotificationsOpen = !globalNotificationBox.notificationsHidden;
globalNotificationBox.notificationsHidden = true;
this._chromeState.syncNotificationsOpen = false;
var syncNotifications = document.getElementById("sync-notifications");
if (syncNotifications) {
this._chromeState.syncNotificationsOpen = !syncNotifications.notificationsHidden;
syncNotifications.notificationsHidden = true;
}
gBrowser.getNotificationBox().stack.hidden = true;
gNotificationBox.stack.hidden = true;
},
_showChrome() {
if (this._chromeState.notificationsOpen)
gBrowser.getNotificationBox().notificationsHidden = false;
gNotificationBox.stack.hidden = false;
gBrowser.getNotificationBox().stack.hidden = false;
if (this._chromeState.findOpen)
if (this._chromeState.findOpen) {
gLazyFindCommand("open");
}
if (this._chromeState.globalNotificationsOpen)
document.getElementById("global-notificationbox").notificationsHidden = false;
if (this._chromeState.syncNotificationsOpen)
document.getElementById("sync-notifications").notificationsHidden = false;
if (this._chromeState.sidebarOpen)
if (this._chromeState.sidebarOpen) {
SidebarUI.show(this._sidebarCommand);
}
},
activateBrowser(browser) {

View File

@ -1325,7 +1325,7 @@ xmlns="http://www.w3.org/1999/xhtml"
<splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" hidden="true"/>
<vbox id="appcontent" flex="1">
<notificationbox id="high-priority-global-notificationbox" notificationside="top"/>
<!-- gHighPriorityNotificationBox will be added here lazily. -->
<tabbox id="tabbrowser-tabbox"
flex="1" tabcontainer="tabbrowser-tabs">
<tabpanels id="tabbrowser-tabpanels"
@ -1368,7 +1368,7 @@ xmlns="http://www.w3.org/1999/xhtml"
</html:div>
<vbox id="browser-bottombox" layer="true">
<notificationbox id="global-notificationbox" notificationside="bottom"/>
<!-- gNotificationBox will be added here lazily. -->
</vbox>
</window>

View File

@ -654,7 +654,14 @@ window._gBrowser = {
},
getNotificationBox(aBrowser) {
return this.getBrowserContainer(aBrowser).firstElementChild;
let browser = aBrowser || this.selectedBrowser;
if (!browser._notificationBox) {
browser._notificationBox = new MozElements.NotificationBox(element => {
element.setAttribute("notificationside", "top");
this.getBrowserContainer(browser).prepend(element);
});
}
return browser._notificationBox;
},
getTabModalPromptBox(aBrowser) {

View File

@ -98,9 +98,8 @@ function ensurePortalTab(win) {
}
function ensurePortalNotification(win) {
let notificationBox =
win.document.getElementById("high-priority-global-notificationbox");
let notification = notificationBox.getNotificationWithValue(PORTAL_NOTIFICATION_VALUE);
let notification = win.gHighPriorityNotificationBox.getNotificationWithValue(
PORTAL_NOTIFICATION_VALUE);
isnot(notification, null,
"There should be a captive portal notification in the window.");
return notification;
@ -122,9 +121,8 @@ function ensureNoPortalTab(win) {
}
function ensureNoPortalNotification(win) {
let notificationBox =
win.document.getElementById("high-priority-global-notificationbox");
is(notificationBox.getNotificationWithValue(PORTAL_NOTIFICATION_VALUE), null,
is(win.gHighPriorityNotificationBox
.getNotificationWithValue(PORTAL_NOTIFICATION_VALUE), null,
"There should be no captive portal notification in the window.");
}

View File

@ -45,7 +45,7 @@ function promiseNextTick() {
*/
function promiseWaitForAlertActive(aNotificationBox) {
let deferred = PromiseUtils.defer();
aNotificationBox.addEventListener("AlertActive", function() {
aNotificationBox.stack.addEventListener("AlertActive", function() {
deferred.resolve();
}, {once: true});
return deferred.promise;
@ -120,8 +120,6 @@ add_task(async function test_single_window() {
// Close all the notifications, then try to trigger the data choices infobar.
await closeAllNotifications();
let notificationBox = document.getElementById("global-notificationbox");
// Make sure that we have a coherent initial state.
Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_VERSION, 0), 0,
"No version should be set on init.");
@ -130,7 +128,7 @@ add_task(async function test_single_window() {
Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(),
"User not notified about datareporting policy.");
let alertShownPromise = promiseWaitForAlertActive(notificationBox);
let alertShownPromise = promiseWaitForAlertActive(gNotificationBox);
Assert.ok(!TelemetryReportingPolicy.canUpload(),
"User should not be allowed to upload.");
@ -138,15 +136,15 @@ add_task(async function test_single_window() {
triggerInfoBar(10 * 1000);
await alertShownPromise;
Assert.equal(notificationBox.allNotifications.length, 1, "Notification Displayed.");
Assert.equal(gNotificationBox.allNotifications.length, 1, "Notification Displayed.");
Assert.ok(TelemetryReportingPolicy.canUpload(), "User should be allowed to upload now.");
await promiseNextTick();
let promiseClosed = promiseWaitForNotificationClose(notificationBox.currentNotification);
await checkInfobarButton(notificationBox.currentNotification);
let promiseClosed = promiseWaitForNotificationClose(gNotificationBox.currentNotification);
await checkInfobarButton(gNotificationBox.currentNotification);
await promiseClosed;
Assert.equal(notificationBox.allNotifications.length, 0, "No notifications remain.");
Assert.equal(gNotificationBox.allNotifications.length, 0, "No notifications remain.");
// Check that we are still clear to upload and that the policy data is saved.
Assert.ok(TelemetryReportingPolicy.canUpload());
@ -168,13 +166,7 @@ add_task(async function test_multiple_windows() {
// results in dismiss on every window.
let otherWindow = await BrowserTestUtils.openNewBrowserWindow();
// Get the notification box for both windows.
let notificationBoxes = [
document.getElementById("global-notificationbox"),
otherWindow.document.getElementById("global-notificationbox"),
];
Assert.ok(notificationBoxes[1], "2nd window has a global notification box.");
Assert.ok(otherWindow.gNotificationBox, "2nd window has a global notification box.");
// Make sure that we have a coherent initial state.
Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_VERSION, 0), 0, "No version should be set on init.");
@ -182,8 +174,8 @@ add_task(async function test_multiple_windows() {
Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(), "User not notified about datareporting policy.");
let showAlertPromises = [
promiseWaitForAlertActive(notificationBoxes[0]),
promiseWaitForAlertActive(notificationBoxes[1]),
promiseWaitForAlertActive(gNotificationBox),
promiseWaitForAlertActive(otherWindow.gNotificationBox),
];
Assert.ok(!TelemetryReportingPolicy.canUpload(),
@ -195,10 +187,10 @@ add_task(async function test_multiple_windows() {
// Both notification were displayed. Close one and check that both gets closed.
let closeAlertPromises = [
promiseWaitForNotificationClose(notificationBoxes[0].currentNotification),
promiseWaitForNotificationClose(notificationBoxes[1].currentNotification),
promiseWaitForNotificationClose(gNotificationBox.currentNotification),
promiseWaitForNotificationClose(otherWindow.gNotificationBox.currentNotification),
];
notificationBoxes[0].currentNotification.close();
gNotificationBox.currentNotification.close();
await Promise.all(closeAlertPromises);
// Close the second window we opened.

View File

@ -28,18 +28,17 @@ add_task(async function() {
// await SpecialPowers.pushPrefEnv({set: [["privacy.reduceTimerPrecision", false]]});
await notifyStoragePressure();
let notificationbox = document.getElementById("high-priority-global-notificationbox");
let notification = notificationbox.getNotificationWithValue("storage-pressure-notification");
let notification = gHighPriorityNotificationBox.getNotificationWithValue("storage-pressure-notification");
ok(notification instanceof XULElement, "Should display storage pressure notification");
notification.close();
await notifyStoragePressure();
notification = notificationbox.getNotificationWithValue("storage-pressure-notification");
notification = gHighPriorityNotificationBox.getNotificationWithValue("storage-pressure-notification");
is(notification, null, "Should not display storage pressure notification more than once within the given interval");
await new Promise(resolve => setTimeout(resolve, TEST_NOTIFICATION_INTERVAL_MS + 1));
await notifyStoragePressure();
notification = notificationbox.getNotificationWithValue("storage-pressure-notification");
notification = gHighPriorityNotificationBox.getNotificationWithValue("storage-pressure-notification");
ok(notification instanceof XULElement, "Should display storage pressure notification after the given interval");
notification.close();
});
@ -53,8 +52,7 @@ add_task(async function() {
const USAGE_THRESHOLD_BYTES = BYTES_IN_GIGABYTE *
Services.prefs.getIntPref("browser.storageManager.pressureNotification.usageThresholdGB");
await notifyStoragePressure(USAGE_THRESHOLD_BYTES);
let notificationbox = document.getElementById("high-priority-global-notificationbox");
let notification = notificationbox.getNotificationWithValue("storage-pressure-notification");
let notification = gHighPriorityNotificationBox.getNotificationWithValue("storage-pressure-notification");
ok(notification instanceof XULElement, "Should display storage pressure notification");
let prefBtn = notification.getElementsByTagName("button")[1];
@ -76,8 +74,7 @@ add_task(async function() {
await notifyStoragePressure();
await notifyStoragePressure();
let notificationbox = document.getElementById("high-priority-global-notificationbox");
let allNotifications = notificationbox.allNotifications;
let allNotifications = gHighPriorityNotificationBox.allNotifications;
let pressureNotificationCount = 0;
allNotifications.forEach(notification => {
if (notification.getAttribute("value") == "storage-pressure-notification") {
@ -85,5 +82,5 @@ add_task(async function() {
}
});
is(pressureNotificationCount, 1, "Should not display the 2nd notification when there is already one");
notificationbox.removeAllNotifications();
gHighPriorityNotificationBox.removeAllNotifications();
});

View File

@ -13,8 +13,6 @@ ChromeUtils.defineModuleGetter(this, "TabCrashHandler",
* Wait for a <notification> to be closed then call the specified callback.
*/
function waitForNotificationClose(notification, cb) {
let parent = notification.parentNode;
let observer = new MutationObserver(function onMutatations(mutations) {
for (let mutation of mutations) {
for (let i = 0; i < mutation.removedNodes.length; i++) {
@ -27,20 +25,18 @@ function waitForNotificationClose(notification, cb) {
}
}
});
observer.observe(parent, {childList: true});
observer.observe(notification.control.stack, {childList: true});
}
function closeAllNotifications() {
let notificationBox = document.getElementById("global-notificationbox");
if (!notificationBox || !notificationBox.currentNotification) {
if (!gNotificationBox.currentNotification) {
return Promise.resolve();
}
return new Promise(resolve => {
for (let notification of notificationBox.allNotifications) {
for (let notification of gNotificationBox.allNotifications) {
waitForNotificationClose(notification, function() {
if (notificationBox.allNotifications.length === 0) {
if (gNotificationBox.allNotifications.length === 0) {
resolve();
}
});

View File

@ -26,7 +26,7 @@ function promiseNotification(aBrowser, value, expected, input) {
notificationObserver.disconnect();
}
notificationObserver = new MutationObserver(checkForNotification);
notificationObserver.observe(notificationBox, {childList: true});
notificationObserver.observe(notificationBox.stack, {childList: true});
} else {
setTimeout(() => {
is(notificationBox.getNotificationWithValue(value), null,

View File

@ -491,8 +491,28 @@ var Policies = {
"Extensions": {
onBeforeUIStartup(manager, param) {
let uninstallingPromise = Promise.resolve();
if ("Uninstall" in param) {
uninstallingPromise = runOncePerModification("extensionsUninstall", JSON.stringify(param.Uninstall), async () => {
// If we're uninstalling add-ons, re-run the extensionsInstall runOnce even if it hasn't
// changed, which will allow add-ons to be updated.
Services.prefs.clearUserPref("browser.policies.runOncePerModification.extensionsInstall");
let addons = await AddonManager.getAddonsByIDs(param.Uninstall);
for (let addon of addons) {
if (addon) {
try {
await addon.uninstall();
} catch (e) {
// This can fail for add-ons that can't be uninstalled.
// Just ignore.
}
}
}
});
}
if ("Install" in param) {
runOncePerModification("extensionsInstall", JSON.stringify(param.Install), () => {
runOncePerModification("extensionsInstall", JSON.stringify(param.Install), async () => {
await uninstallingPromise;
for (let location of param.Install) {
let url;
if (location.includes("://")) {
@ -544,21 +564,6 @@ var Policies = {
}
});
}
if ("Uninstall" in param) {
runOncePerModification("extensionsUninstall", JSON.stringify(param.Uninstall), async () => {
let addons = await AddonManager.getAddonsByIDs(param.Uninstall);
for (let addon of addons) {
if (addon) {
try {
addon.uninstall();
} catch (e) {
// This can fail for add-ons that can't be uninstalled.
// Just ignore.
}
}
}
});
}
if ("Locked" in param) {
for (let ID of param.Locked) {
manager.disallowFeature(`modify-extension:${ID}`);
@ -1046,6 +1051,8 @@ function runOnce(actionName, callback) {
* callback once when the policy is set, then never again.
* runOncePerModification runs the callback once each time the policy value
* changes from its previous value.
* If the callback that was passed is an async function, you can await on this
* function to await for the callback.
*
* @param {string} actionName
* A given name which will be used to track if this callback has run.
@ -1057,16 +1064,19 @@ function runOnce(actionName, callback) {
* string.
* @param {Function} callback
* The callback to be run when the pref value changes
* @returns Promise
* A promise that will resolve once the callback finishes running.
*
*/
function runOncePerModification(actionName, policyValue, callback) {
async function runOncePerModification(actionName, policyValue, callback) {
let prefName = `browser.policies.runOncePerModification.${actionName}`;
let oldPolicyValue = Services.prefs.getStringPref(prefName, undefined);
if (policyValue === oldPolicyValue) {
log.debug(`Not running action ${actionName} again because the policy's value is unchanged`);
return;
return Promise.resolve();
}
Services.prefs.setStringPref(prefName, policyValue);
callback();
return callback();
}
let gChromeURLSBlocked = false;

View File

@ -5,7 +5,8 @@ support-files =
config_broken_json.json
opensearch.html
opensearchEngine.xml
policytest.xpi
policytest_v0.1.xpi
policytest_v0.2.xpi
policy_websitefilter_block.html
policy_websitefilter_exception.html
../../../../../toolkit/components/antitracking/test/browser/page.html

View File

@ -3,6 +3,7 @@
"use strict";
const addonID = "policytest@mozilla.com";
const BASE_URL = "http://mochi.test:8888/browser/browser/components/enterprisepolicies/tests/browser";
add_task(async function test_addon_install() {
let installPromise = wait_for_addon_install();
@ -10,7 +11,7 @@ add_task(async function test_addon_install() {
"policies": {
"Extensions": {
"Install": [
"http://mochi.test:8888/browser/browser/components/enterprisepolicies/tests/browser/policytest.xpi",
`${BASE_URL}/policytest_v0.1.xpi`,
],
"Locked": [
addonID,
@ -21,6 +22,7 @@ add_task(async function test_addon_install() {
await installPromise;
let addon = await AddonManager.getAddonByID(addonID);
isnot(addon, null, "Addon not installed.");
is(addon.version, "0.1", "Addon version is correct");
Assert.deepEqual(addon.installTelemetryInfo, {source: "enterprise-policy"},
"Got the expected addon.installTelemetryInfo");
@ -41,7 +43,40 @@ add_task(async function test_addon_locked() {
BrowserTestUtils.removeTab(tab);
});
add_task(async function test_addon_reinstall() {
// Test that uninstalling and reinstalling the same addon ID works as expected.
// This can be used to update an addon.
let uninstallPromise = wait_for_addon_uninstall();
let installPromise = wait_for_addon_install();
await setupPolicyEngineWithJson({
"policies": {
"Extensions": {
"Uninstall": [
addonID,
],
"Install": [
`${BASE_URL}/policytest_v0.2.xpi`,
],
},
},
});
// Older version was uninstalled
await uninstallPromise;
// New version was installed
await installPromise;
let addon = await AddonManager.getAddonByID(addonID);
isnot(addon, null, "Addon still exists because the policy was used to update it.");
is(addon.version, "0.2", "New version is correct");
});
add_task(async function test_addon_uninstall() {
EnterprisePolicyTesting.resetRunOnceState();
let uninstallPromise = wait_for_addon_uninstall();
await setupPolicyEngineWithJson({
"policies": {

View File

@ -5,29 +5,24 @@
Name | Used for | Type | Example value
--- | --- | --- | ---
`whitelistHosts` | Whitelist a host in order to fetch messages from its endpoint | `[String]` | `["gist.github.com", "gist.githubusercontent.com", "localhost:8000"]`
`providers.snippets` | Message provider options for snippets | `Object` | [see below](#message-providers)
`providers.cfr` | Message provider options for cfr | `Object` | [see below](#message-providers)
`providers.onboarding` | Message provider options for onboarding | `Object` | [see below](#message-providers)
`messageProviders` | Message provider options | `Object` | [see below](#message-providers)
### Message providers examples
### Message providers
```json
{
"id" : "snippets",
"type" : "remote",
"enabled": true,
"url" : "https://snippets.cdn.mozilla.net/us-west/bundles/bundle_d6d90fb9098ce8b45e60acf601bcb91b68322309.json",
"updateCycleInMs" : 14400000
}
```
```json
{
"id" : "onboarding",
"enabled": true,
"type" : "local",
"localProvider" : "OnboardingMessageProvider"
}
[
{
"id" : "onboarding",
"type" : "local",
"localProvider" : "OnboardingMessageProvider"
},
{
"type" : "remote",
"url" : "https://snippets.cdn.mozilla.net/us-west/bundles/bundle_d6d90fb9098ce8b45e60acf601bcb91b68322309.json",
"updateCycleInMs" : 14400000,
"id" : "snippets"
}
]
```
## Admin Interface

View File

@ -9,7 +9,6 @@ Please note that some targeting attributes require stricter controls on the tele
## Available attributes
* [addonsInfo](#addonsinfo)
* [attributionData](#attributiondata)
* [browserSettings](#browsersettings)
* [currentDate](#currentdate)
* [devToolsOpenedCount](#devtoolsopenedcount)
@ -77,40 +76,20 @@ interface AddonsInfoResponse {
};
}
```
### `attributionData`
An object containing information on exactly how Firefox was downloaded
#### Examples
* Was the browser installed via the `"back_to_school"` campaign?
```java
attributionData && attributionData.campaign == "back_to_school"
```
#### Definition
```ts
declare const attributionData: AttributionCode;
interface AttributionCode {
// Descriptor for where the download started from
campaign: string,
// A source, like addons.mozilla.org, or google.com
source: string,
// The medium for the download, like if this was referral
medium: string,
// Additional content, like an addonID for instance
content: string
}
```
### `browserSettings`
Includes two properties:
* `attribution`, which indicates how Firefox was downloaded - DEPRECATED - please use [attributionData](#attributiondata)
* `attribution`, which indicates how Firefox was downloaded,
* `update`, which has information about how Firefox updates
Note that attribution can be `undefined`, so you should check that it exists first.
#### Examples
* Was the browser installed via the `"back_to_school"` campaign?
```java
browserSettings.attribution && browserSettings.attribution.campaign == "back_to_school"
```
* Is updating enabled?
```java
browserSettings.update.enabled

View File

@ -4,13 +4,11 @@ import {SubmitFormSnippet} from "../SubmitFormSnippet/SubmitFormSnippet.jsx";
export const NewsletterSnippet = props => {
const extendedContent = {
form_action: "https://basket.mozilla.org/subscribe.json",
scene2_email_placeholder_text: "Your Email Here",
scene2_button_label: "Sign Me Up",
...props.content,
hidden_inputs: {
newsletters: props.content.scene2_newsletter || "mozilla-foundation",
fmt: "H",
lang: props.content.locale || "en-US",
lang: "en-US",
source_url: `https://snippets.mozilla.com/show/${props.id}`,
...props.content.hidden_inputs,
},

View File

@ -86,10 +86,6 @@
&.gift {
background-image: url('resource://activity-stream/data/content/assets/illustration-gift@2x.png');
}
&.sync {
background-image: url('resource://activity-stream/data/content/assets/illustration-sync@2x.png');
}
}
.onboardingContent {

View File

@ -150,7 +150,7 @@ export class SubmitFormSnippet extends React.PureComponent {
return (<SnippetBase {...this.props} className={containerClass} footerDismiss={true}>
{content.scene2_icon ? <div className="scene2Icon"><img src={content.scene2_icon} /></div> : null}
<div className="message">
<p>{content.scene2_title ? <h3 className="scene2Title">{content.scene2_title}</h3> : null} {content.scene2_text}</p>
<p>{content.scene2_text}</p>
</div>
<form action={content.form_action} method={this.props.form_method} onSubmit={this.handleSubmit} ref="form">
{this.renderHiddenFormInputs()}

View File

@ -26,13 +26,6 @@
}
}
.scene2Title {
font-size: inherit;
margin: 0;
font-weight: bold;
display: inline;
}
form {
display: flex;
flex-direction: column;

View File

@ -1,5 +1,4 @@
import {ASRouterUtils} from "../../asrouter/asrouter-content";
import {ModalOverlay} from "../../asrouter/components/ModalOverlay/ModalOverlay";
import React from "react";
export class ASRouterAdmin extends React.PureComponent {
@ -12,17 +11,7 @@ export class ASRouterAdmin extends React.PureComponent {
this.findOtherBundledMessagesOfSameTemplate = this.findOtherBundledMessagesOfSameTemplate.bind(this);
this.handleExpressionEval = this.handleExpressionEval.bind(this);
this.onChangeTargetingParameters = this.onChangeTargetingParameters.bind(this);
this.onCopyTargetingParams = this.onCopyTargetingParams.bind(this);
this.onPasteTargetingParams = this.onPasteTargetingParams.bind(this);
this.onNewTargetingParams = this.onNewTargetingParams.bind(this);
this.state = {
messageFilter: "all",
evaluationStatus: {},
stringTargetingParameters: null,
newStringTargetingParameters: null,
copiedToClipboard: false,
pasteFromClipboard: false,
};
this.state = {messageFilter: "all", evaluationStatus: {}, stringTargetingParameters: null};
}
onMessage({data: action}) {
@ -100,6 +89,7 @@ export class ASRouterAdmin extends React.PureComponent {
onChangeTargetingParameters(event) {
const {name} = event.target;
const {value} = event.target;
this.refs.evaluationStatus.innerText = "";
this.setState(({stringTargetingParameters}) => {
let targetingParametersError = null;
@ -112,15 +102,67 @@ export class ASRouterAdmin extends React.PureComponent {
targetingParametersError = {id: name};
}
return {
copiedToClipboard: false,
evaluationStatus: {},
stringTargetingParameters: updatedParameters,
targetingParametersError,
};
return {stringTargetingParameters: updatedParameters, targetingParametersError};
});
}
renderMessageItem(msg) {
const isCurrent = msg.id === this.state.lastMessageId;
const isBlocked = this.state.messageBlockList.includes(msg.id);
const impressions = this.state.messageImpressions[msg.id] ? this.state.messageImpressions[msg.id].length : 0;
let itemClassName = "message-item";
if (isCurrent) { itemClassName += " current"; }
if (isBlocked) { itemClassName += " blocked"; }
return (<tr className={itemClassName} key={msg.id}>
<td className="message-id"><span>{msg.id} <br /></span></td>
<td>
<button className={`button ${(isBlocked ? "" : " primary")}`} onClick={isBlocked ? this.handleUnblock(msg) : this.handleBlock(msg)}>{isBlocked ? "Unblock" : "Block"}</button>
{isBlocked ? null : <button className="button" onClick={this.handleOverride(msg.id)}>Show</button>}
<br />({impressions} impressions)
</td>
<td className="message-summary">
<pre>{JSON.stringify(msg, null, 2)}</pre>
</td>
</tr>);
}
renderMessages() {
if (!this.state.messages) {
return null;
}
const messagesToShow = this.state.messageFilter === "all" ? this.state.messages : this.state.messages.filter(message => message.provider === this.state.messageFilter);
return (<table><tbody>
{messagesToShow.map(msg => this.renderMessageItem(msg))}
</tbody></table>);
}
onChangeMessageFilter(event) {
this.setState({messageFilter: event.target.value});
}
renderMessageFilter() {
if (!this.state.providers) {
return null;
}
return (<p>Show messages from <select value={this.state.messageFilter} onChange={this.onChangeMessageFilter}>
<option value="all">all providers</option>
{this.state.providers.map(provider => (<option key={provider.id} value={provider.id}>{provider.id}</option>))}
</select></p>);
}
renderTableHead() {
return (<thead>
<tr className="message-item">
<td className="min" />
<td className="min">Provider ID</td>
<td>Source</td>
<td>Last Updated</td>
</tr>
</thead>);
}
handleEnabledToggle(event) {
const provider = this.state.providerPrefs.find(p => p.id === event.target.dataset.provider);
const userPrefInfo = this.state.userPrefs;
@ -149,107 +191,6 @@ export class ASRouterAdmin extends React.PureComponent {
this.setState({messageFilter: "all"});
}
onChangeMessageFilter(event) {
this.setState({messageFilter: event.target.value});
}
// Simulate a copy event that sets to clipboard all targeting paramters and values
onCopyTargetingParams(event) {
const stringTargetingParameters = {...this.state.stringTargetingParameters};
for (const key of Object.keys(stringTargetingParameters)) {
// If the value is not set the parameter will be lost when we stringify
if (stringTargetingParameters[key] === undefined) {
stringTargetingParameters[key] = null;
}
}
const setClipboardData = e => {
e.preventDefault();
e.clipboardData.setData("text", JSON.stringify(stringTargetingParameters, null, 2));
document.removeEventListener("copy", setClipboardData);
this.setState({copiedToClipboard: true});
};
document.addEventListener("copy", setClipboardData);
document.execCommand("copy");
}
// Copy all clipboard data to targeting parameters
onPasteTargetingParams(event) {
this.setState(({pasteFromClipboard}) => ({
pasteFromClipboard: !pasteFromClipboard,
newStringTargetingParameters: "",
}));
}
onNewTargetingParams(event) {
this.setState({newStringTargetingParameters: event.target.value});
event.target.classList.remove("errorState");
this.refs.targetingParamsEval.innerText = "";
try {
const stringTargetingParameters = JSON.parse(event.target.value);
this.setState({stringTargetingParameters});
} catch (e) {
event.target.classList.add("errorState");
this.refs.targetingParamsEval.innerText = e.message;
}
}
renderMessageItem(msg) {
const isCurrent = msg.id === this.state.lastMessageId;
const isBlocked = this.state.messageBlockList.includes(msg.id) || this.state.messageBlockList.includes(msg.campaign);
const impressions = this.state.messageImpressions[msg.id] ? this.state.messageImpressions[msg.id].length : 0;
let itemClassName = "message-item";
if (isCurrent) { itemClassName += " current"; }
if (isBlocked) { itemClassName += " blocked"; }
return (<tr className={itemClassName} key={msg.id}>
<td className="message-id"><span>{msg.id} <br /></span></td>
<td>
<button className={`button ${(isBlocked ? "" : " primary")}`} onClick={isBlocked ? this.handleUnblock(msg) : this.handleBlock(msg)}>{isBlocked ? "Unblock" : "Block"}</button>
{isBlocked ? null : <button className="button" onClick={this.handleOverride(msg.id)}>Show</button>}
<br />({impressions} impressions)
</td>
<td className="message-summary">
<pre>{JSON.stringify(msg, null, 2)}</pre>
</td>
</tr>);
}
renderMessages() {
if (!this.state.messages) {
return null;
}
const messagesToShow = this.state.messageFilter === "all" ? this.state.messages : this.state.messages.filter(message => message.provider === this.state.messageFilter);
return (<table><tbody>
{messagesToShow.map(msg => this.renderMessageItem(msg))}
</tbody></table>);
}
renderMessageFilter() {
if (!this.state.providers) {
return null;
}
return (<p>Show messages from <select value={this.state.messageFilter} onChange={this.onChangeMessageFilter}>
<option value="all">all providers</option>
{this.state.providers.map(provider => (<option key={provider.id} value={provider.id}>{provider.id}</option>))}
</select></p>);
}
renderTableHead() {
return (<thead>
<tr className="message-item">
<td className="min" />
<td className="min">Provider ID</td>
<td>Source</td>
<td className="min">Cohort</td>
<td className="min">Last Updated</td>
</tr>
</thead>);
}
renderProviders() {
const providersConfig = this.state.providerPrefs;
const providerInfo = this.state.providers;
@ -264,7 +205,11 @@ export class ASRouterAdmin extends React.PureComponent {
let label = "local";
if (provider.type === "remote") {
label = (<span>endpoint (<a className="providerUrl" target="_blank" href={info.url}>{info.url}</a>)</span>);
let displayUrl = "";
try {
displayUrl = `(${new URL(info.url).hostname})`;
} catch (err) {}
label = (<span>endpoint <a target="_blank" href={info.url}>{displayUrl}</a></span>);
} else if (provider.type === "remote-settings") {
label = `remote settings (${provider.bucket})`;
}
@ -285,30 +230,12 @@ export class ASRouterAdmin extends React.PureComponent {
<td>{isTestProvider ? <input type="checkbox" disabled={true} readOnly={true} checked={true} /> : <input type="checkbox" data-provider={provider.id} checked={isUserEnabled && isSystemEnabled} onChange={this.handleEnabledToggle} />}</td>
<td>{provider.id}</td>
<td><span className={`sourceLabel${(isUserEnabled && isSystemEnabled) ? "" : " isDisabled"}`}>{label}</span></td>
<td>{provider.cohort}</td>
<td style={{whiteSpace: "nowrap"}}>{info.lastUpdated ? new Date(info.lastUpdated).toLocaleString() : ""}</td>
</tr>);
})}
</tbody></table>);
}
renderPasteModal() {
if (!this.state.pasteFromClipboard) {
return null;
}
const errors = this.refs.targetingParamsEval && this.refs.targetingParamsEval.innerText.length;
return (
<ModalOverlay title="New targeting parameters" button_label={errors ? "Cancel" : "Done"} onDoneButton={this.onPasteTargetingParams}>
<div className="onboardingMessage">
<p>
<textarea onChange={this.onNewTargetingParams} value={this.state.newStringTargetingParameters} autoFocus={true} rows="20" cols="60" />
</p>
<p ref="targetingParamsEval" />
</div>
</ModalOverlay>
);
}
renderTargetingParameters() {
// There was no error and the result is truthy
const success = this.state.evaluationStatus.success && !!this.state.evaluationStatus.result;
@ -326,16 +253,6 @@ export class ASRouterAdmin extends React.PureComponent {
</td>
</tr>
<tr><td><h2>Modify targeting parameters</h2></td></tr>
<tr>
<td>
<button className="ASRouterButton secondary" onClick={this.onCopyTargetingParams} disabled={this.state.copiedToClipboard}>
{this.state.copiedToClipboard ? "Parameters copied!" : "Copy parameters"}
</button>
<button className="ASRouterButton secondary" onClick={this.onPasteTargetingParams} disabled={this.state.pasteFromClipboard}>
Paste parameters
</button>
</td>
</tr>
{this.state.stringTargetingParameters && Object.keys(this.state.stringTargetingParameters).map((param, i) => {
const value = this.state.stringTargetingParameters[param];
const errorState = this.state.targetingParametersError && this.state.targetingParametersError.id === param;
@ -363,7 +280,6 @@ export class ASRouterAdmin extends React.PureComponent {
<h2>Messages</h2>
{this.renderMessageFilter()}
{this.renderMessages()}
{this.renderPasteModal()}
{this.renderTargetingParameters()}
</div>);
}

View File

@ -91,10 +91,6 @@
}
}
.providerUrl {
font-size: 12px;
}
pre {
background: var(--newtab-textbox-background-color);
margin: 0;

View File

@ -2,7 +2,6 @@
display: flex;
align-items: center;
white-space: nowrap;
line-height: 1.230769231; // (16 / 13) -> 16px computed
&::after {
background: url('#{$image-path}topic-show-more-12.svg') no-repeat center center;

View File

@ -27,7 +27,6 @@
.cta-text {
font-weight: normal;
font-size: 13px;
line-height: 1.230769231; // (16 / 13) > 16px computed
}
.pocket-cta-button,

View File

@ -84,6 +84,11 @@
font-size: 13px;
}
}
@media (min-width: $break-point-large) {
line-height: 16px;
height: 16px;
}
}
@media (min-width: $break-point-widest) {

View File

@ -183,7 +183,7 @@ export class TopSiteLink extends React.PureComponent {
}
return (<li className={topSiteOuterClassName} onDrop={this.onDragEvent} onDragOver={this.onDragEvent} onDragEnter={this.onDragEvent} onDragLeave={this.onDragEvent} {...draggableProps}>
<div className="top-site-inner">
<a href={link.searchTopSite ? undefined : link.url} tabIndex="0" onKeyPress={this.onKeyPress} onClick={onClick} draggable={true}>
<a href={!link.searchTopSite && link.url} tabIndex="0" onKeyPress={this.onKeyPress} onClick={onClick} draggable={true}>
<div className="tile" aria-hidden={true} data-fallback={letterFallback}>
<div className={imageClassName} style={imageStyle} />
{link.searchTopSite && <div className="top-site-icon search-topsite" />}

View File

@ -247,6 +247,10 @@ export class SnippetsProvider {
}
}
_noSnippetFallback() {
// TODO
}
_showRemoteSnippets() {
const snippetsEl = document.getElementById(this.elementId);
const payload = this.snippetsMap.get("snippets");
@ -268,8 +272,6 @@ export class SnippetsProvider {
// eslint-disable-next-line no-unsanitized/property
snippetsEl.innerHTML = payload;
this._logIfDevtools("Successfully added snippets.");
// Scripts injected by innerHTML are inactive, so we have to relocate them
// through DOM manipulation to activate their contents.
for (const scriptEl of snippetsEl.getElementsByTagName("script")) {
@ -288,13 +290,6 @@ export class SnippetsProvider {
}
}
// istanbul ignore next
_logIfDevtools(text) {
if (this.devtoolsEnabled) {
console.log("Legacy snippets:", text); // eslint-disable-line no-console
}
}
/**
* init - Fetch the snippet payload and show snippets
*
@ -309,11 +304,8 @@ export class SnippetsProvider {
appData: {},
elementId: "snippets",
connect: true,
devtoolsEnabled: false,
}, options);
this._logIfDevtools("Initializing...");
// Add listener so we know when snippets are blocked on other pages
if (global.RPMAddMessageListener) {
global.RPMAddMessageListener("ActivityStream:MainToContent", this._onAction);
@ -345,14 +337,12 @@ export class SnippetsProvider {
try {
this._showRemoteSnippets();
} catch (e) {
this._logIfDevtools("Problem inserting remote snippets!");
console.error(e); // eslint-disable-line no-console
this._noSnippetFallback(e);
}
window.dispatchEvent(new Event(SNIPPETS_ENABLED_EVENT));
this.initialized = true;
this._logIfDevtools("Finished initializing.");
}
uninit() {
@ -407,7 +397,11 @@ export function addSnippetsSubscriber(store) {
location.hash !== "#asrouter"
) {
initializing = true;
await snippets.init({appData: state.Snippets, devtoolsEnabled: state.Prefs.values["asrouter.devtoolsEnabled"]});
await snippets.init({appData: state.Snippets});
// istanbul ignore if
if (state.Prefs.values["asrouter.devtoolsEnabled"]) {
console.log("Legacy snippets initialized"); // eslint-disable-line no-console
}
initializing = false;
/** If we should remove snippets... */

View File

@ -913,6 +913,10 @@ main {
.top-stories-bottom-container a.more-recommendations {
font-weight: normal;
font-size: 13px; }
@media (min-width: 866px) {
.top-stories-bottom-container {
line-height: 16px;
height: 16px; } }
@media (min-width: 1122px) {
.sections-list .normal-cards .section-list {
@ -1836,8 +1840,6 @@ a.firstrun-link {
.asrouter-admin .message-item .message-id {
font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Mono", "Droid Sans Mono", "Source Code Pro", monospace;
font-size: 12px; }
.asrouter-admin .providerUrl {
font-size: 12px; }
.asrouter-admin pre {
background: var(--newtab-textbox-background-color);
margin: 0;
@ -1870,8 +1872,7 @@ a.firstrun-link {
margin-inline-end: 10px; }
.pocket-logged-in-cta .cta-text {
font-weight: normal;
font-size: 13px;
line-height: 1.230769231; }
font-size: 13px; }
.pocket-logged-in-cta .pocket-cta-button,
.pocket-logged-in-cta .cta-text {
vertical-align: top; }
@ -1879,8 +1880,7 @@ a.firstrun-link {
.more-recommendations {
display: flex;
align-items: center;
white-space: nowrap;
line-height: 1.230769231; }
white-space: nowrap; }
.more-recommendations::after {
background: url("../data/content/assets/topic-show-more-12.svg") no-repeat center center;
content: '';
@ -2148,11 +2148,6 @@ a.firstrun-link {
.SubmitFormSnippet .scene2Icon img {
width: 98px;
display: inline-block; }
.SubmitFormSnippet .scene2Title {
font-size: inherit;
margin: 0;
font-weight: bold;
display: inline; }
.SubmitFormSnippet form {
display: flex;
flex-direction: column;
@ -2267,8 +2262,6 @@ a.firstrun-link {
background-image: url("resource://activity-stream/data/content/assets/illustration-screenshots@2x.png"); }
.onboardingMessage .onboardingMessageImage.gift {
background-image: url("resource://activity-stream/data/content/assets/illustration-gift@2x.png"); }
.onboardingMessage .onboardingMessageImage.sync {
background-image: url("resource://activity-stream/data/content/assets/illustration-sync@2x.png"); }
.onboardingMessage .onboardingContent {
height: 175px; }
.onboardingMessage .onboardingContent > span > h3 {

File diff suppressed because one or more lines are too long

View File

@ -916,6 +916,10 @@ main {
.top-stories-bottom-container a.more-recommendations {
font-weight: normal;
font-size: 13px; }
@media (min-width: 866px) {
.top-stories-bottom-container {
line-height: 16px;
height: 16px; } }
@media (min-width: 1122px) {
.sections-list .normal-cards .section-list {
@ -1839,8 +1843,6 @@ a.firstrun-link {
.asrouter-admin .message-item .message-id {
font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Mono", "Droid Sans Mono", "Source Code Pro", monospace;
font-size: 12px; }
.asrouter-admin .providerUrl {
font-size: 12px; }
.asrouter-admin pre {
background: var(--newtab-textbox-background-color);
margin: 0;
@ -1873,8 +1875,7 @@ a.firstrun-link {
margin-inline-end: 10px; }
.pocket-logged-in-cta .cta-text {
font-weight: normal;
font-size: 13px;
line-height: 1.230769231; }
font-size: 13px; }
.pocket-logged-in-cta .pocket-cta-button,
.pocket-logged-in-cta .cta-text {
vertical-align: top; }
@ -1882,8 +1883,7 @@ a.firstrun-link {
.more-recommendations {
display: flex;
align-items: center;
white-space: nowrap;
line-height: 1.230769231; }
white-space: nowrap; }
.more-recommendations::after {
background: url("../data/content/assets/topic-show-more-12.svg") no-repeat center center;
content: '';
@ -2151,11 +2151,6 @@ a.firstrun-link {
.SubmitFormSnippet .scene2Icon img {
width: 98px;
display: inline-block; }
.SubmitFormSnippet .scene2Title {
font-size: inherit;
margin: 0;
font-weight: bold;
display: inline; }
.SubmitFormSnippet form {
display: flex;
flex-direction: column;
@ -2270,8 +2265,6 @@ a.firstrun-link {
background-image: url("resource://activity-stream/data/content/assets/illustration-screenshots@2x.png"); }
.onboardingMessage .onboardingMessageImage.gift {
background-image: url("resource://activity-stream/data/content/assets/illustration-gift@2x.png"); }
.onboardingMessage .onboardingMessageImage.sync {
background-image: url("resource://activity-stream/data/content/assets/illustration-sync@2x.png"); }
.onboardingMessage .onboardingContent {
height: 175px; }
.onboardingMessage .onboardingContent > span > h3 {

File diff suppressed because one or more lines are too long

View File

@ -913,6 +913,10 @@ main {
.top-stories-bottom-container a.more-recommendations {
font-weight: normal;
font-size: 13px; }
@media (min-width: 866px) {
.top-stories-bottom-container {
line-height: 16px;
height: 16px; } }
@media (min-width: 1122px) {
.sections-list .normal-cards .section-list {
@ -1836,8 +1840,6 @@ a.firstrun-link {
.asrouter-admin .message-item .message-id {
font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Mono", "Droid Sans Mono", "Source Code Pro", monospace;
font-size: 12px; }
.asrouter-admin .providerUrl {
font-size: 12px; }
.asrouter-admin pre {
background: var(--newtab-textbox-background-color);
margin: 0;
@ -1870,8 +1872,7 @@ a.firstrun-link {
margin-inline-end: 10px; }
.pocket-logged-in-cta .cta-text {
font-weight: normal;
font-size: 13px;
line-height: 1.230769231; }
font-size: 13px; }
.pocket-logged-in-cta .pocket-cta-button,
.pocket-logged-in-cta .cta-text {
vertical-align: top; }
@ -1879,8 +1880,7 @@ a.firstrun-link {
.more-recommendations {
display: flex;
align-items: center;
white-space: nowrap;
line-height: 1.230769231; }
white-space: nowrap; }
.more-recommendations::after {
background: url("../data/content/assets/topic-show-more-12.svg") no-repeat center center;
content: '';
@ -2148,11 +2148,6 @@ a.firstrun-link {
.SubmitFormSnippet .scene2Icon img {
width: 98px;
display: inline-block; }
.SubmitFormSnippet .scene2Title {
font-size: inherit;
margin: 0;
font-weight: bold;
display: inline; }
.SubmitFormSnippet form {
display: flex;
flex-direction: column;
@ -2267,8 +2262,6 @@ a.firstrun-link {
background-image: url("resource://activity-stream/data/content/assets/illustration-screenshots@2x.png"); }
.onboardingMessage .onboardingMessageImage.gift {
background-image: url("resource://activity-stream/data/content/assets/illustration-gift@2x.png"); }
.onboardingMessage .onboardingMessageImage.sync {
background-image: url("resource://activity-stream/data/content/assets/illustration-sync@2x.png"); }
.onboardingMessage .onboardingContent {
height: 175px; }
.onboardingMessage .onboardingContent > span > h3 {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -33,16 +33,14 @@ Two encoded integers for about:newtab and about:home are combined in a bitwise f
## Page takeover ping
This ping is submitted once upon Activity Stream initialization if either about:home or about:newtab are set to a custom URL. It sends the category of the custom URL. It also includes the web extension id of the extension controlling the home and/or newtab page.
This ping is submitted once upon Activity Stream initialization if either about:home or about:newtab are set to a custom URL. It sends the category of the custom URL.
```js
{
"event": "PAGE_TAKEOVER_DATA",
"value": {
"home_url_category": ["search-engine" | "search-engine-mozilla-tag" | "search-engine-other-tag" | "news-portal" | "ecommerce" | "social-media" | "known-hijacker" | "other"],
"home_extension_id": "26288a14-5cc4-d14f-ae0a-bb01ef45be9c",
"newtab_url_category": ["search-engine" | "search-engine-mozilla-tag" | "search-engine-other-tag" | "news-portal" | "ecommerce" | "social-media" | "known-hijacker" | "other"],
"newtab_extension_id": "26288a14-5cc4-d14f-ae0a-bb01ef45be9c",
},
// Basic metadata
@ -840,7 +838,7 @@ This reports the user's interaction with Activity Stream Router.
"source": "CFR",
// message_id could be the ID of the recommendation, such as "wikipedia_addon"
"message_id": "wikipedia_addon",
"event": "[INSTALL | BLOCK | DISMISS | RATIONALE | LEARN_MORE | CLICK_DOORHANGER | MANAGE]"
"event": "[INSTALL | BLOCK | DISMISS | RATIONALE | LEARN_MORE | CLICK_DOORHANGER]"
}
```
@ -856,7 +854,7 @@ This reports the user's interaction with Activity Stream Router.
// message_id should be a bucket ID in the release channel, we may not use the
// individual ID, such as addon ID, per legal's request
"message_id": "bucket_id",
"event": "[INSTALL | BLOCK | DISMISS | RATIONALE | LEARN_MORE | CLICK_DOORHANGER | MANAGE]"
"event": "[INSTALL | BLOCK | DISMISS | RATIONALE | LEARN_MORE | CLICK_DOORHANGER]"
}
```

View File

@ -673,7 +673,6 @@ class _ASRouter {
let {state} = this;
return state.messages.filter(item =>
!state.messageBlockList.includes(item.id) &&
(!item.campaign || !state.messageBlockList.includes(item.campaign)) &&
!state.providerBlockList.includes(item.provider)
);
}
@ -830,20 +829,10 @@ class _ASRouter {
const idsToBlock = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
await this.setState(state => {
const messageBlockList = [...state.messageBlockList];
const messageBlockList = [...state.messageBlockList, ...idsToBlock];
// When a message is blocked, its impressions should be cleared as well
const messageImpressions = {...state.messageImpressions};
idsToBlock.forEach(id => {
const message = state.messages.find(m => m.id === id);
const idToBlock = (message && message.campaign) ? message.campaign : id;
if (!messageBlockList.includes(idToBlock)) {
messageBlockList.push(idToBlock);
}
// When a message is blocked, its impressions should be cleared as well
delete messageImpressions[id];
});
idsToBlock.forEach(id => delete messageImpressions[id]);
this._storage.set("messageBlockList", messageBlockList);
return {messageBlockList, messageImpressions};
});
@ -1001,9 +990,7 @@ class _ASRouter {
case "UNBLOCK_MESSAGE_BY_ID":
await this.setState(state => {
const messageBlockList = [...state.messageBlockList];
const message = state.messages.find(m => m.id === action.data.id);
const idToUnblock = (message && message.campaign) ? message.campaign : action.data.id;
messageBlockList.splice(messageBlockList.indexOf(idToUnblock), 1);
messageBlockList.splice(messageBlockList.indexOf(action.data.id), 1);
this._storage.set("messageBlockList", messageBlockList);
return {messageBlockList};
});

View File

@ -5,13 +5,13 @@
ChromeUtils.import("resource://gre/modules/Services.jsm");
const PROVIDER_PREF_BRANCH = "browser.newtabpage.activity-stream.asrouter.providers.";
const PROVIDER_PREF = "browser.newtabpage.activity-stream.asrouter.messageProviders";
const DEVTOOLS_PREF = "browser.newtabpage.activity-stream.asrouter.devtoolsEnabled";
const DEFAULT_STATE = {
_initialized: false,
_providers: null,
_providerPrefBranch: PROVIDER_PREF_BRANCH,
_providerPref: PROVIDER_PREF,
_devtoolsEnabled: null,
_devtoolsPref: DEVTOOLS_PREF,
};
@ -35,24 +35,17 @@ class _ASRouterPreferences {
}
_getProviderConfig() {
const prefList = Services.prefs.getChildList(this._providerPrefBranch);
return prefList.reduce((filtered, pref) => {
let value;
try {
value = JSON.parse(Services.prefs.getStringPref(pref, ""));
} catch (e) {
Cu.reportError(`Could not parse ASRouter preference. Try resetting ${pref} in about:config.`);
}
if (value) {
filtered.push(value);
}
return filtered;
}, []);
try {
return JSON.parse(Services.prefs.getStringPref(this._providerPref, ""));
} catch (e) {
Cu.reportError(`Could not parse ASRouter preference. Try resetting ${this._providerPref} in about:config.`);
}
return null;
}
get providers() {
if (!this._initialized || this._providers === null) {
const config = this._getProviderConfig();
const config = this._getProviderConfig() || [];
const providers = config.map(provider => Object.freeze(provider));
if (this.devtoolsEnabled) {
providers.unshift(TEST_PROVIDER);
@ -65,19 +58,26 @@ class _ASRouterPreferences {
enableOrDisableProvider(id, value) {
const providers = this._getProviderConfig();
const config = providers.find(p => p.id === id);
if (!config) {
Cu.reportError(`Cannot set enabled state for '${id}' because the pref ${this._providerPrefBranch}${id} does not exist or is not correctly formatted.`);
if (!providers) {
Cu.reportError(`Cannot enable/disable providers if ${this._providerPref} is unparseable.`);
return;
}
if (!providers.find(p => p.id === id)) {
Cu.reportError(`Cannot set enabled state for '${id}' because it does not exist in ${this._providerPref}`);
return;
}
Services.prefs.setStringPref(this._providerPrefBranch + id, JSON.stringify({...config, enabled: value}));
const newConfig = providers.map(provider => {
if (provider.id === id) {
return {...provider, enabled: value};
}
return provider;
});
Services.prefs.setStringPref(this._providerPref, JSON.stringify(newConfig));
}
resetProviderPref() {
for (const pref of Services.prefs.getChildList(this._providerPrefBranch)) {
Services.prefs.clearUserPref(pref);
}
Services.prefs.clearUserPref(this._providerPref);
for (const id of Object.keys(USER_PREFERENCES)) {
Services.prefs.clearUserPref(USER_PREFERENCES[id]);
}
@ -101,11 +101,14 @@ class _ASRouterPreferences {
}
observe(aSubject, aTopic, aPrefName) {
if (aPrefName && aPrefName.startsWith(this._providerPrefBranch)) {
this._providers = null;
} else if (aPrefName === this._devtoolsPref) {
this._providers = null;
this._devtoolsEnabled = null;
switch (aPrefName) {
case this._providerPref:
this._providers = null;
break;
case this._devtoolsPref:
this._providers = null;
this._devtoolsEnabled = null;
break;
}
this._callbacks.forEach(cb => cb(aPrefName));
}
@ -144,7 +147,7 @@ class _ASRouterPreferences {
if (this._initialized) {
return;
}
Services.prefs.addObserver(this._providerPrefBranch, this);
Services.prefs.addObserver(this._providerPref, this);
Services.prefs.addObserver(this._devtoolsPref, this);
for (const id of Object.keys(USER_PREFERENCES)) {
Services.prefs.addObserver(USER_PREFERENCES[id], this);
@ -154,7 +157,7 @@ class _ASRouterPreferences {
uninit() {
if (this._initialized) {
Services.prefs.removeObserver(this._providerPrefBranch, this);
Services.prefs.removeObserver(this._providerPref, this);
Services.prefs.removeObserver(this._devtoolsPref, this);
for (const id of Object.keys(USER_PREFERENCES)) {
Services.prefs.removeObserver(USER_PREFERENCES[id], this);

View File

@ -15,8 +15,8 @@ ChromeUtils.defineModuleGetter(this, "TelemetryEnvironment",
"resource://gre/modules/TelemetryEnvironment.jsm");
ChromeUtils.defineModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
ChromeUtils.defineModuleGetter(this, "AttributionCode",
"resource:///modules/AttributionCode.jsm");
ChromeUtils.defineModuleGetter(this, "NewTabUtils",
"resource://gre/modules/NewTabUtils.jsm");
const FXA_USERNAME_PREF = "services.sync.username";
const SEARCH_REGION_PREF = "browser.search.region";
@ -163,15 +163,10 @@ const TargetingGetters = {
get browserSettings() {
const {settings} = TelemetryEnvironment.currentEnvironment;
return {
// This way of getting attribution is deprecated - use atttributionData instead
attribution: settings.attribution,
update: settings.update,
};
},
get attributionData() {
// Attribution is determined at startup - so we can use the cached attribution at this point
return AttributionCode.getCachedAttributionData();
},
get currentDate() {
return new Date();
},
@ -255,11 +250,11 @@ const TargetingGetters = {
)));
},
get pinnedSites() {
return NewTabUtils.pinnedLinks.links.map(site => (site ? {
return NewTabUtils.pinnedLinks.links.map(site => ({
url: site.url,
host: (new URL(site.url)).hostname,
searchTopSite: site.searchTopSite,
} : {}));
}));
},
get providerCohorts() {
return ASRouterPreferences.providers.reduce((prev, current) => {

View File

@ -202,34 +202,33 @@ const PREFS_CONFIG = new Map([
title: "Does the user allow CFR recommendations?",
value: true,
}],
["asrouter.providers.onboarding", {
title: "Configuration for onboarding provider",
value: JSON.stringify({
["asrouter.messageProviders", {
title: "Configuration for ASRouter message providers",
/**
* Each provider must have a unique id and a type of "local" or "remote".
* Local providers must specify the name of an ASRouter message provider.
* Remote providers must specify a `url` and an `updateCycleInMs`.
* Each provider must also have an `enabled` boolean.
*/
value: JSON.stringify([{
id: "onboarding",
type: "local",
localProvider: "OnboardingMessageProvider",
enabled: true,
}),
}],
["asrouter.providers.snippets", {
title: "Configuration for snippets provider",
value: JSON.stringify({
}, {
id: "snippets",
type: "remote",
url: "https://snippets.cdn.mozilla.net/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/",
updateCycleInMs: ONE_HOUR_IN_MS * 4,
enabled: UpdateUtils.getUpdateChannel(true) === "nightly",
}),
}],
["asrouter.providers.cfr", {
title: "Configuration for CFR provider",
value: JSON.stringify({
enabled: false,
}, {
id: "cfr",
type: "local",
localProvider: "CFRMessageProvider",
frequency: {custom: [{period: "daily", cap: 1}]},
enabled: true,
}),
enabled: IS_NIGHTLY_OR_UNBRANDED_BUILD,
cohort: IS_NIGHTLY_OR_UNBRANDED_BUILD ? "nightly" : "",
}]),
}],
]);

View File

@ -39,6 +39,58 @@ const REDDIT_ENHANCEMENT_PARAMS = {
};
const CFR_MESSAGES = [
{
id: "FACEBOOK_CONTAINER_1",
template: "cfr_doorhanger",
content: {
bucket_id: "CFR_M1",
notification_text: {string_id: "cfr-doorhanger-extension-notification"},
heading_text: {string_id: "cfr-doorhanger-extension-heading"},
info_icon: {
label: {string_id: "cfr-doorhanger-extension-sumo-link"},
sumo_path: FACEBOOK_CONTAINER_PARAMS.sumo_path,
},
addon: {
id: "954390",
title: "Facebook Container",
icon: "resource://activity-stream/data/content/assets/cfr_fb_container.png",
rating: 4.6,
users: 299019,
author: "Mozilla",
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/facebook-container/",
},
text: "Stop Facebook from tracking your activity across the web. Use Facebook the way you normally do without annoying ads following you around.",
buttons: {
primary: {
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: null},
},
},
secondary: [{
label: {string_id: "cfr-doorhanger-extension-cancel-button"},
action: {type: "CANCEL"},
}, {
label: {string_id: "cfr-doorhanger-extension-never-show-recommendation"},
}, {
label: {string_id: "cfr-doorhanger-extension-manage-settings-button"},
action: {
type: "OPEN_PREFERENCES_PAGE",
data: {category: "general-cfr", origin: "CFR"},
},
}],
},
},
frequency: {lifetime: 1},
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr in ["one_per_day", "nightly"]) &&
(xpinstallEnabled == true) &&
(${JSON.stringify(FACEBOOK_CONTAINER_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(FACEBOOK_CONTAINER_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${FACEBOOK_CONTAINER_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: FACEBOOK_CONTAINER_PARAMS.open_urls},
},
{
id: "FACEBOOK_CONTAINER_3",
template: "cfr_doorhanger",
@ -85,11 +137,64 @@ const CFR_MESSAGES = [
frequency: {lifetime: 3},
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr == "three_per_day") &&
(xpinstallEnabled == true) &&
(${JSON.stringify(FACEBOOK_CONTAINER_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(FACEBOOK_CONTAINER_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${FACEBOOK_CONTAINER_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: FACEBOOK_CONTAINER_PARAMS.open_urls},
},
{
id: "GOOGLE_TRANSLATE_1",
template: "cfr_doorhanger",
content: {
bucket_id: "CFR_M1",
notification_text: {string_id: "cfr-doorhanger-extension-notification"},
heading_text: {string_id: "cfr-doorhanger-extension-heading"},
info_icon: {
label: {string_id: "cfr-doorhanger-extension-sumo-link"},
sumo_path: GOOGLE_TRANSLATE_PARAMS.sumo_path,
},
addon: {
id: "445852",
title: "To Google Translate",
icon: "resource://activity-stream/data/content/assets/cfr_google_translate.png",
rating: 4.1,
users: 313474,
author: "Juan Escobar",
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/to-google-translate/",
},
text: "Instantly translate any webpage text. Simply highlight the text, right-click to open the context menu, and choose a text or aural translation.",
buttons: {
primary: {
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: null},
},
},
secondary: [{
label: {string_id: "cfr-doorhanger-extension-cancel-button"},
action: {type: "CANCEL"},
}, {
label: {string_id: "cfr-doorhanger-extension-never-show-recommendation"},
}, {
label: {string_id: "cfr-doorhanger-extension-manage-settings-button"},
action: {
type: "OPEN_PREFERENCES_PAGE",
data: {category: "general-cfr", origin: "CFR"},
},
}],
},
},
frequency: {lifetime: 1},
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr in ["one_per_day", "nightly"]) &&
(xpinstallEnabled == true) &&
(${JSON.stringify(GOOGLE_TRANSLATE_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(GOOGLE_TRANSLATE_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${GOOGLE_TRANSLATE_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: GOOGLE_TRANSLATE_PARAMS.open_urls},
},
{
id: "GOOGLE_TRANSLATE_3",
template: "cfr_doorhanger",
@ -136,11 +241,64 @@ const CFR_MESSAGES = [
frequency: {lifetime: 3},
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr == "three_per_day") &&
(xpinstallEnabled == true) &&
(${JSON.stringify(GOOGLE_TRANSLATE_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(GOOGLE_TRANSLATE_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${GOOGLE_TRANSLATE_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: GOOGLE_TRANSLATE_PARAMS.open_urls},
},
{
id: "YOUTUBE_ENHANCE_1",
template: "cfr_doorhanger",
content: {
bucket_id: "CFR_M1",
notification_text: {string_id: "cfr-doorhanger-extension-notification"},
heading_text: {string_id: "cfr-doorhanger-extension-heading"},
info_icon: {
label: {string_id: "cfr-doorhanger-extension-sumo-link"},
sumo_path: YOUTUBE_ENHANCE_PARAMS.sumo_path,
},
addon: {
id: "700308",
title: "Enhancer for YouTube\u2122",
icon: "resource://activity-stream/data/content/assets/cfr_enhancer_youtube.png",
rating: 4.8,
users: 357328,
author: "Maxime RF",
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/enhancer-for-youtube/",
},
text: "Take control of your YouTube experience. Automatically block annoying ads, set playback speed and volume, remove annotations, and more.",
buttons: {
primary: {
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: null},
},
},
secondary: [{
label: {string_id: "cfr-doorhanger-extension-cancel-button"},
action: {type: "CANCEL"},
}, {
label: {string_id: "cfr-doorhanger-extension-never-show-recommendation"},
}, {
label: {string_id: "cfr-doorhanger-extension-manage-settings-button"},
action: {
type: "OPEN_PREFERENCES_PAGE",
data: {category: "general-cfr", origin: "CFR"},
},
}],
},
},
frequency: {lifetime: 1},
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr in ["one_per_day", "nightly"]) &&
(xpinstallEnabled == true) &&
(${JSON.stringify(YOUTUBE_ENHANCE_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(YOUTUBE_ENHANCE_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${YOUTUBE_ENHANCE_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: YOUTUBE_ENHANCE_PARAMS.open_urls},
},
{
id: "YOUTUBE_ENHANCE_3",
template: "cfr_doorhanger",
@ -187,11 +345,64 @@ const CFR_MESSAGES = [
frequency: {lifetime: 3},
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr == "three_per_day") &&
(xpinstallEnabled == true) &&
(${JSON.stringify(YOUTUBE_ENHANCE_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(YOUTUBE_ENHANCE_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${YOUTUBE_ENHANCE_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: YOUTUBE_ENHANCE_PARAMS.open_urls},
},
{
id: "WIKIPEDIA_CONTEXT_MENU_SEARCH_1",
template: "cfr_doorhanger",
content: {
bucket_id: "CFR_M1",
notification_text: {string_id: "cfr-doorhanger-extension-notification"},
heading_text: {string_id: "cfr-doorhanger-extension-heading"},
info_icon: {
label: {string_id: "cfr-doorhanger-extension-sumo-link"},
sumo_path: WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.sumo_path,
},
addon: {
id: "659026",
title: "Wikipedia Context Menu Search",
icon: "resource://activity-stream/data/content/assets/cfr_wiki_search.png",
rating: 4.9,
users: 3095,
author: "Nick Diedrich",
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/wikipedia-context-menu-search/",
},
text: "Get to a Wikipedia page fast, from anywhere on the web. Just highlight any webpage text and right-click to open the context menu to start a Wikipedia search.",
buttons: {
primary: {
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: null},
},
},
secondary: [{
label: {string_id: "cfr-doorhanger-extension-cancel-button"},
action: {type: "CANCEL"},
}, {
label: {string_id: "cfr-doorhanger-extension-never-show-recommendation"},
}, {
label: {string_id: "cfr-doorhanger-extension-manage-settings-button"},
action: {
type: "OPEN_PREFERENCES_PAGE",
data: {category: "general-cfr", origin: "CFR"},
},
}],
},
},
frequency: {lifetime: 1},
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr in ["one_per_day", "nightly"]) &&
(xpinstallEnabled == true) &&
(${JSON.stringify(WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.open_urls},
},
{
id: "WIKIPEDIA_CONTEXT_MENU_SEARCH_3",
template: "cfr_doorhanger",
@ -238,11 +449,64 @@ const CFR_MESSAGES = [
frequency: {lifetime: 3},
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr == "three_per_day") &&
(xpinstallEnabled == true) &&
(${JSON.stringify(WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.open_urls},
},
{
id: "REDDIT_ENHANCEMENT_1",
template: "cfr_doorhanger",
content: {
bucket_id: "CFR_M1",
notification_text: {string_id: "cfr-doorhanger-extension-notification"},
heading_text: {string_id: "cfr-doorhanger-extension-heading"},
info_icon: {
label: {string_id: "cfr-doorhanger-extension-sumo-link"},
sumo_path: REDDIT_ENHANCEMENT_PARAMS.sumo_path,
},
addon: {
id: "387429",
title: "Reddit Enhancement Suite",
icon: "resource://activity-stream/data/content/assets/cfr_reddit_enhancement.png",
rating: 4.6,
users: 258129,
author: "honestbleeps",
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/reddit-enhancement-suite/",
},
text: "New features include Inline Image Viewer, Never Ending Reddit (never click 'next page' again), Keyboard Navigation, Account Switcher, and User Tagger.",
buttons: {
primary: {
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: null},
},
},
secondary: [{
label: {string_id: "cfr-doorhanger-extension-cancel-button"},
action: {type: "CANCEL"},
}, {
label: {string_id: "cfr-doorhanger-extension-never-show-recommendation"},
}, {
label: {string_id: "cfr-doorhanger-extension-manage-settings-button"},
action: {
type: "OPEN_PREFERENCES_PAGE",
data: {category: "general-cfr", origin: "CFR"},
},
}],
},
},
frequency: {lifetime: 1},
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr in ["one_per_day", "nightly"]) &&
(xpinstallEnabled == true) &&
(${JSON.stringify(REDDIT_ENHANCEMENT_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(REDDIT_ENHANCEMENT_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${REDDIT_ENHANCEMENT_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: REDDIT_ENHANCEMENT_PARAMS.open_urls},
},
{
id: "REDDIT_ENHANCEMENT_3",
template: "cfr_doorhanger",
@ -289,6 +553,7 @@ const CFR_MESSAGES = [
frequency: {lifetime: 3},
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr == "three_per_day") &&
(xpinstallEnabled == true) &&
(${JSON.stringify(REDDIT_ENHANCEMENT_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(REDDIT_ENHANCEMENT_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${REDDIT_ENHANCEMENT_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,

View File

@ -353,7 +353,6 @@ class PageAction {
callback: () => {
this._blockMessage(id);
this.hide();
this._sendTelemetry({message_id: id, bucket_id: content.bucket_id, event: "BLOCK"});
RecommendationMap.delete(browser);
},
}, {
@ -362,7 +361,6 @@ class PageAction {
callback: () => {
this.dispatchUserAction(secondary[2].action);
this.hide();
this._sendTelemetry({message_id: id, bucket_id: content.bucket_id, event: "MANAGE"});
RecommendationMap.delete(browser);
},
}];

View File

@ -3,15 +3,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
ChromeUtils.import("resource://gre/modules/Localization.jsm");
ChromeUtils.import("resource://gre/modules/FxAccountsConfig.jsm");
const L10N = new Localization([
"branding/brand.ftl",
"browser/branding/sync-brand.ftl",
"browser/newtab/onboarding.ftl",
]);
const ONBOARDING_MESSAGES = async () => ([
const ONBOARDING_MESSAGES = () => ([
{
id: "ONBOARDING_1",
template: "onboarding",
@ -58,7 +55,6 @@ const ONBOARDING_MESSAGES = async () => ([
data: {args: "addons"},
},
},
targeting: "attributionData.campaign != 'non-fx-button' && attributionData.source != 'addons.mozilla.org'",
trigger: {id: "firstRun"},
},
{
@ -79,24 +75,6 @@ const ONBOARDING_MESSAGES = async () => ([
targeting: "providerCohorts.onboarding == 'ghostery'",
trigger: {id: "firstRun"},
},
{
id: "ONBOARDING_5",
template: "onboarding",
bundled: 3,
order: 4,
content: {
title: {string_id: "onboarding-fxa-title"},
text: {string_id: "onboarding-fxa-text"},
icon: "sync",
button_label: {string_id: "onboarding-button-label-get-started"},
button_action: {
type: "OPEN_URL",
data: {args: await FxAccountsConfig.promiseEmailFirstURI("onboarding")},
},
},
targeting: "attributionData.campaign == 'non-fx-button' && attributionData.source == 'addons.mozilla.org'",
trigger: {id: "firstRun"},
},
]);
const OnboardingMessageProvider = {
@ -108,13 +86,12 @@ const OnboardingMessageProvider = {
return {header: header.value, button_label: button_label.value};
},
async getMessages() {
const messages = await this.translateMessages(await ONBOARDING_MESSAGES());
const messages = await this.translateMessages(ONBOARDING_MESSAGES());
return messages;
},
async getUntranslatedMessages() {
getUntranslatedMessages() {
// This is helpful for jsonSchema testing - since we are localizing in the provider
const messages = await ONBOARDING_MESSAGES();
return messages;
return ONBOARDING_MESSAGES();
},
async translateMessages(messages) {
let translatedMessages = [];

View File

@ -9,18 +9,6 @@ const MESSAGES = () => ([
{
"id": "SIMPLE_TEST_1",
"template": "simple_snippet",
"campaign": "test_campaign_blocking",
"content": {
"icon": TEST_ICON,
"text": "<syncLink>Sync it, link it, take it with you</syncLink>. All this and more with a Firefox Account.",
"links": {"syncLink": {"url": "https://www.mozilla.org/en-US/firefox/accounts"}},
"block_button_text": "Block",
},
},
{
"id": "SIMPLE_TEST_1_SAME_CAMPAIGN",
"template": "simple_snippet",
"campaign": "test_campaign_blocking",
"content": {
"icon": TEST_ICON,
"text": "<syncLink>Sync it, link it, take it with you</syncLink>. All this and more with a Firefox Account.",
@ -63,28 +51,6 @@ const MESSAGES = () => ([
"block_button_text": "Block",
},
},
{
"id": "NEWSLETTER_TEST_DEFAULTS",
"template": "newsletter_snippet",
"content": {
"scene1_icon": TEST_ICON,
"scene1_title": "Be a part of a movement.",
"scene1_title_icon": "",
"scene1_text": "Internet shutdowns, hackers, harassment &ndash; the health of the internet is on the line. Sign up and Mozilla will keep you updated on how you can help.",
"scene1_button_label": "Continue",
"scene1_button_color": "#712b00",
"scene1_button_background_color": "#ff9400",
"scene2_title": "Let's do this!",
"scene2_dismiss_button_text": "Dismiss",
"scene2_text": "Sign up for the Mozilla newsletter and we will keep you updated on how you can help.",
"scene2_privacy_html": "I'm okay with Mozilla handling my info as explained in this <privacyLink>Privacy Notice</privacyLink>.",
"scene2_newsletter": "mozilla-foundation",
"form_action": "https://basket.mozilla.org/subscribe.json",
"success_text": "Check your inbox for the confirmation!",
"error_text": "Error!",
"links": {"privacyLink": {"url": "https://www.mozilla.org/privacy/websites/?sample_rate=0.001&snippet_name=7894"}},
},
},
{
"id": "NEWSLETTER_TEST_1",
"template": "newsletter_snippet",
@ -96,17 +62,19 @@ const MESSAGES = () => ([
"scene1_button_label": "Continue",
"scene1_button_color": "#712b00",
"scene1_button_background_color": "#ff9400",
"scene2_title": "Let's do this!",
"locale": "en-CA",
"scene2_dismiss_button_text": "Dismiss",
"scene2_text": "Sign up for the Mozilla newsletter and we will keep you updated on how you can help.",
"scene2_privacy_html": "I'm okay with Mozilla handling my info as explained in this <privacyLink>Privacy Notice</privacyLink>.",
"scene2_button_label": "Sign Me up",
"scene2_email_placeholder_text": "Your email here",
"scene2_newsletter": "mozilla-foundation",
"form_action": "https://basket.mozilla.org/subscribe.json",
"success_text": "Check your inbox for the confirmation!",
"error_text": "Error!",
"hidden_inputs": {
"fmt": "H",
"lang": "en-US",
"newsletters": "mozilla-foundation",
},
"links": {"privacyLink": {"url": "https://www.mozilla.org/privacy/websites/?sample_rate=0.001&snippet_name=7894"}},
},
},
@ -155,7 +123,7 @@ const MESSAGES = () => ([
scene2_icon: TEST_ICON,
scene2_input_placeholder: "Your email address or phone number",
scene2_text: "Send Firefox to your phone and take a powerful independent browser with you.",
scene2_title: "Let's do this!",
scene2_title: "",
error_text: "Oops, there was a problem.",
success_title: "Your download link was sent.",

View File

@ -24,8 +24,6 @@ ChromeUtils.defineModuleGetter(this, "UpdateUtils",
"resource://gre/modules/UpdateUtils.jsm");
ChromeUtils.defineModuleGetter(this, "HomePage",
"resource:///modules/HomePage.jsm");
ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
"resource://gre/modules/ExtensionSettingsStore.jsm");
XPCOMUtils.defineLazyServiceGetters(this, {
gUUIDGenerator: ["@mozilla.org/uuid-generator;1", "nsIUUIDGenerator"],
@ -494,8 +492,7 @@ this.TelemetryFeed = class TelemetryFeed {
async sendPageTakeoverData() {
if (this.telemetryEnabled) {
const value = {};
let newtabAffected = false;
let homeAffected = false;
let page;
// Check whether or not about:home and about:newtab are set to a custom URL.
// If so, classify them.
@ -503,35 +500,14 @@ this.TelemetryFeed = class TelemetryFeed {
aboutNewTabService.overridden &&
!aboutNewTabService.newTabURL.startsWith("moz-extension://")) {
value.newtab_url_category = await this._classifySite(aboutNewTabService.newTabURL);
newtabAffected = true;
}
// Check if the newtab page setting is controlled by an extension.
await ExtensionSettingsStore.initialize();
const newtabExtensionInfo = ExtensionSettingsStore.getSetting("url_overrides", "newTabURL");
if (newtabExtensionInfo && newtabExtensionInfo.id) {
value.newtab_extension_id = newtabExtensionInfo.id;
newtabAffected = true;
page = "about:newtab";
}
const homePageURL = HomePage.get();
if (!["about:home", "about:blank"].includes(homePageURL) &&
!homePageURL.startsWith("moz-extension://")) {
value.home_url_category = await this._classifySite(homePageURL);
homeAffected = true;
}
const homeExtensionInfo = ExtensionSettingsStore.getSetting("prefs", "homepage_override");
if (homeExtensionInfo && homeExtensionInfo.id) {
value.home_extension_id = homeExtensionInfo.id;
homeAffected = true;
}
let page;
if (newtabAffected && homeAffected) {
page = "both";
} else if (newtabAffected) {
page = "about:newtab";
} else if (homeAffected) {
page = "about:home";
page = page ? "both" : "about:home";
}
if (page) {

View File

@ -127,85 +127,42 @@ topsites_form_title_label=শিরোনাম
topsites_form_title_placeholder=একটি শিরোনাম লিখুন
topsites_form_url_label=URL
topsites_form_image_url_label=ছবির কাস্টম URL
topsites_form_url_placeholder=একটি URL লিখুন বা পেস্ট করুন
topsites_form_use_image_link=স্বনির্ধারিত ছবি ব্যবহার করুন…
# LOCALIZATION NOTE (topsites_form_*_button): These are verbs/actions.
topsites_form_preview_button=প্রাকদর্শন
topsites_form_add_button=যোগ করুন
topsites_form_save_button=সংরক্ষণ করুন
topsites_form_cancel_button=বাতিল করুন
topsites_form_url_validation=বৈধ URL প্রয়োজন
topsites_form_image_validation=ছবি লোড করতে ব্যর্থ। ভিন্ন URL এ চেস্টা করুন।
# LOCALIZATION NOTE (pocket_read_more): This is shown at the bottom of the
# trending stories section and precedes a list of links to popular topics.
pocket_read_more=জনপ্রিয় বিষয়গুলি:
# LOCALIZATION NOTE (pocket_read_even_more): This is shown as a link at the
# end of the list of popular topic links.
pocket_read_even_more=আরো খবর দেখুন
pocket_more_reccommendations=আরো সুপারিশ
pocket_learn_more=আরো জানুন
pocket_how_it_works=এটি কিভাবে কাজ করে
pocket_cta_button=Pocket পান
pocket_cta_text=আপনার পছন্দের গল্পগুলো Pocket এ সংরক্ষণ করুন, এবং আকর্ষণীয় মনে পড়ুন।
highlights_empty_state=ব্রাউজিং শুরু করুন, এবং আমরা কিছু মহান নিবন্ধ, ভিডিও, এবং আপনার সম্প্রতি প্রদর্শিত পৃষ্ঠা বা বুকমার্ক এখানে প্রদর্শিত হবে।
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
# in the space that would have shown a few stories, this is shown instead.
# {provider} is replaced by the name of the content provider for this section.
topstories_empty_state=আপনি ধরা পড়েছেন। আরো শীর্ষ গল্পের জন্য পরে আবার {provider} এর থেকে চেক করুন। অপেক্ষা করতে পারছেন না? ওয়েব থেকে আরো মহান গল্প খুঁজে পেতে একটি জনপ্রিয় বিষয় নির্বাচন করুন।
# LOCALIZATION NOTE (manual_migration_explanation2): This message is shown to encourage users to
# import their browser profile from another browser they might be using.
manual_migration_explanation2=অন্য ব্রাউজার থেকে বুকমার্ক, ইতিহাস এবং পাসওয়ার্ডগুলির সাথে Firefox ব্যবহার করে দেখুন।
# LOCALIZATION NOTE (manual_migration_cancel_button): This message is shown on a button that cancels the
# process of importing another browsers profile into Firefox.
manual_migration_cancel_button=না, ধন্যবাদ
# LOCALIZATION NOTE (manual_migration_import_button): This message is shown on a button that starts the process
# of importing another browsers profile profile into Firefox.
manual_migration_import_button=এখন আমদানি করুন
# LOCALIZATION NOTE (error_fallback_default_*): This message and suggested
# action link are shown in each section of UI that fails to render
error_fallback_default_info=ওহো, বিষয়বস্তুটি লোড করতে কিছু ভুল হয়েছে।
error_fallback_default_refresh_suggestion=পুনরায় চেস্টা করার জন্য পেজটি রিফ্রেশ করুন।
# LOCALIZATION NOTE (section_menu_action_*). These strings are displayed in the section
# context menu and are meant as a call to action for the given section.
section_menu_action_remove_section=বিভাগটিকে সরান
section_menu_action_collapse_section=সেকশনটি সংকোচন করুন
section_menu_action_expand_section=বিভাগটি প্রসারিত করুন
section_menu_action_manage_section=বিভাগটি পরিচালনা করুন
section_menu_action_manage_webext=এক্সটেনশনটি পরিচালনা করুন
section_menu_action_add_topsite=উপরে সাইট যোগ করুন
section_menu_action_add_search_engine=অনুসন্ধানের ইঞ্জিন যোগ করুন
section_menu_action_move_up=উপরে স্থানান্তর
section_menu_action_move_down=নীচে স্থানান্তর
section_menu_action_privacy_notice=গোপনীয়তা সংক্রান্ত নীতি
# LOCALIZATION NOTE (firstrun_*). These strings are displayed only once, on the
# firstrun of the browser, they give an introduction to Firefox and Sync.
firstrun_title=Firefox কে আপনার সঙ্গে নিন
firstrun_content=আপনার সমস্ত ডিভাইসে আপনার বুকমার্ক, ইতিহাস, পাসওয়ার্ড এবং অন্যান্য সেটিংস পান।
firstrun_learn_more_link=Firefox অ্যাকাউন্টগুলি সম্বন্ধে আরও জানুন
# LOCALIZATION NOTE (firstrun_form_header and firstrun_form_sub_header):
# firstrun_form_sub_header is a continuation of firstrun_form_header, they are one sentence.
# firstrun_form_header is displayed more boldly as the call to action.
firstrun_form_header=আপনার ইমেইল দিন
firstrun_form_sub_header=Firefox সিঙ্কের সাথে অবিরত করুন
firstrun_email_input_placeholder=ইমেইল
firstrun_invalid_input=বৈধ ইমেইল প্রয়োজন
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
firstrun_extra_legal_links=এগিয়ে যাওয়ার জন্য আপনি {terms} এবং {privacy} সম্মত হন।
firstrun_terms_of_service=পরিষেবার শর্তাদি
firstrun_privacy_notice=গোপনীয়তা সংক্রান্ত নীতি
firstrun_continue_to_login=এগিয়ে চলুন
firstrun_skip_login=ধাপটি উপেক্ষা করুন
# LOCALIZATION NOTE (context_menu_title): Action tooltip to open a context menu
context_menu_title=মেনু খুলুন
section_menu_action_add_search_engine=অনুসন্ধান ইঞ্জিন যোগ করুন

View File

@ -143,6 +143,7 @@ pocket_read_more=Nima'q taq Na'oj:
# LOCALIZATION NOTE (pocket_read_even_more): This is shown as a link at the
# end of the list of popular topic links.
pocket_read_even_more=Ketz'et ch'aqa' chik taq B'anob'äl
pocket_more_reccommendations=Ch'aqa' chik taq Chilab'enïk
pocket_learn_more=Tetamäx Ch'aqa' Chik
pocket_cta_button=Tik'ul Pocket
@ -195,6 +196,7 @@ firstrun_form_header=Tatz'ib'aj ri ataqoya'l
firstrun_form_sub_header=richin yatok pa Firefox Sync.
firstrun_email_input_placeholder=Taqoya'l
firstrun_invalid_input=Najowäx ütz chi taqoya'l
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
@ -205,6 +207,3 @@ firstrun_privacy_notice=Ichinan Na'oj
firstrun_continue_to_login=Titikïr chik el
firstrun_skip_login=Tixakalüx re jun ruxak re'
# LOCALIZATION NOTE (context_menu_title): Action tooltip to open a context menu
context_menu_title=Tijaq k'utüy samaj

View File

@ -143,6 +143,7 @@ pocket_read_more=Temas populares:
# LOCALIZATION NOTE (pocket_read_even_more): This is shown as a link at the
# end of the list of popular topic links.
pocket_read_even_more=Ver más historias
pocket_more_reccommendations=Más recomendaciones
pocket_learn_more=Aprender más
pocket_how_it_works=Cómo funciona
@ -196,6 +197,7 @@ firstrun_form_header=Ingresa tu correo
firstrun_form_sub_header=para conectarte a Firefox Sync.
firstrun_email_input_placeholder=Correo
firstrun_invalid_input=Se requiere un correo válido
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
@ -206,6 +208,3 @@ firstrun_privacy_notice=Política de privacidad
firstrun_continue_to_login=Continuar
firstrun_skip_login=Saltar este paso
# LOCALIZATION NOTE (context_menu_title): Action tooltip to open a context menu
context_menu_title=Abrir menú

View File

@ -143,6 +143,7 @@ pocket_read_more=Populaarsed teemad:
# LOCALIZATION NOTE (pocket_read_even_more): This is shown as a link at the
# end of the list of popular topic links.
pocket_read_even_more=Rohkem lugusid
pocket_more_reccommendations=Rohkem soovitusi
pocket_learn_more=Rohkem teavet
pocket_how_it_works=Kuidas see töötab?
@ -196,6 +197,7 @@ firstrun_form_header=Sisesta enda e-posti aadress
firstrun_form_sub_header=Firefox Synciga jätkamiseks
firstrun_email_input_placeholder=E-post
firstrun_invalid_input=E-posti aadress peab olema korrektne
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
@ -206,6 +208,3 @@ firstrun_privacy_notice=privaatsusreeglitega
firstrun_continue_to_login=Jätka
firstrun_skip_login=Jäta see samm vahele
# LOCALIZATION NOTE (context_menu_title): Action tooltip to open a context menu
context_menu_title=Ava menüü

View File

@ -206,6 +206,3 @@ firstrun_privacy_notice=نکات حریم‌خصوصی
firstrun_continue_to_login=ادامه
firstrun_skip_login=پرش از این مرحله
# LOCALIZATION NOTE (context_menu_title): Action tooltip to open a context menu
context_menu_title=باز کردن منو

View File

@ -123,9 +123,7 @@ topsites_form_edit_header=Uredi najbolju stranicu
topsites_form_title_label=Naslov
topsites_form_title_placeholder=Unesi naslov
topsites_form_url_label=URL
topsites_form_image_url_label=Prilagođeni URL slike
topsites_form_url_placeholder=Utipkajte ili zalijepite URL
topsites_form_use_image_link=Koristi prilagođenu sliku…
# LOCALIZATION NOTE (topsites_form_*_button): These are verbs/actions.
topsites_form_preview_button=Pregled
topsites_form_add_button=Dodaj
@ -142,8 +140,6 @@ pocket_read_more=Popularne teme:
pocket_read_even_more=Prikaži više priča
pocket_more_reccommendations=Više preporuka
pocket_learn_more=Saznajte više
pocket_how_it_works=Kako ovo funkcionira
pocket_cta_button=Nabavite Pocket
highlights_empty_state=Započnite pretraživati i pokazat ćemo vam neke od izvrsnih članaka, videa i drugih web stranica prema vašim nedavno posjećenim stranicama ili zabilješkama.
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
@ -171,7 +167,6 @@ section_menu_action_remove_section=Ukloni odjel
section_menu_action_collapse_section=Skupi odjel
section_menu_action_expand_section=Proširi odjel
section_menu_action_manage_section=Upravljanje odjelom
section_menu_action_manage_webext=Upravljanje dodatkom
section_menu_action_add_topsite=Dodaj najbolju stranicu
section_menu_action_add_search_engine=Dodaj tražilicu
section_menu_action_move_up=Pomakni gore
@ -181,7 +176,6 @@ section_menu_action_privacy_notice=Politika privatnosti
# LOCALIZATION NOTE (firstrun_*). These strings are displayed only once, on the
# firstrun of the browser, they give an introduction to Firefox and Sync.
firstrun_title=Uzmite Firefox sa sobom
firstrun_content=Preuzmite svoje zabilješke, povijest, lozinke i druge postavke na sve vaše uređaje.
firstrun_learn_more_link=Saznajte više o Firefox računima
# LOCALIZATION NOTE (firstrun_form_header and firstrun_form_sub_header):
@ -191,16 +185,13 @@ firstrun_form_header=Unesite vašu adresu e-pošte
firstrun_form_sub_header=i prijavi se u Firefox Sync
firstrun_email_input_placeholder=E-pošta
firstrun_invalid_input=Potrebna je ispravna adresa e-pošte
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
firstrun_extra_legal_links=Nastavljanjem pristajete na {terms} i {privacy}.
firstrun_terms_of_service=Uvjete korištenja
firstrun_privacy_notice=Politiku privatnosti
firstrun_terms_of_service=Uvjeti korištenja
firstrun_privacy_notice=Politika privatnosti
firstrun_continue_to_login=Nastavi
firstrun_skip_login=Preskočite ovaj korak
# LOCALIZATION NOTE (context_menu_title): Action tooltip to open a context menu
context_menu_title=Otvori izbornik

View File

@ -143,9 +143,9 @@ pocket_read_more=Subjectos popular:
# LOCALIZATION NOTE (pocket_read_even_more): This is shown as a link at the
# end of the list of popular topic links.
pocket_read_even_more=Vider plus historias
pocket_more_reccommendations=Altere recommendationes
pocket_learn_more=Saper plus
pocket_how_it_works=Como illo labora
pocket_cta_button=Installa Pocket
pocket_cta_text=Salvar le chronologias que tu ama in Pocket, e alimenta tu mente con lecturas fascinante.
@ -196,6 +196,7 @@ firstrun_form_header=Insere tu email
firstrun_form_sub_header=pro continuar con Firefox Sync.
firstrun_email_input_placeholder=Email
firstrun_invalid_input=Il es necesse un valide adresse email
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
@ -206,6 +207,3 @@ firstrun_privacy_notice=Notification de confidentialitate
firstrun_continue_to_login=Continuar
firstrun_skip_login=Saltar iste grado
# LOCALIZATION NOTE (context_menu_title): Action tooltip to open a context menu
context_menu_title=Aperir le menu

View File

@ -19,8 +19,6 @@ header_recommended_by={provider} ರಿಂದ ಶಿಫಾರಸುಮಾಡು
type_label_visited=ಭೇಟಿ ನೀಡಲಾದ‍
type_label_bookmarked=ಪುಟಗುರುತು ಮಾಡಲಾದ
type_label_recommended=ಪ್ರಚಲಿತ
type_label_pocket=ಪಾಕೆಟ್‌ನಲ್ಲಿ ಉಳಿಸಲಾಗಿದೆ
type_label_downloaded=ಡೌನ್ಲೋಡ್ ಮಾಡಲಾಗಿದೆ
# LOCALIZATION NOTE (menu_action_*): These strings are displayed in a context
# menu and are meant as a call to action for a given page.
@ -39,16 +37,11 @@ menu_action_unpin=ಅನ್‌ಪಿನ್
# page from history.
confirm_history_delete_notice_p2=ಈ ಕಾರ್ಯವನ್ನು ರದ್ದುಗೊಳಿಸಲು ಸಾಧ್ಯವಿರುವುದಿಲ್ಲ.
menu_action_save_to_pocket=ಪಾಕೆಟ್‌ನಲ್ಲಿ ಉಳಿಸಿ‍
menu_action_delete_pocket=ಪಾಕೆಟ್ನಿಂದ ಅಳಿಸಿ
menu_action_archive_pocket=ಪಾಕೆಟ್ನಲ್ಲಿ ಆರ್ಕೈವ್ ಮಾಡಿ
# LOCALIZATION NOTE (menu_action_show_file_*): These are platform specific strings
# found in the context menu of an item that has been downloaded. The intention behind
# "this action" is that it will show where the downloaded file exists on the file system
# for each operating system.
menu_action_show_file_mac_os=ಶೋಧಕದಲ್ಲಿ ತೋರಿಸು
menu_action_show_file_windows=ಹೊಂದಿರುವ ಕಡತಕೋಶವನ್ನು ತೆರೆ
menu_action_show_file_linux=ಹೊಂದಿರುವ ಕಡತಕೋಶವನ್ನು ತೆರೆ
menu_action_show_file_default=ಕಡತ ತೋರಿಸು
menu_action_open_file=ಕಡತವನ್ನು ತೆರೆ
@ -77,22 +70,16 @@ search_web_placeholder=ಅಂತರ್ಜಾಲವನ್ನು ಹುಡುಕ
# how the stories are selected.
# LOCALIZATION NOTE (section_disclaimer_topstories_buttontext): The text of
# the button used to acknowledge, and hide this disclaimer in the future.
section_disclaimer_topstories_buttontext=ಸರಿ, ಗೊತ್ತಾಯಿತು
# LOCALIZATION NOTE (prefs_*, settings_*): These are shown in about:preferences
# for a "Firefox Home" section. "Firefox" should be treated as a brand and kept
# in English, while "Home" should be localized matching the about:preferences
# sidebar mozilla-central string for the panel that has preferences related to
# what is shown for the homepage, new windows, and new tabs.
prefs_home_header=ಫೈರ್ಫಾಕ್ಸ್ ಮುಖಪುಟದ ವಿಷಯ
# LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
# plural forms used in a drop down of multiple row options (1 row, 2 rows).
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
prefs_search_header=ಜಾಲದ ಹುಡುಕಾಟ
prefs_topstories_sponsored_learn_more=ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ
prefs_highlights_options_visited_label=ಭೇಟಿಕೊಟ್ಟ ಪುಟಗಳು
prefs_highlights_options_download_label=ತೀರಾ ಇತ್ತೀಚಿನ ಡೌನ್ಲೋಡ್
prefs_highlights_options_pocket_label=ಪಾಕೆಟ್ಗೆ ಉಳಿಸಲಾದ ಪುಟಗಳು
settings_pane_button_label=ಹೊಸ ಹಾಳೆಯ ಪುಟವನ್ನು ಅಗತ್ಯಾನುಗುಣಗೊಳಿಸಿ
settings_pane_topsites_header=ಪ್ರಮುಖ ತಾಣಗಳು
settings_pane_highlights_header=ಮುಖ್ಯಾಂಶಗಳು
@ -157,7 +144,6 @@ section_menu_action_expand_section=ವಿಭಾಗ ವಿಸ್ತರಿಸಿ
section_menu_action_manage_section=ವಿಭಾಗವನ್ನು ನಿರ್ವಹಿಸಿ
section_menu_action_manage_webext=ವಿಸ್ತರಣೆಯನ್ನು ನಿರ್ವಹಿಸಿ
section_menu_action_move_up=ಮೇಲೆ ಜರುಗಿಸು
section_menu_action_move_down=ಕೆಳಗೆ ಜರುಗಿಸು
# LOCALIZATION NOTE (firstrun_*). These strings are displayed only once, on the
# firstrun of the browser, they give an introduction to Firefox and Sync.

View File

@ -206,6 +206,3 @@ firstrun_privacy_notice=Privātuma politikai
firstrun_continue_to_login=Turpināt
firstrun_skip_login=Izlaist šo soli
# LOCALIZATION NOTE (context_menu_title): Action tooltip to open a context menu
context_menu_title=Atvērt izvēlni

View File

@ -142,10 +142,6 @@ pocket_read_more=ਪ੍ਰਸਿੱਧ ਵਿਸ਼ੇ:
# LOCALIZATION NOTE (pocket_read_even_more): This is shown as a link at the
# end of the list of popular topic links.
pocket_read_even_more=ਹੋਰ ਕਹਾਣੀਆਂ ਵੇਖੋ
pocket_more_reccommendations=ਹੋਰ ਸਿਫਾਰਸ਼ਾਂ
pocket_learn_more=ਹੋਰ ਸਿੱਖੋ
pocket_how_it_works=ਇਹ ਕਿਵੇਂ ਕੰਮ ਕਰਦੀ ਹੈ
pocket_cta_button=ਪਾਕੇਟ ਲਵੋ
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
# in the space that would have shown a few stories, this is shown instead.
@ -173,7 +169,6 @@ section_menu_action_expand_section=ਸੈਕਸ਼ਨ ਦੀ ਫੈਲਾਓ
section_menu_action_manage_section=ਸੈਕਸ਼ਨ ਦਾ ਪ੍ਰਬੰਧ ਕਰੋ
section_menu_action_manage_webext=ਇਕਸਟੈਨਸ਼ਨਾਂ ਦਾ ਇੰਤਜ਼ਾਮ
section_menu_action_add_topsite=ਚੋਟੀ ਦੀਆਂ ਸਾਈਟਾਂ ਜੋੜੋ
section_menu_action_add_search_engine=ਖੋਜ ਇੰਜਣ ਜੋੜੋ
section_menu_action_move_up=ਉੱਤੇ ਭੇਜੋ
section_menu_action_move_down=ਹੇਠਾਂ ਭੇਜੋ
section_menu_action_privacy_notice=ਨਿੱਜੀ ਨੋਟਿਸ
@ -191,7 +186,6 @@ firstrun_form_header=ਆਪਣਾ ਈਮੇਲ ਦਿਓ
firstrun_form_sub_header=ਤਾਂ ਕਿ ਫਾਇਰਫਾਕਸ ਸਿੰਕ ਨਾਲ ਜਾਰੀ ਰੱਖਿਆ ਜਾਵੇ।
firstrun_email_input_placeholder=ਈਮੇਲ
firstrun_invalid_input=ਢੁੱਕਵੀਂ ਈਮੇਲ ਚਾਹੀਦੀ ਹੈ
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
@ -201,6 +195,4 @@ firstrun_privacy_notice=ਪਰਦੇਦਾਰੀ ਦਾ ਨੋਟਿਸ
firstrun_continue_to_login=ਜਾਰੀ ਰੱਖੋ
firstrun_skip_login=ਇਹ ਪਗ਼ ਛੱਡੋ
# LOCALIZATION NOTE (context_menu_title): Action tooltip to open a context menu
context_menu_title=ਮੇਨੂ ਖੋਲ੍ਹੋ
section_menu_action_add_search_engine=ਖੋਜ ਇੰਜਣ ਜੋੜੋ

View File

@ -30,8 +30,8 @@ type_label_downloaded=Transferido
# bookmarks"
menu_action_bookmark=Adicionar aos marcadores
menu_action_remove_bookmark=Remover marcador
menu_action_open_new_window=Abrir numa nova janela
menu_action_open_private_window=Abrir numa nova janela privada
menu_action_open_new_window=Abrir em nova janela
menu_action_open_private_window=Abrir em nova janela privada
menu_action_dismiss=Dispensar
menu_action_delete=Apagar do histórico
menu_action_pin=Afixar

File diff suppressed because one or more lines are too long

View File

@ -74,10 +74,10 @@ window.gActivityStreamStrings = {
"topsites_form_image_validation": "ছবি লোড করতে ব্যর্থ। ভিন্ন URL এ চেস্টা করুন।",
"pocket_read_more": "জনপ্রিয় বিষয়:",
"pocket_read_even_more": "আরও গল্প দেখুন",
"pocket_more_reccommendations": "আরো সুপারিশ",
"pocket_more_reccommendations": "More Recommendations",
"pocket_how_it_works": "কিভাবে এটা কাজ করে",
"pocket_cta_button": "Pocket পান",
"pocket_cta_text": "আপনার পছন্দের গল্পগুলো Pocket এ সংরক্ষণ করুন, এবং আকর্ষণীয় মনে পড়ুন।",
"pocket_cta_button": "Get Pocket",
"pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
"highlights_empty_state": "ব্রাউজি করা শুরু করুন, এবং কিছু গুরুত্বপূর্ণ নিবন্ধ, ভিডিও, এবং আপনি সম্প্রতি পরিদর্শন বা বুকমার্ক করেছেন এমন কিছু পৃষ্ঠা আমরা এখানে প্রদর্শন করব।",
"topstories_empty_state": "কিছু একটা ঠিক নেই। {provider} এর শীর্ষ গল্পগুলো পেতে কিছুক্ষণ পর আবার দেখুন। অপেক্ষা করতে চান না? বিশ্বের সেরা গল্পগুলো পেতে কোন জনপ্রিয় বিষয় নির্বাচন করুন।",
"manual_migration_explanation2": "অন্য ব্রাউজার থেকে আনা বুকমার্ক, ইতিহাস এবং পাসওয়ার্ডগুলির সাথে ফায়ারফক্স ব্যবহার করে দেখুন।",
@ -107,6 +107,6 @@ window.gActivityStreamStrings = {
"firstrun_privacy_notice": "গোপনীয়তা নীতি",
"firstrun_continue_to_login": "চালিয়ে যান",
"firstrun_skip_login": "এই ধাপটি বাদ দিন",
"context_menu_title": "মেনু খুলুন",
"context_menu_title": "Open menu",
"pocket_learn_more": "আরও জানুন"
};

File diff suppressed because one or more lines are too long

View File

@ -64,49 +64,49 @@ window.gActivityStreamStrings = {
"topsites_form_title_placeholder": "একটি শিরোনাম লিখুন",
"topsites_form_url_label": "URL",
"topsites_form_image_url_label": "ছবির কাস্টম URL",
"topsites_form_url_placeholder": "একটি URL লিখুন বা পেস্ট করুন",
"topsites_form_use_image_link": "স্বনির্ধারিত ছবি ব্যবহার করুন…",
"topsites_form_url_placeholder": "টাইপ করুন অথবা পেস্ট করুন URL",
"topsites_form_use_image_link": "কাস্টম ছবি ব্যবহার করুন…",
"topsites_form_preview_button": "প্রাকদর্শন",
"topsites_form_add_button": "যোগ করুন",
"topsites_form_save_button": "সংরক্ষণ করুন",
"topsites_form_add_button": "যোগ",
"topsites_form_save_button": "সংরক্ষণ",
"topsites_form_cancel_button": "বাতিল করুন",
"topsites_form_url_validation": "বৈধ URL প্রয়োজন",
"topsites_form_url_validation": "কার্যকর URL প্রয়োজন",
"topsites_form_image_validation": "ছবি লোড করতে ব্যর্থ। ভিন্ন URL এ চেস্টা করুন।",
"pocket_read_more": "জনপ্রিয় বিষয়গুলি:",
"pocket_read_even_more": "আরো খবর দেখুন",
"pocket_more_reccommendations": "আরো সুপারিশ",
"pocket_how_it_works": "এটি কিভাবে কাজ করে",
"pocket_cta_button": "Pocket পান",
"pocket_cta_text": "আপনার পছন্দের গল্পগুলো Pocket এ সংরক্ষণ করুন, এবং আকর্ষণীয় মনে পড়ুন।",
"highlights_empty_state": "ব্রাউজিং শুরু করুন, এবং আমরা কিছু মহান নিবন্ধ, ভিডিও, এবং আপনার সম্প্রতি প্রদর্শিত পৃষ্ঠা বা বুকমার্ক এখানে প্রদর্শিত হবে।",
"topstories_empty_state": "আপনি ধরা পড়েছেন। আরো শীর্ষ গল্পের জন্য পরে আবার {provider} এর থেকে চেক করুন। অপেক্ষা করতে পারছেন না? ওয়েব থেকে আরো মহান গল্প খুঁজে পেতে একটি জনপ্রিয় বিষয় নির্বাচন করুন।",
"manual_migration_explanation2": "অন্য ব্রাউজার থেকে বুকমার্ক, ইতিহাস এবং পাসওয়ার্ডগুলির সাথে Firefox ব্যবহার করে দেখুন।",
"manual_migration_cancel_button": "না, ধন্যবাদ",
"manual_migration_import_button": "এখন আমদানি করুন",
"error_fallback_default_info": "ওহো, বিষয়বস্তুটি লোড করতে কিছু ভুল হয়েছে।",
"pocket_read_more": "জনপ্রিয় বিষয়:",
"pocket_read_even_more": "আরও গল্প দেখুন",
"pocket_more_reccommendations": "More Recommendations",
"pocket_how_it_works": "কিভাবে এটা কাজ করে",
"pocket_cta_button": "Get Pocket",
"pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
"highlights_empty_state": "ব্রাউজি করা শুরু করুন, এবং কিছু গুরুত্বপূর্ণ নিবন্ধ, ভিডিও, এবং আপনি সম্প্রতি পরিদর্শন বা বুকমার্ক করেছেন এমন কিছু পৃষ্ঠা আমরা এখানে প্রদর্শন করব।",
"topstories_empty_state": "কিছু একটা ঠিক নেই। {provider} এর শীর্ষ গল্পগুলো পেতে কিছুক্ষণ পর আবার দেখুন। অপেক্ষা করতে চান না? বিশ্বের সেরা গল্পগুলো পেতে কোন জনপ্রিয় বিষয় নির্বাচন করুন।",
"manual_migration_explanation2": "অন্য ব্রাউজার থেকে আনা বুকমার্ক, ইতিহাস এবং পাসওয়ার্ডগুলির সাথে ফায়ারফক্স ব্যবহার করে দেখুন।",
"manual_migration_cancel_button": "প্রয়োজন নেই",
"manual_migration_import_button": "এখনই ইম্পোর্ট করুন",
"error_fallback_default_info": "ওহো, কনটেন্টটি লোড করতে কিছু ভুল হয়েছে।",
"error_fallback_default_refresh_suggestion": "পুনরায় চেস্টা করার জন্য পেজটি রিফ্রেশ করুন।",
"section_menu_action_remove_section": "বিভাগটিকে সরান",
"section_menu_action_remove_section": "সেকশনটি সরান",
"section_menu_action_collapse_section": "সেকশনটি সংকোচন করুন",
"section_menu_action_expand_section": "বিভাগটি প্রসারিত করুন",
"section_menu_action_manage_section": "বিভাগটি পরিচালনা করুন",
"section_menu_action_manage_webext": "এক্সটেনশনটি পরিচালনা করুন",
"section_menu_action_add_topsite": "উপরে সাইট যোগ করুন",
"section_menu_action_add_search_engine": "অনুসন্ধানের ইঞ্জিন যোগ করুন",
"section_menu_action_move_up": "উপরে স্থানান্তর",
"section_menu_action_move_down": "নীচে স্থানান্তর",
"section_menu_action_privacy_notice": "গোপনীয়তা সংক্রান্ত নীতি",
"firstrun_title": "Firefox কে আপনার সঙ্গে নিন",
"firstrun_content": "আপনার সমস্ত ডিভাইসে আপনার বুকমার্ক, ইতিহাস, পাসওয়ার্ড এবং অন্যান্য সেটিংস পান।",
"firstrun_learn_more_link": "Firefox অ্যাকাউন্টগুলি সম্বন্ধে আরও জানুন",
"firstrun_form_header": "আপনার ইমেইল দিন",
"firstrun_form_sub_header": "Firefox সিঙ্কের সাথে অবিরত করুন",
"section_menu_action_expand_section": "সেকশনটি প্রসারিত করুন",
"section_menu_action_manage_section": "সেকশনটি পরিচালনা করুন",
"section_menu_action_manage_webext": "এক্সটেনসন ব্যবহার করুন",
"section_menu_action_add_topsite": "টপ সাইট যোগ করুন",
"section_menu_action_add_search_engine": "অনুসন্ধান ইঞ্জিন যোগ করুন",
"section_menu_action_move_up": "উপরে উঠাও",
"section_menu_action_move_down": "নীচে নামাও",
"section_menu_action_privacy_notice": "গোপনীয়তা নীতি",
"firstrun_title": "অাপনি Firefox ব্যবহার করুন",
"firstrun_content": "আপনার সমস্ত ডিভাইসে আপনার বুকমার্ক, ইতিহাস, পাসওয়ার্ড এবং অন্যান্য সেটিংস পাওয়া যাবে।",
"firstrun_learn_more_link": "Firefox অ্যাকাউন্ট সম্পর্কে আরও জানুন",
"firstrun_form_header": "আপনার ই-মেইল লিখুন",
"firstrun_form_sub_header": "Firefox সিঙ্ক চালিয়ে যেতে",
"firstrun_email_input_placeholder": "ইমেইল",
"firstrun_invalid_input": "বৈধ ইমেইল প্রয়োজন",
"firstrun_extra_legal_links": "এগিয়ে যাওয়ার জন্য আপনি {terms} এবং {privacy} সম্মত হন।",
"firstrun_terms_of_service": "পরিষেবার শর্তাদি",
"firstrun_privacy_notice": "গোপনীয়তা সংক্রান্ত নীতি",
"firstrun_continue_to_login": "এগিয়ে চলুন",
"firstrun_skip_login": "ধাপটি উপেক্ষা করুন",
"context_menu_title": "মেনু খুলুন",
"pocket_learn_more": "আর জানুন"
"firstrun_invalid_input": "কার্যকর ইমেইল আবশ্যক",
"firstrun_extra_legal_links": "অগ্রসর হওয়ার মাধ্যমে আপনি {terms} এবং {privacy} এর সাথে সম্মত হচ্ছেন।",
"firstrun_terms_of_service": "সেবার শর্ত",
"firstrun_privacy_notice": "গোপনীয়তা নীতি",
"firstrun_continue_to_login": "চালিয়ে যান",
"firstrun_skip_login": "এই ধাপটি বাদ দিন",
"context_menu_title": "Open menu",
"pocket_learn_more": "আর জানুন"
};

File diff suppressed because one or more lines are too long

View File

@ -107,6 +107,6 @@ window.gActivityStreamStrings = {
"firstrun_privacy_notice": "Ichinan Na'oj",
"firstrun_continue_to_login": "Titikïr chik el",
"firstrun_skip_login": "Tixakalüx re jun ruxak re'",
"context_menu_title": "Tijaq k'utüy samaj",
"context_menu_title": "Open menu",
"pocket_learn_more": "Tetamäx Ch'aqa' Chik"
};

File diff suppressed because one or more lines are too long

View File

@ -107,6 +107,6 @@ window.gActivityStreamStrings = {
"firstrun_privacy_notice": "privaatsusreeglitega",
"firstrun_continue_to_login": "Jätka",
"firstrun_skip_login": "Jäta see samm vahele",
"context_menu_title": "Ava menüü",
"context_menu_title": "Open menu",
"pocket_learn_more": "Rohkem teavet"
};

File diff suppressed because one or more lines are too long

View File

@ -107,6 +107,6 @@ window.gActivityStreamStrings = {
"firstrun_privacy_notice": "نکات حریم‌خصوصی",
"firstrun_continue_to_login": "ادامه",
"firstrun_skip_login": "پرش از این مرحله",
"context_menu_title": "باز کردن منو",
"context_menu_title": "Open menu",
"pocket_learn_more": "بیشتر بدانید"
};

File diff suppressed because one or more lines are too long

View File

@ -63,9 +63,9 @@ window.gActivityStreamStrings = {
"topsites_form_title_label": "Naslov",
"topsites_form_title_placeholder": "Unesi naslov",
"topsites_form_url_label": "URL",
"topsites_form_image_url_label": "Prilagođeni URL slike",
"topsites_form_image_url_label": "Custom Image URL",
"topsites_form_url_placeholder": "Utipkajte ili zalijepite URL",
"topsites_form_use_image_link": "Koristi prilagođenu sliku…",
"topsites_form_use_image_link": "Use a custom image…",
"topsites_form_preview_button": "Pregled",
"topsites_form_add_button": "Dodaj",
"topsites_form_save_button": "Spremi",
@ -75,8 +75,8 @@ window.gActivityStreamStrings = {
"pocket_read_more": "Popularne teme:",
"pocket_read_even_more": "Prikaži više priča",
"pocket_more_reccommendations": "Više preporuka",
"pocket_how_it_works": "Kako ovo funkcionira",
"pocket_cta_button": "Nabavite Pocket",
"pocket_how_it_works": "How it works",
"pocket_cta_button": "Get Pocket",
"pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
"highlights_empty_state": "Započnite pretraživati i pokazat ćemo vam neke od izvrsnih članaka, videa i drugih web stranica prema vašim nedavno posjećenim stranicama ili zabilješkama.",
"topstories_empty_state": "Provjerite kasnije za više najpopularnijih priča od {provider}. Ne možete čekati? Odaberite popularne teme kako biste pronašli više kvalitetnih priča s cijelog weba.",
@ -89,24 +89,24 @@ window.gActivityStreamStrings = {
"section_menu_action_collapse_section": "Skupi odjel",
"section_menu_action_expand_section": "Proširi odjel",
"section_menu_action_manage_section": "Upravljanje odjelom",
"section_menu_action_manage_webext": "Upravljanje dodatkom",
"section_menu_action_manage_webext": "Manage Extension",
"section_menu_action_add_topsite": "Dodaj najbolju stranicu",
"section_menu_action_add_search_engine": "Dodaj tražilicu",
"section_menu_action_move_up": "Pomakni gore",
"section_menu_action_move_down": "Pomakni dolje",
"section_menu_action_privacy_notice": "Politika privatnosti",
"firstrun_title": "Uzmite Firefox sa sobom",
"firstrun_content": "Preuzmite svoje zabilješke, povijest, lozinke i druge postavke na sve vaše uređaje.",
"firstrun_content": "Get your bookmarks, history, passwords and other settings on all your devices.",
"firstrun_learn_more_link": "Saznajte više o Firefox računima",
"firstrun_form_header": "Unesite vašu adresu e-pošte",
"firstrun_form_sub_header": "i prijavi se u Firefox Sync",
"firstrun_email_input_placeholder": "E-pošta",
"firstrun_invalid_input": "Potrebna je ispravna adresa e-pošte",
"firstrun_extra_legal_links": "Nastavljanjem pristajete na {terms} i {privacy}.",
"firstrun_terms_of_service": "Uvjete korištenja",
"firstrun_privacy_notice": "Politiku privatnosti",
"firstrun_invalid_input": "Valid email required",
"firstrun_extra_legal_links": "By proceeding, you agree to the {terms} and {privacy}.",
"firstrun_terms_of_service": "Uvjeti korištenja",
"firstrun_privacy_notice": "Politika privatnosti",
"firstrun_continue_to_login": "Nastavi",
"firstrun_skip_login": "Preskočite ovaj korak",
"context_menu_title": "Otvori izbornik",
"context_menu_title": "Open menu",
"pocket_learn_more": "Saznajte više"
};

File diff suppressed because one or more lines are too long

View File

@ -75,7 +75,7 @@ window.gActivityStreamStrings = {
"pocket_read_more": "Subjectos popular:",
"pocket_read_even_more": "Vider plus historias",
"pocket_more_reccommendations": "Altere recommendationes",
"pocket_how_it_works": "Como illo labora",
"pocket_how_it_works": "How it works",
"pocket_cta_button": "Installa Pocket",
"pocket_cta_text": "Salvar le chronologias que tu ama in Pocket, e alimenta tu mente con lecturas fascinante.",
"highlights_empty_state": "Comencia navigar e nos te monstrara alcun del grande articulos, videos e altere paginas que tu ha recentemente visitate o addite marcapaginas hic.",
@ -107,6 +107,6 @@ window.gActivityStreamStrings = {
"firstrun_privacy_notice": "Notification de confidentialitate",
"firstrun_continue_to_login": "Continuar",
"firstrun_skip_login": "Saltar iste grado",
"context_menu_title": "Aperir le menu",
"context_menu_title": "Open menu",
"pocket_learn_more": "Saper plus"
};

View File

@ -9,8 +9,8 @@ window.gActivityStreamStrings = {
"type_label_visited": "ಭೇಟಿ ನೀಡಲಾದ‍",
"type_label_bookmarked": "ಪುಟಗುರುತು ಮಾಡಲಾದ",
"type_label_recommended": "ಪ್ರಚಲಿತ",
"type_label_pocket": "ಪಾಕೆಟ್‌ನಲ್ಲಿ ಉಳಿಸಲಾಗಿದೆ",
"type_label_downloaded": "ಡೌನ್ಲೋಡ್ ಮಾಡಲಾಗಿದೆ",
"type_label_pocket": "Saved to Pocket",
"type_label_downloaded": "Downloaded",
"menu_action_bookmark": "ಪುಟ ಗುರುತು",
"menu_action_remove_bookmark": "ಪುಟ ಗುರುತು ತೆಗೆ",
"menu_action_open_new_window": "ಹೊಸ ಕಿಟಕಿಯಲ್ಲಿ ತೆರೆ",
@ -22,11 +22,11 @@ window.gActivityStreamStrings = {
"confirm_history_delete_p1": "Are you sure you want to delete every instance of this page from your history?",
"confirm_history_delete_notice_p2": "ಈ ಕಾರ್ಯವನ್ನು ರದ್ದುಗೊಳಿಸಲು ಸಾಧ್ಯವಿರುವುದಿಲ್ಲ.",
"menu_action_save_to_pocket": "ಪಾಕೆಟ್‌ನಲ್ಲಿ ಉಳಿಸಿ‍",
"menu_action_delete_pocket": "ಪಾಕೆಟ್ನಿಂದ ಅಳಿಸಿ",
"menu_action_archive_pocket": "ಪಾಕೆಟ್ನಲ್ಲಿ ಆರ್ಕೈವ್ ಮಾಡಿ",
"menu_action_show_file_mac_os": "ಶೋಧಕದಲ್ಲಿ ತೋರಿಸು",
"menu_action_show_file_windows": "ಹೊಂದಿರುವ ಕಡತಕೋಶವನ್ನು ತೆರೆ",
"menu_action_show_file_linux": "ಹೊಂದಿರುವ ಕಡತಕೋಶವನ್ನು ತೆರೆ",
"menu_action_delete_pocket": "Delete from Pocket",
"menu_action_archive_pocket": "Archive in Pocket",
"menu_action_show_file_mac_os": "Show in Finder",
"menu_action_show_file_windows": "Open Containing Folder",
"menu_action_show_file_linux": "Open Containing Folder",
"menu_action_show_file_default": "ಕಡತ ತೋರಿಸು",
"menu_action_open_file": "ಕಡತವನ್ನು ತೆರೆ",
"menu_action_copy_download_link": "ಡೌನ್ಲೋಡ್ ಕೊಂಡಿಯನ್ನು ಪ್ರತಿ ಮಾಡು",
@ -37,19 +37,19 @@ window.gActivityStreamStrings = {
"search_web_placeholder": "ಅಂತರ್ಜಾಲವನ್ನು ಹುಡುಕಿ",
"section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.",
"section_disclaimer_topstories_linktext": "Learn how it works.",
"section_disclaimer_topstories_buttontext": "ಸರಿ, ಗೊತ್ತಾಯಿತು",
"prefs_home_header": "ಫೈರ್ಫಾಕ್ಸ್ ಮುಖಪುಟದ ವಿಷಯ",
"section_disclaimer_topstories_buttontext": "Okay, got it",
"prefs_home_header": "Firefox Home Content",
"prefs_home_description": "Choose what content you want on your Firefox Home screen.",
"prefs_section_rows_option": "{num} row;{num} rows",
"prefs_search_header": "ಜಾಲದ ಹುಡುಕಾಟ",
"prefs_topsites_description": "The sites you visit most",
"prefs_topstories_description2": "Great content from around the web, personalized for you",
"prefs_topstories_options_sponsored_label": "Sponsored Stories",
"prefs_topstories_sponsored_learn_more": "ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ",
"prefs_topstories_sponsored_learn_more": "Learn more",
"prefs_highlights_description": "A selection of sites that youve saved or visited",
"prefs_highlights_options_visited_label": "ಭೇಟಿಕೊಟ್ಟ ಪುಟಗಳು",
"prefs_highlights_options_download_label": "ತೀರಾ ಇತ್ತೀಚಿನ ಡೌನ್ಲೋಡ್",
"prefs_highlights_options_pocket_label": "ಪಾಕೆಟ್ಗೆ ಉಳಿಸಲಾದ ಪುಟಗಳು",
"prefs_highlights_options_visited_label": "Visited Pages",
"prefs_highlights_options_download_label": "Most Recent Download",
"prefs_highlights_options_pocket_label": "Pages Saved to Pocket",
"prefs_snippets_description": "Updates from Mozilla and Firefox",
"settings_pane_button_label": "ಹೊಸ ಹಾಳೆಯ ಪುಟವನ್ನು ಅಗತ್ಯಾನುಗುಣಗೊಳಿಸಿ",
"settings_pane_topsites_header": "ಪ್ರಮುಖ ತಾಣಗಳು",
@ -93,7 +93,7 @@ window.gActivityStreamStrings = {
"section_menu_action_add_topsite": "Add Top Site",
"section_menu_action_add_search_engine": "Add Search Engine",
"section_menu_action_move_up": "ಮೇಲೆ ಜರುಗಿಸು",
"section_menu_action_move_down": "ಕೆಳಗೆ ಜರುಗಿಸು",
"section_menu_action_move_down": "Move Down",
"section_menu_action_privacy_notice": "Privacy Notice",
"firstrun_title": "Take Firefox with You",
"firstrun_content": "Get your bookmarks, history, passwords and other settings on all your devices.",

File diff suppressed because one or more lines are too long

View File

@ -107,6 +107,6 @@ window.gActivityStreamStrings = {
"firstrun_privacy_notice": "Privātuma politikai",
"firstrun_continue_to_login": "Turpināt",
"firstrun_skip_login": "Izlaist šo soli",
"context_menu_title": "Atvērt izvēlni",
"context_menu_title": "Open menu",
"pocket_learn_more": "Uzzināt vairāk"
};

File diff suppressed because one or more lines are too long

View File

@ -74,9 +74,9 @@ window.gActivityStreamStrings = {
"topsites_form_image_validation": "ਚਿੱਤਰ ਲੋਡ ਕਰਨ ਤੋਂ ਅਸਫ਼ਲ ਰਿਹਾ। ਕਿਸੇ ਵੱਖਰੇ URL ਨਾਲ ਕੋਸ਼ਿਸ਼ ਕਰੋ।",
"pocket_read_more": "ਪ੍ਰਸਿੱਧ ਵਿਸ਼ੇ:",
"pocket_read_even_more": "ਹੋਰ ਕਹਾਣੀਆਂ ਵੇਖੋ",
"pocket_more_reccommendations": "ਹੋਰ ਸਿਫਾਰਸ਼ਾਂ",
"pocket_how_it_works": "ਇਹ ਕਿਵੇਂ ਕੰਮ ਕਰਦੀ ਹੈ",
"pocket_cta_button": "ਪਾਕੇਟ ਲਵੋ",
"pocket_more_reccommendations": "More Recommendations",
"pocket_how_it_works": "How it works",
"pocket_cta_button": "Get Pocket",
"pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
"highlights_empty_state": "Start browsing, and well show some of the great articles, videos, and other pages youve recently visited or bookmarked here.",
"topstories_empty_state": "Youve caught up. Check back later for more top stories from {provider}. Cant wait? Select a popular topic to find more great stories from around the web.",
@ -101,12 +101,11 @@ window.gActivityStreamStrings = {
"firstrun_form_header": "ਆਪਣਾ ਈਮੇਲ ਦਿਓ",
"firstrun_form_sub_header": "ਤਾਂ ਕਿ ਫਾਇਰਫਾਕਸ ਸਿੰਕ ਨਾਲ ਜਾਰੀ ਰੱਖਿਆ ਜਾਵੇ।",
"firstrun_email_input_placeholder": "ਈਮੇਲ",
"firstrun_invalid_input": "ਢੁੱਕਵੀਂ ਈਮੇਲ ਚਾਹੀਦੀ ਹੈ",
"firstrun_invalid_input": "Valid email required",
"firstrun_extra_legal_links": "ਜਾਰੀ ਰੱਖ ਕੇ ਤੁਸੀਂ {terms} ਅਤੇ {privacy} ਨਾਲ ਸਹਿਮਤ ਹੁੰਦੇ ਹੋ।",
"firstrun_terms_of_service": "ਸੇਵਾ ਦੀਆਂ ਸ਼ਰਤਾਂ",
"firstrun_privacy_notice": "ਪਰਦੇਦਾਰੀ ਦਾ ਨੋਟਿਸ",
"firstrun_continue_to_login": "ਜਾਰੀ ਰੱਖੋ",
"firstrun_skip_login": "ਇਹ ਪਗ਼ ਛੱਡੋ",
"context_menu_title": "ਮੇਨੂ ਖੋਲ੍ਹੋ",
"pocket_learn_more": "ਹੋਰ ਸਿੱਖੋ"
"context_menu_title": "Open menu"
};

View File

@ -13,8 +13,8 @@ window.gActivityStreamStrings = {
"type_label_downloaded": "Transferido",
"menu_action_bookmark": "Adicionar aos marcadores",
"menu_action_remove_bookmark": "Remover marcador",
"menu_action_open_new_window": "Abrir numa nova janela",
"menu_action_open_private_window": "Abrir numa nova janela privada",
"menu_action_open_new_window": "Abrir em nova janela",
"menu_action_open_private_window": "Abrir em nova janela privada",
"menu_action_dismiss": "Dispensar",
"menu_action_delete": "Apagar do histórico",
"menu_action_pin": "Afixar",

View File

@ -344,16 +344,10 @@ add_task(async function check_pinned_sites() {
const originalPin = JSON.stringify(NewTabUtils.pinnedLinks.links);
const sitesToPin = [
{url: "https://foo.com"},
{url: "https://bloo.com"},
{url: "https://floogle.com", searchTopSite: true},
];
sitesToPin.forEach((site => NewTabUtils.pinnedLinks.pin(site, NewTabUtils.pinnedLinks.links.length)));
// Unpinning adds null to the list of pinned sites, which we should test that we handle gracefully for our targeting
NewTabUtils.pinnedLinks.unpin(sitesToPin[1]);
ok(NewTabUtils.pinnedLinks.links.includes(null),
"should have set an item in pinned links to null via unpinning for testing");
let message;
message = {id: "foo", targeting: "'https://foo.com' in pinnedSites|mapToProperty('url')"};
@ -391,6 +385,9 @@ add_task(async function check_region() {
});
add_task(async function check_browserSettings() {
is(await ASRouterTargeting.Environment.browserSettings.attribution, TelemetryEnvironment.currentEnvironment.settings.attribution,
"should return correct attribution info");
is(await JSON.stringify(ASRouterTargeting.Environment.browserSettings.update), JSON.stringify(TelemetryEnvironment.currentEnvironment.settings.update),
"should return correct update info");
});
@ -405,12 +402,9 @@ add_task(async function check_sync() {
});
add_task(async function check_provider_cohorts() {
await pushPrefs(["browser.newtabpage.activity-stream.asrouter.providers.onboarding", JSON.stringify({id: "onboarding", messages: [], enabled: true, cohort: "foo"})]);
await pushPrefs(["browser.newtabpage.activity-stream.asrouter.providers.cfr", JSON.stringify({id: "cfr", enabled: true, cohort: "bar"})]);
is(await ASRouterTargeting.Environment.providerCohorts.onboarding, "foo",
"should have cohort foo for onboarding");
is(await ASRouterTargeting.Environment.providerCohorts.cfr, "bar",
"should have cohort bar for cfr");
await pushPrefs(["browser.newtabpage.activity-stream.asrouter.messageProviders", JSON.stringify([{id: "onboarding", messages: [], enabled: true, cohort: "foo"}, {id: "cfr", messages: [], cohort: "bar"}])]);
is(await ASRouterTargeting.Environment.providerCohorts.onboarding, "foo");
is(await ASRouterTargeting.Environment.providerCohorts.cfr, "bar");
});
add_task(async function check_xpinstall_enabled() {

View File

@ -32,9 +32,7 @@ export const UserEventPing = Joi.object().keys(Object.assign({}, baseKeys, {
recommender_type: Joi.string(),
value: Joi.object().keys({
newtab_url_category: Joi.string(),
newtab_extension_id: Joi.string(),
home_url_category: Joi.string(),
home_extension_id: Joi.string(),
}),
}));

View File

@ -18,7 +18,7 @@ import {CFRPageActions} from "lib/CFRPageActions.jsm";
import {GlobalOverrider} from "test/unit/utils";
import ProviderResponseSchema from "content-src/asrouter/schemas/provider-response.schema.json";
const MESSAGE_PROVIDER_PREF_NAME = "browser.newtabpage.activity-stream.asrouter.providers.snippets";
const MESSAGE_PROVIDER_PREF_NAME = "browser.newtabpage.activity-stream.asrouter.messageProviders";
const FAKE_PROVIDERS = [FAKE_LOCAL_PROVIDER, FAKE_REMOTE_PROVIDER, FAKE_REMOTE_SETTINGS_PROVIDER];
const ALL_MESSAGE_IDS = [...FAKE_LOCAL_MESSAGES, ...FAKE_REMOTE_MESSAGES].map(message => message.id);
const FAKE_BUNDLE = [FAKE_LOCAL_MESSAGES[1], FAKE_LOCAL_MESSAGES[2]];
@ -385,19 +385,6 @@ describe("ASRouter", () => {
assert.calledOnce(targetStub.sendAsyncMessage);
assert.equal(Router.state.lastMessageId, ALL_MESSAGE_IDS[0]);
});
it("should not return a message from a blocked campaign", async () => {
// Block all messages except the first
await Router.setState(() => ({
messages: [{id: "foo", campaign: "foocampaign"}, {id: "bar"}],
messageBlockList: ["foocampaign"],
}));
const targetStub = {sendAsyncMessage: sandbox.stub()};
await Router.sendNextMessage(targetStub);
assert.calledOnce(targetStub.sendAsyncMessage);
assert.equal(Router.state.lastMessageId, "bar");
});
it("should not return a message from a blocked provider", async () => {
// There are only two providers; block the FAKE_LOCAL_PROVIDER, leaving
// only FAKE_REMOTE_PROVIDER unblocked, which provides only one message
@ -565,19 +552,6 @@ describe("ASRouter", () => {
assert.isTrue(Router.state.messageBlockList.includes("foo"));
assert.calledWith(channel.sendAsyncMessage, PARENT_TO_CHILD_MESSAGE_NAME, {type: "CLEAR_MESSAGE", data: {id: "foo"}});
});
it("should add the campaign to the messageBlockList instead of id if .campaign is specified and not select messages of that campaign again", async () => {
await Router.setState({
messages: [
{id: "1", campaign: "foocampaign"},
{id: "2", campaign: "foocampaign"},
],
});
const msg = fakeAsyncMessage({type: "BLOCK_MESSAGE_BY_ID", data: {id: "1"}});
await Router.onMessage(msg);
assert.isTrue(Router.state.messageBlockList.includes("foocampaign"));
assert.isEmpty(Router._getUnblockedMessages());
});
it("should not broadcast CLEAR_MESSAGE if preventDismiss is true", async () => {
const msg = fakeAsyncMessage({type: "BLOCK_MESSAGE_BY_ID", data: {id: "foo", preventDismiss: true}});
await Router.onMessage(msg);
@ -628,14 +602,6 @@ describe("ASRouter", () => {
assert.isFalse(Router.state.messageBlockList.includes("foo"));
});
it("should remove the campaign from the messageBlockList if it is defined", async () => {
await Router.setState({messages: [{id: "1", campaign: "foo"}]});
await Router.onMessage(fakeAsyncMessage({type: "BLOCK_MESSAGE_BY_ID", data: {id: "1"}}));
assert.isTrue(Router.state.messageBlockList.includes("foo"), "blocklist has campaign id");
await Router.onMessage(fakeAsyncMessage({type: "UNBLOCK_MESSAGE_BY_ID", data: {id: "1"}}));
assert.isFalse(Router.state.messageBlockList.includes("foo"), "campaign id removed from blocklist");
});
it("should save the messageBlockList", async () => {
await Router.onMessage(fakeAsyncMessage({type: "UNBLOCK_MESSAGE_BY_ID", data: {id: "foo"}}));

View File

@ -1,13 +1,13 @@
import {_ASRouterPreferences, ASRouterPreferences as ASRouterPreferencesSingleton, TEST_PROVIDER} from "lib/ASRouterPreferences.jsm";
const FAKE_PROVIDERS = [{id: "foo"}, {id: "bar"}];
const PROVIDER_PREF_BRANCH = "browser.newtabpage.activity-stream.asrouter.providers.";
const PROVIDER_PREF = "browser.newtabpage.activity-stream.asrouter.messageProviders";
const DEVTOOLS_PREF = "browser.newtabpage.activity-stream.asrouter.devtoolsEnabled";
const SNIPPETS_USER_PREF = "browser.newtabpage.activity-stream.feeds.snippets";
const CFR_USER_PREF = "browser.newtabpage.activity-stream.asrouter.userprefs.cfr";
/** NUMBER_OF_PREFS_TO_OBSERVE includes:
* 1. asrouter.providers. pref branch
* 1. asrouter.messageProvider
* 2. asrouter.devtoolsEnabled
* 3. browser.newtabpage.activity-stream.feeds.snippets (user preference - snippets)
* 4. browser.newtabpage.activity-stream.asrouter.userprefs.cfr (user preference - cfr)
@ -20,34 +20,17 @@ describe("ASRouterPreferences", () => {
let addObserverStub;
let stringPrefStub;
let boolPrefStub;
beforeEach(() => {
ASRouterPreferences = new _ASRouterPreferences();
sandbox = sinon.sandbox.create();
addObserverStub = sandbox.stub(global.Services.prefs, "addObserver");
stringPrefStub = sandbox.stub(global.Services.prefs, "getStringPref");
FAKE_PROVIDERS.forEach(provider => {
stringPrefStub.withArgs(`${PROVIDER_PREF_BRANCH}${provider.id}`).returns(JSON.stringify(provider));
});
sandbox.stub(global.Services.prefs, "getChildList")
.withArgs(PROVIDER_PREF_BRANCH).returns(FAKE_PROVIDERS.map(provider => `${PROVIDER_PREF_BRANCH}${provider.id}`));
stringPrefStub = sandbox.stub(global.Services.prefs, "getStringPref").withArgs(PROVIDER_PREF).returns(JSON.stringify(FAKE_PROVIDERS));
boolPrefStub = sandbox.stub(global.Services.prefs, "getBoolPref").returns(false);
});
afterEach(() => {
sandbox.restore();
});
function getPrefNameForProvider(providerId) {
return `${PROVIDER_PREF_BRANCH}${providerId}`;
}
function setPrefForProvider(providerId, value) {
stringPrefStub.withArgs(getPrefNameForProvider(providerId)).returns(JSON.stringify(value));
}
it("ASRouterPreferences should be an instance of _ASRouterPreferences", () => {
assert.instanceOf(ASRouterPreferencesSingleton, _ASRouterPreferences);
});
@ -92,7 +75,7 @@ describe("ASRouterPreferences", () => {
ASRouterPreferences.uninit();
assert.callCount(removeStub, NUMBER_OF_PREFS_TO_OBSERVE);
assert.calledWith(removeStub, PROVIDER_PREF_BRANCH);
assert.calledWith(removeStub, PROVIDER_PREF);
assert.calledWith(removeStub, DEVTOOLS_PREF);
assert.isEmpty(ASRouterPreferences._callbacks);
});
@ -103,16 +86,14 @@ describe("ASRouterPreferences", () => {
const result = ASRouterPreferences.providers;
assert.deepEqual(result, FAKE_PROVIDERS);
// once per pref
assert.calledTwice(stringPrefStub);
assert.calledOnce(stringPrefStub);
});
it("should return the cached value the second time .providers is accessed", () => {
ASRouterPreferences.init();
const [, secondCall] = [ASRouterPreferences.providers, ASRouterPreferences.providers];
assert.deepEqual(secondCall, FAKE_PROVIDERS);
// once per pref
assert.calledTwice(stringPrefStub);
assert.calledOnce(stringPrefStub);
});
it("should just parse the pref each time if ASRouterPreferences hasn't been initialized yet", () => {
// Intentionally not initialized
@ -120,13 +101,13 @@ describe("ASRouterPreferences", () => {
assert.deepEqual(firstCall, FAKE_PROVIDERS);
assert.deepEqual(secondCall, FAKE_PROVIDERS);
assert.callCount(stringPrefStub, 4);
assert.calledTwice(stringPrefStub);
});
it("should skip the pref without throwing if a pref is not parsable", () => {
stringPrefStub.withArgs(`${PROVIDER_PREF_BRANCH}foo`).returns("not json");
it("should return [] if the pref was not parsable", () => {
stringPrefStub.withArgs(PROVIDER_PREF).returns("not json");
ASRouterPreferences.init();
assert.deepEqual(ASRouterPreferences.providers, [{id: "bar"}]);
assert.deepEqual(ASRouterPreferences.providers, []);
});
it("should include TEST_PROVIDER if devtools is turned on", () => {
boolPrefStub.withArgs(DEVTOOLS_PREF).returns(true);
@ -195,21 +176,21 @@ describe("ASRouterPreferences", () => {
describe("#enableOrDisableProvider", () => {
it("should enable an existing provider if second param is true", () => {
const setStub = sandbox.stub(global.Services.prefs, "setStringPref");
setPrefForProvider("foo", {id: "foo", enabled: false});
stringPrefStub.withArgs(PROVIDER_PREF).returns(JSON.stringify([{id: "foo", enabled: false}, {id: "bar", enabled: false}]));
assert.isFalse(ASRouterPreferences.providers[0].enabled);
ASRouterPreferences.enableOrDisableProvider("foo", true);
assert.calledWith(setStub, getPrefNameForProvider("foo"), JSON.stringify({id: "foo", enabled: true}));
assert.calledWith(setStub, PROVIDER_PREF, JSON.stringify([{id: "foo", enabled: true}, {id: "bar", enabled: false}]));
});
it("should disable an existing provider if second param is false", () => {
const setStub = sandbox.stub(global.Services.prefs, "setStringPref");
setPrefForProvider("foo", {id: "foo", enabled: true});
stringPrefStub.withArgs(PROVIDER_PREF).returns(JSON.stringify([{id: "foo", enabled: true}, {id: "bar", enabled: true}]));
assert.isTrue(ASRouterPreferences.providers[0].enabled);
ASRouterPreferences.enableOrDisableProvider("foo", false);
assert.calledWith(setStub, getPrefNameForProvider("foo"), JSON.stringify({id: "foo", enabled: false}));
assert.calledWith(setStub, PROVIDER_PREF, JSON.stringify([{id: "foo", enabled: false}, {id: "bar", enabled: true}]));
});
it("should not throw if the id does not exist", () => {
assert.doesNotThrow(() => {
@ -217,7 +198,7 @@ describe("ASRouterPreferences", () => {
});
});
it("should not throw if pref is not parseable", () => {
stringPrefStub.withArgs(getPrefNameForProvider("foo")).returns("not valid");
stringPrefStub.withArgs(PROVIDER_PREF).returns("not valid");
assert.doesNotThrow(() => {
ASRouterPreferences.enableOrDisableProvider("foo", true);
});
@ -238,36 +219,29 @@ describe("ASRouterPreferences", () => {
it("should reset the pref and user prefs", () => {
const resetStub = sandbox.stub(global.Services.prefs, "clearUserPref");
ASRouterPreferences.resetProviderPref();
FAKE_PROVIDERS.forEach(provider => {
assert.calledWith(resetStub, getPrefNameForProvider(provider.id));
});
assert.calledWith(resetStub, PROVIDER_PREF);
assert.calledWith(resetStub, SNIPPETS_USER_PREF);
assert.calledWith(resetStub, CFR_USER_PREF);
});
});
describe("observer, listeners", () => {
it("should invalidate .providers when the pref is changed", () => {
const testProvider = {id: "newstuff"};
const newProviders = [...FAKE_PROVIDERS, testProvider];
const testProviders = [{id: "newstuff"}];
ASRouterPreferences.init();
assert.deepEqual(ASRouterPreferences.providers, FAKE_PROVIDERS);
stringPrefStub.withArgs(getPrefNameForProvider(testProvider.id)).returns(JSON.stringify(testProvider));
global.Services.prefs.getChildList
.withArgs(PROVIDER_PREF_BRANCH).returns(newProviders.map(provider => getPrefNameForProvider(provider.id)));
ASRouterPreferences.observe(null, null, getPrefNameForProvider(testProvider.id));
stringPrefStub.withArgs(PROVIDER_PREF).returns(JSON.stringify(testProviders));
ASRouterPreferences.observe(null, null, PROVIDER_PREF);
// Cache should be invalidated so we access the new value of the pref now
assert.deepEqual(ASRouterPreferences.providers, newProviders);
assert.deepEqual(ASRouterPreferences.providers, testProviders);
});
it("should invalidate .devtoolsEnabled and .providers when the pref is changed", () => {
ASRouterPreferences.init();
assert.isFalse(ASRouterPreferences.devtoolsEnabled);
boolPrefStub.withArgs(DEVTOOLS_PREF).returns(true);
global.Services.prefs.getChildList
.withArgs(PROVIDER_PREF_BRANCH).returns([]);
stringPrefStub.withArgs(PROVIDER_PREF).returns("[]");
ASRouterPreferences.observe(null, null, DEVTOOLS_PREF);
// Cache should be invalidated so we access the new value of the pref now
@ -282,8 +256,8 @@ describe("ASRouterPreferences", () => {
ASRouterPreferences.addListener(callback1);
ASRouterPreferences.addListener(callback2);
ASRouterPreferences.observe(null, null, getPrefNameForProvider("foo"));
assert.calledWith(callback1, getPrefNameForProvider("foo"));
ASRouterPreferences.observe(null, null, PROVIDER_PREF);
assert.calledWith(callback1, PROVIDER_PREF);
ASRouterPreferences.observe(null, null, DEVTOOLS_PREF);
assert.calledWith(callback2, DEVTOOLS_PREF);
@ -293,8 +267,8 @@ describe("ASRouterPreferences", () => {
ASRouterPreferences.init();
ASRouterPreferences.addListener(callback);
ASRouterPreferences.observe(null, null, getPrefNameForProvider("foo"));
assert.calledWith(callback, getPrefNameForProvider("foo"));
ASRouterPreferences.observe(null, null, PROVIDER_PREF);
assert.calledWith(callback, PROVIDER_PREF);
callback.reset();
ASRouterPreferences.removeListener(callback);

View File

@ -10,15 +10,22 @@ const REGULAR_IDS = [
];
describe("CFRMessageProvider", () => {
it("should have a total of 5 messages", () => {
assert.lengthOf(messages, 5);
it("should have a total of 10 messages", () => {
assert.lengthOf(messages, 10);
});
it("should have one message each for the five regular addons", () => {
it("should two variants for each of the five regular addons", () => {
for (const id of REGULAR_IDS) {
const cohort1 = messages.find(msg => msg.id === `${id}_1`);
assert.ok(cohort1, `contains one day cohort for ${id}`);
assert.deepEqual(cohort1.frequency, {lifetime: 1}, "one day cohort has the right frequency cap");
assert.include(cohort1.targeting, `(providerCohorts.cfr in ["one_per_day", "nightly"])`);
const cohort3 = messages.find(msg => msg.id === `${id}_3`);
assert.ok(cohort3, `contains three day cohort for ${id}`);
assert.deepEqual(cohort3.frequency, {lifetime: 3}, "three day cohort has the right frequency cap");
assert.notInclude(cohort3.targeting, `providerCohorts.cfr`);
assert.include(cohort3.targeting, `(providerCohorts.cfr == "three_per_day")`);
assert.deepEqual(cohort1.content, cohort3.content, "cohorts should have the same content");
}
});
it("should always have xpinstallEnabled as targeting if it is an addon", () => {

View File

@ -525,54 +525,6 @@ describe("CFRPageActions", () => {
// Should remove the recommendation
assert.isFalse(CFRPageActions.RecommendationMap.has(fakeBrowser));
});
it("should send right telemetry for BLOCK secondary action", async () => {
await pageAction._handleClick();
const blockAction = global.PopupNotifications.show.firstCall.args[5][1]; // eslint-disable-line prefer-destructuring
assert.deepEqual(blockAction.label, {value: "Secondary Button 2", attributes: {accesskey: "a"}});
sandbox.spy(pageAction, "hide");
sandbox.spy(pageAction, "_blockMessage");
CFRPageActions.RecommendationMap.set(fakeBrowser, {});
blockAction.callback();
assert.calledOnce(pageAction.hide);
assert.calledOnce(pageAction._blockMessage);
// Should send telemetry
assert.calledWith(dispatchStub, {
type: "DOORHANGER_TELEMETRY",
data: {
action: "cfr_user_event",
source: "CFR",
message_id: fakeRecommendation.id,
bucket_id: fakeRecommendation.content.bucket_id,
event: "BLOCK",
},
});
// Should remove the recommendation
assert.isFalse(CFRPageActions.RecommendationMap.has(fakeBrowser));
});
it("should send right telemetry for MANAGE secondary action", async () => {
await pageAction._handleClick();
const blockAction = global.PopupNotifications.show.firstCall.args[5][2]; // eslint-disable-line prefer-destructuring
assert.deepEqual(blockAction.label, {value: "Secondary Button 3", attributes: {accesskey: "g"}});
sandbox.spy(pageAction, "hide");
CFRPageActions.RecommendationMap.set(fakeBrowser, {});
blockAction.callback();
assert.calledOnce(pageAction.hide);
// Should send telemetry
assert.calledWith(dispatchStub, {
type: "DOORHANGER_TELEMETRY",
data: {
action: "cfr_user_event",
source: "CFR",
message_id: fakeRecommendation.id,
bucket_id: fakeRecommendation.content.bucket_id,
event: "MANAGE",
},
});
// Should remove the recommendation
assert.isFalse(CFRPageActions.RecommendationMap.has(fakeBrowser));
});
it("should call PopupNotifications.show with the right arguments", async () => {
await pageAction._handleClick();
assert.calledWith(

View File

@ -1,4 +1,3 @@
import {GlobalOverrider} from "test/unit/utils";
import {OnboardingMessageProvider} from "lib/OnboardingMessageProvider.jsm";
import schema from "content-src/asrouter/templates/OnboardingMessage/OnboardingMessage.schema.json";
@ -22,25 +21,14 @@ const L10N_CONTENT = {
};
describe("OnboardingMessage", () => {
let globals;
let sandbox;
beforeEach(() => {
globals = new GlobalOverrider();
sandbox = sinon.sandbox.create();
globals.set("FxAccountsConfig", {promiseEmailFirstURI: sandbox.stub().resolves("some/url")});
});
afterEach(() => {
sandbox.restore();
globals.restore();
});
it("should validate DEFAULT_CONTENT", () => {
assert.jsonSchema(DEFAULT_CONTENT, schema);
});
it("should validate L10N_CONTENT", () => {
assert.jsonSchema(L10N_CONTENT, schema);
});
it("should validate all messages from OnboardingMessageProvider", async () => {
const messages = await OnboardingMessageProvider.getUntranslatedMessages();
it("should validate all messages from OnboardingMessageProvider", () => {
const messages = OnboardingMessageProvider.getUntranslatedMessages();
messages.forEach(msg => assert.jsonSchema(msg.content, schema));
});
});

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