diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:34:42 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:34:42 +0000 |
commit | da4c7e7ed675c3bf405668739c3012d140856109 (patch) | |
tree | cdd868dba063fecba609a1d819de271f0d51b23e /browser/components/asrouter | |
parent | Adding upstream version 125.0.3. (diff) | |
download | firefox-da4c7e7ed675c3bf405668739c3012d140856109.tar.xz firefox-da4c7e7ed675c3bf405668739c3012d140856109.zip |
Adding upstream version 126.0.upstream/126.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/asrouter')
41 files changed, 1174 insertions, 2384 deletions
diff --git a/browser/components/asrouter/.eslintrc.js b/browser/components/asrouter/.eslintrc.js index ef5bc81b68..b2a647e42d 100644 --- a/browser/components/asrouter/.eslintrc.js +++ b/browser/components/asrouter/.eslintrc.js @@ -63,8 +63,6 @@ module.exports = { }, ], rules: { - "fetch-options/no-fetch-credentials": "error", - "react/jsx-boolean-value": ["error", "always"], "react/jsx-key": "error", "react/jsx-no-bind": [ diff --git a/browser/components/asrouter/actors/ASRouterChild.sys.mjs b/browser/components/asrouter/actors/ASRouterChild.sys.mjs index 2096d92bb3..95f625e2b5 100644 --- a/browser/components/asrouter/actors/ASRouterChild.sys.mjs +++ b/browser/components/asrouter/actors/ASRouterChild.sys.mjs @@ -11,9 +11,7 @@ // eslint-disable-next-line mozilla/use-static-import const { MESSAGE_TYPE_LIST, MESSAGE_TYPE_HASH: msg } = - ChromeUtils.importESModule( - "resource:///modules/asrouter/ActorConstants.sys.mjs" - ); + ChromeUtils.importESModule("resource:///modules/asrouter/ActorConstants.mjs"); const VALID_TYPES = new Set(MESSAGE_TYPE_LIST); @@ -103,8 +101,6 @@ export class ASRouterChild extends JSWindowActorChild { case msg.DISABLE_PROVIDER: case msg.ENABLE_PROVIDER: case msg.EXPIRE_QUERY_CACHE: - case msg.FORCE_WHATSNEW_PANEL: - case msg.CLOSE_WHATSNEW_PANEL: case msg.FORCE_PRIVATE_BROWSING_WINDOW: case msg.IMPRESSION: case msg.RESET_PROVIDER_PREF: diff --git a/browser/components/asrouter/bin/import-rollouts.js b/browser/components/asrouter/bin/import-rollouts.js index d29a31a068..bb5c17d9ae 100644 --- a/browser/components/asrouter/bin/import-rollouts.js +++ b/browser/components/asrouter/bin/import-rollouts.js @@ -126,10 +126,6 @@ async function getMessageValidators(skipValidation) { "./content-src/templates/OnboardingMessage/UpdateAction.schema.json", { common: true } ), - whatsnew_panel_message: await getValidator( - "./content-src/templates/OnboardingMessage/WhatsNewMessage.schema.json", - { common: true } - ), feature_callout: await getValidator( // For now, Feature Callout and Spotlight share a common schema "./content-src/templates/OnboardingMessage/Spotlight.schema.json", diff --git a/browser/components/asrouter/content-src/asrouter-utils.mjs b/browser/components/asrouter/content-src/asrouter-utils.mjs index 989d864e71..3789158547 100644 --- a/browser/components/asrouter/content-src/asrouter-utils.mjs +++ b/browser/components/asrouter/content-src/asrouter-utils.mjs @@ -2,10 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -// eslint-disable-next-line mozilla/reject-import-system-module-from-non-system -import { MESSAGE_TYPE_HASH as msg } from "../modules/ActorConstants.sys.mjs"; -// eslint-disable-next-line mozilla/reject-import-system-module-from-non-system -import { actionCreators as ac } from "../../newtab/common/Actions.sys.mjs"; +import { MESSAGE_TYPE_HASH as msg } from "../modules/ActorConstants.mjs"; +import { actionCreators as ac } from "../../newtab/common/Actions.mjs"; export const ASRouterUtils = { addListener(listener) { diff --git a/browser/components/asrouter/content-src/components/ASRouterAdmin/ASRouterAdmin.jsx b/browser/components/asrouter/content-src/components/ASRouterAdmin/ASRouterAdmin.jsx index befce707ef..32d1614307 100644 --- a/browser/components/asrouter/content-src/components/ASRouterAdmin/ASRouterAdmin.jsx +++ b/browser/components/asrouter/content-src/components/ASRouterAdmin/ASRouterAdmin.jsx @@ -15,6 +15,18 @@ const Row = props => ( </tr> ); +// Convert a UTF-8 string to a string in which only one byte of each +// 16-bit unit is occupied. This is necessary to comply with `btoa` API constraints. +export function toBinary(string) { + const codeUnits = new Uint16Array(string.length); + for (let i = 0; i < codeUnits.length; i++) { + codeUnits[i] = string.charCodeAt(i); + } + return btoa( + String.fromCharCode(...Array.from(new Uint8Array(codeUnits.buffer))) + ); +} + function relativeTime(timestamp) { if (!timestamp) { return ""; @@ -531,7 +543,9 @@ export class ASRouterAdminInner extends React.PureComponent { {aboutMessagePreviewSupported ? ( <CopyButton transformer={text => - `about:messagepreview?json=${encodeURIComponent(btoa(text))}` + `about:messagepreview?json=${encodeURIComponent( + toBinary(text) + )}` } label="Share" copiedLabel="Copied!" diff --git a/browser/components/asrouter/content-src/schemas/BackgroundTaskMessagingExperiment.schema.json b/browser/components/asrouter/content-src/schemas/BackgroundTaskMessagingExperiment.schema.json index 9de01052f7..5fe86f9617 100644 --- a/browser/components/asrouter/content-src/schemas/BackgroundTaskMessagingExperiment.schema.json +++ b/browser/components/asrouter/content-src/schemas/BackgroundTaskMessagingExperiment.schema.json @@ -10,7 +10,9 @@ "const": "multi" } }, - "required": ["template"] + "required": [ + "template" + ] }, "then": { "$ref": "chrome://browser/content/asrouter/schemas/BackgroundTaskMessagingExperiment.schema.json#/$defs/MultiMessage" @@ -68,7 +70,9 @@ "type": "object" } }, - "required": ["type"], + "required": [ + "type" + ], "additionalProperties": true }, "requireInteraction": { @@ -116,24 +120,37 @@ "type": "object" } }, - "required": ["type"], + "required": [ + "type" + ], "additionalProperties": true } }, - "required": ["action", "title"], + "required": [ + "action", + "title" + ], "additionalProperties": true } } }, "additionalProperties": true, - "required": ["title", "body"] + "required": [ + "title", + "body" + ] }, "template": { "type": "string", "const": "toast_notification" } }, - "required": ["content", "targeting", "template", "trigger"], + "required": [ + "content", + "targeting", + "template", + "trigger" + ], "additionalProperties": true }, "Message": { @@ -154,7 +171,9 @@ "template": { "type": "string", "description": "Which messaging template this message is using.", - "enum": ["toast_notification"] + "enum": [ + "toast_notification" + ] }, "frequency": { "type": "object", @@ -184,7 +203,10 @@ "maximum": 100 } }, - "required": ["period", "cap"] + "required": [ + "period", + "cap" + ] } } } @@ -224,7 +246,9 @@ } } }, - "required": ["id"] + "required": [ + "id" + ] }, "provider": { "description": "An identifier for the provider of this message, such as \"cfr\" or \"preview\".", @@ -233,8 +257,14 @@ }, "additionalProperties": true, "dependentRequired": { - "content": ["id", "template"], - "template": ["id", "content"] + "content": [ + "id", + "template" + ], + "template": [ + "id", + "content" + ] } }, "localizedText": { @@ -245,7 +275,9 @@ "type": "string" } }, - "required": ["string_id"] + "required": [ + "string_id" + ] }, "localizableText": { "description": "Either a raw string or an object containing the string_id of the localized text", @@ -272,10 +304,14 @@ "properties": { "template": { "type": "string", - "enum": ["toast_notification"] + "enum": [ + "toast_notification" + ] } }, - "required": ["template"] + "required": [ + "template" + ] }, "then": { "$ref": "chrome://browser/content/asrouter/schemas/BackgroundTaskMessagingExperiment.schema.json#/$defs/ToastNotification" @@ -299,7 +335,10 @@ } } }, - "required": ["template", "messages"] + "required": [ + "template", + "messages" + ] } } } diff --git a/browser/components/asrouter/content-src/schemas/MessagingExperiment.schema.json b/browser/components/asrouter/content-src/schemas/MessagingExperiment.schema.json index fbabb109f8..dd4ce4776d 100644 --- a/browser/components/asrouter/content-src/schemas/MessagingExperiment.schema.json +++ b/browser/components/asrouter/content-src/schemas/MessagingExperiment.schema.json @@ -10,7 +10,9 @@ "const": "multi" } }, - "required": ["template"] + "required": [ + "template" + ] }, "then": { "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/MultiMessage" @@ -41,7 +43,9 @@ "layout": { "type": "string", "description": "Describes how content should be displayed.", - "enum": ["chiclet_open_url"] + "enum": [ + "chiclet_open_url" + ] }, "bucket_id": { "type": "string", @@ -66,11 +70,17 @@ "where": { "description": "Should it open in a new tab or the current tab", "type": "string", - "enum": ["current", "tabshifted"] + "enum": [ + "current", + "tabshifted" + ] } }, "additionalProperties": true, - "required": ["url", "where"] + "required": [ + "url", + "where" + ] } }, "additionalProperties": true, @@ -87,7 +97,10 @@ "const": "cfr_urlbar_chiclet" } }, - "required": ["targeting", "trigger"] + "required": [ + "targeting", + "trigger" + ] }, "ExtensionDoorhanger": { "$schema": "https://json-schema.org/draft/2019-09/schema", @@ -162,10 +175,14 @@ "description": "Text for button tooltip used to provide information about the doorhanger." } }, - "required": ["tooltiptext"] + "required": [ + "tooltiptext" + ] } }, - "required": ["attributes"] + "required": [ + "attributes" + ] }, { "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/localizedText" @@ -185,7 +202,10 @@ "learn_more": { "type": "string", "description": "Last part of the path in the SUMO URL to the support page with the information about the doorhanger.", - "examples": ["extensionpromotions", "extensionrecommendations"] + "examples": [ + "extensionpromotions", + "extensionrecommendations" + ] }, "heading_text": { "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/localizableText", @@ -236,7 +256,12 @@ "description": "Link that offers more information related to the addon." } }, - "required": ["title", "author", "icon", "amo_url"] + "required": [ + "title", + "author", + "icon", + "amo_url" + ] }, "text": { "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/localizableText", @@ -255,7 +280,9 @@ } } }, - "required": ["steps"] + "required": [ + "steps" + ] }, "buttons": { "description": "The label and functionality for the buttons in the pop-over.", @@ -281,11 +308,16 @@ "description": "A single character to be used as a shortcut key for the secondary button. This should be one of the characters that appears in the button label." } }, - "required": ["accesskey"], + "required": [ + "accesskey" + ], "description": "Button attributes." } }, - "required": ["value", "attributes"] + "required": [ + "value", + "attributes" + ] }, { "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/localizedText" @@ -341,11 +373,16 @@ "description": "A single character to be used as a shortcut key for the secondary button. This should be one of the characters that appears in the button label." } }, - "required": ["accesskey"], + "required": [ + "accesskey" + ], "description": "Button attributes." } }, - "required": ["value", "attributes"] + "required": [ + "value", + "attributes" + ] }, { "properties": { @@ -360,7 +397,9 @@ ] } }, - "required": ["string_id"] + "required": [ + "string_id" + ] } ], "description": "Id of localized string or message override." @@ -417,16 +456,25 @@ } }, "then": { - "required": ["category", "notification_text"] + "required": [ + "category", + "notification_text" + ] } }, "template": { "type": "string", - "enum": ["cfr_doorhanger", "milestone_message"] + "enum": [ + "cfr_doorhanger", + "milestone_message" + ] } }, "additionalProperties": true, - "required": ["targeting", "trigger"], + "required": [ + "targeting", + "trigger" + ], "$defs": { "plainText": { "description": "Plain text (no HTML allowed)", @@ -457,7 +505,10 @@ "type": { "type": "string", "description": "Should the message be global (persisted across tabs) or local (disappear when switching to a different tab).", - "enum": ["global", "tab"] + "enum": [ + "global", + "tab" + ] }, "text": { "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/localizableText", @@ -497,7 +548,9 @@ "type": "object" } }, - "required": ["type"], + "required": [ + "type" + ], "additionalProperties": true }, "supportPage": { @@ -505,13 +558,19 @@ "description": "A page title on SUMO to link to" } }, - "required": ["label", "action"], + "required": [ + "label", + "action" + ], "additionalProperties": true } } }, "additionalProperties": true, - "required": ["text", "buttons"] + "required": [ + "text", + "buttons" + ] }, "template": { "type": "string", @@ -519,7 +578,10 @@ } }, "additionalProperties": true, - "required": ["targeting", "trigger"], + "required": [ + "targeting", + "trigger" + ], "$defs": { "plainText": { "description": "Plain text (no HTML allowed)", @@ -587,12 +649,22 @@ "promoType": { "type": "string", "description": "Promo type used to determine if promo should show to a given user", - "enum": ["FOCUS", "VPN", "PIN", "COOKIE_BANNERS", "OTHER"] + "enum": [ + "FOCUS", + "VPN", + "PIN", + "COOKIE_BANNERS", + "OTHER" + ] }, "promoSectionStyle": { "type": "string", "description": "Sets the position of the promo section. Possible values are: top, below-search, bottom. Default bottom.", - "enum": ["top", "below-search", "bottom"] + "enum": [ + "top", + "below-search", + "bottom" + ] }, "promoTitle": { "type": "string", @@ -624,16 +696,23 @@ "type": "object" } }, - "required": ["type"], + "required": [ + "type" + ], "additionalProperties": true } }, - "required": ["action"] + "required": [ + "action" + ] }, "promoLinkType": { "type": "string", "description": "Type of promo link type. Possible values: link, button. Default is link.", - "enum": ["link", "button"] + "enum": [ + "link", + "button" + ] }, "promoImageLarge": { "type": "string", @@ -655,10 +734,14 @@ "const": true } }, - "required": ["promoEnabled"] + "required": [ + "promoEnabled" + ] }, "then": { - "required": ["promoButton"] + "required": [ + "promoButton" + ] } }, { @@ -668,20 +751,28 @@ "const": true } }, - "required": ["infoEnabled"] + "required": [ + "infoEnabled" + ] }, "then": { - "required": ["infoLinkText"], + "required": [ + "infoLinkText" + ], "if": { "properties": { "infoTitleEnabled": { "const": true } }, - "required": ["infoTitleEnabled"] + "required": [ + "infoTitleEnabled" + ] }, "then": { - "required": ["infoTitle"] + "required": [ + "infoTitle" + ] } } } @@ -693,7 +784,9 @@ } }, "additionalProperties": true, - "required": ["targeting"] + "required": [ + "targeting" + ] }, "Spotlight": { "$schema": "https://json-schema.org/draft/2019-09/schema", @@ -763,11 +856,16 @@ "template": { "type": "string", "description": "Specify whether the surface is shown as a Spotlight modal or an in-surface Feature Callout dialog", - "enum": ["spotlight", "feature_callout"] + "enum": [ + "spotlight", + "feature_callout" + ] } }, "additionalProperties": true, - "required": ["targeting"] + "required": [ + "targeting" + ] }, "ToastNotification": { "$schema": "https://json-schema.org/draft/2019-09/schema", @@ -818,7 +916,9 @@ "type": "object" } }, - "required": ["type"], + "required": [ + "type" + ], "additionalProperties": true }, "requireInteraction": { @@ -866,24 +966,37 @@ "type": "object" } }, - "required": ["type"], + "required": [ + "type" + ], "additionalProperties": true } }, - "required": ["action", "title"], + "required": [ + "action", + "title" + ], "additionalProperties": true } } }, "additionalProperties": true, - "required": ["title", "body"] + "required": [ + "title", + "body" + ] }, "template": { "type": "string", "const": "toast_notification" } }, - "required": ["content", "targeting", "template", "trigger"], + "required": [ + "content", + "targeting", + "template", + "trigger" + ], "additionalProperties": true }, "ToolbarBadgeMessage": { @@ -912,7 +1025,9 @@ } }, "additionalProperties": true, - "required": ["id"], + "required": [ + "id" + ], "description": "Optional action to take in addition to showing the notification" }, "delay": { @@ -925,7 +1040,9 @@ } }, "additionalProperties": true, - "required": ["target"] + "required": [ + "target" + ] }, "template": { "type": "string", @@ -933,7 +1050,9 @@ } }, "additionalProperties": true, - "required": ["targeting"] + "required": [ + "targeting" + ] }, "UpdateAction": { "$schema": "https://json-schema.org/draft/2019-09/schema", @@ -973,101 +1092,25 @@ }, "additionalProperties": true, "description": "Optional action to take in addition to showing the notification", - "required": ["id", "data"] - } - }, - "additionalProperties": true, - "required": ["action"] - }, - "template": { - "type": "string", - "const": "update_action" - } - }, - "required": ["targeting"] - }, - "WhatsNewMessage": { - "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "file:///WhatsNewMessage.schema.json", - "title": "WhatsNewMessage", - "description": "A template for the messages that appear in the What's New panel.", - "allOf": [ - { - "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/Message" - } - ], - "type": "object", - "properties": { - "content": { - "type": "object", - "properties": { - "layout": { - "description": "Different message layouts", - "enum": ["tracking-protections"] - }, - "bucket_id": { - "type": "string", - "description": "A bucket identifier for the addon. This is used in order to anonymize telemetry for history-sensitive targeting." - }, - "published_date": { - "type": "integer", - "description": "The date/time (number of milliseconds elapsed since January 1, 1970 00:00:00 UTC) the message was published." - }, - "title": { - "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/localizableText", - "description": "Id of localized string or message override of What's New message title" - }, - "subtitle": { - "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/localizableText", - "description": "Id of localized string or message override of What's New message subtitle" - }, - "body": { - "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/localizableText", - "description": "Id of localized string or message override of What's New message body" - }, - "link_text": { - "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/localizableText", - "description": "(optional) Id of localized string or message override of What's New message link text" - }, - "cta_url": { - "description": "Target URL for the What's New message.", - "type": "string", - "format": "moz-url-format" - }, - "cta_type": { - "description": "Type of url open action", - "enum": ["OPEN_URL", "OPEN_ABOUT_PAGE", "OPEN_PROTECTION_REPORT"] - }, - "cta_where": { - "description": "How to open the cta: new window, tab, focused, unfocused.", - "enum": ["current", "tabshifted", "tab", "save", "window"] - }, - "icon_url": { - "description": "(optional) URL for the What's New message icon.", - "type": "string", - "format": "uri" - }, - "icon_alt": { - "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/localizableText", - "description": "Alt text for image." + "required": [ + "id", + "data" + ] } }, "additionalProperties": true, "required": [ - "published_date", - "title", - "body", - "cta_url", - "bucket_id" + "action" ] }, "template": { "type": "string", - "const": "whatsnew_panel_message" + "const": "update_action" } }, - "required": ["order"], - "additionalProperties": true + "required": [ + "targeting" + ] }, "Message": { "type": "object", @@ -1097,8 +1140,7 @@ "feature_callout", "toast_notification", "toolbar_badge", - "update_action", - "whatsnew_panel_message" + "update_action" ] }, "frequency": { @@ -1129,7 +1171,10 @@ "maximum": 100 } }, - "required": ["period", "cap"] + "required": [ + "period", + "cap" + ] } } } @@ -1169,7 +1214,9 @@ } } }, - "required": ["id"] + "required": [ + "id" + ] }, "provider": { "description": "An identifier for the provider of this message, such as \"cfr\" or \"preview\".", @@ -1178,8 +1225,14 @@ }, "additionalProperties": true, "dependentRequired": { - "content": ["id", "template"], - "template": ["id", "content"] + "content": [ + "id", + "template" + ], + "template": [ + "id", + "content" + ] } }, "localizedText": { @@ -1190,7 +1243,9 @@ "type": "string" } }, - "required": ["string_id"] + "required": [ + "string_id" + ] }, "localizableText": { "description": "Either a raw string or an object containing the string_id of the localized text", @@ -1217,10 +1272,14 @@ "properties": { "template": { "type": "string", - "enum": ["cfr_urlbar_chiclet"] + "enum": [ + "cfr_urlbar_chiclet" + ] } }, - "required": ["template"] + "required": [ + "template" + ] }, "then": { "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/CFRUrlbarChiclet" @@ -1232,10 +1291,15 @@ "properties": { "template": { "type": "string", - "enum": ["cfr_doorhanger", "milestone_message"] + "enum": [ + "cfr_doorhanger", + "milestone_message" + ] } }, - "required": ["template"] + "required": [ + "template" + ] }, "then": { "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/ExtensionDoorhanger" @@ -1247,10 +1311,14 @@ "properties": { "template": { "type": "string", - "enum": ["infobar"] + "enum": [ + "infobar" + ] } }, - "required": ["template"] + "required": [ + "template" + ] }, "then": { "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/InfoBar" @@ -1262,10 +1330,14 @@ "properties": { "template": { "type": "string", - "enum": ["pb_newtab"] + "enum": [ + "pb_newtab" + ] } }, - "required": ["template"] + "required": [ + "template" + ] }, "then": { "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/NewtabPromoMessage" @@ -1277,10 +1349,15 @@ "properties": { "template": { "type": "string", - "enum": ["spotlight", "feature_callout"] + "enum": [ + "spotlight", + "feature_callout" + ] } }, - "required": ["template"] + "required": [ + "template" + ] }, "then": { "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/Spotlight" @@ -1292,10 +1369,14 @@ "properties": { "template": { "type": "string", - "enum": ["toast_notification"] + "enum": [ + "toast_notification" + ] } }, - "required": ["template"] + "required": [ + "template" + ] }, "then": { "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/ToastNotification" @@ -1307,10 +1388,14 @@ "properties": { "template": { "type": "string", - "enum": ["toolbar_badge"] + "enum": [ + "toolbar_badge" + ] } }, - "required": ["template"] + "required": [ + "template" + ] }, "then": { "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/ToolbarBadgeMessage" @@ -1322,29 +1407,18 @@ "properties": { "template": { "type": "string", - "enum": ["update_action"] + "enum": [ + "update_action" + ] } }, - "required": ["template"] + "required": [ + "template" + ] }, "then": { "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/UpdateAction" } - }, - { - "if": { - "type": "object", - "properties": { - "template": { - "type": "string", - "enum": ["whatsnew_panel_message"] - } - }, - "required": ["template"] - }, - "then": { - "$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/WhatsNewMessage" - } } ] }, @@ -1364,7 +1438,10 @@ } } }, - "required": ["template", "messages"] + "required": [ + "template", + "messages" + ] } } } diff --git a/browser/components/asrouter/content-src/schemas/make-schemas.py b/browser/components/asrouter/content-src/schemas/make-schemas.py index f66490f23a..1f677cab28 100755 --- a/browser/components/asrouter/content-src/schemas/make-schemas.py +++ b/browser/components/asrouter/content-src/schemas/make-schemas.py @@ -83,9 +83,6 @@ SCHEMAS = [ "UpdateAction": ( SCHEMA_DIR / "OnboardingMessage" / "UpdateAction.schema.json" ), - "WhatsNewMessage": ( - SCHEMA_DIR / "OnboardingMessage" / "WhatsNewMessage.schema.json" - ), }, bundle_common=True, test_corpus={ diff --git a/browser/components/asrouter/content-src/templates/OnboardingMessage/WhatsNewMessage.schema.json b/browser/components/asrouter/content-src/templates/OnboardingMessage/WhatsNewMessage.schema.json deleted file mode 100644 index 26e795d068..0000000000 --- a/browser/components/asrouter/content-src/templates/OnboardingMessage/WhatsNewMessage.schema.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "file:///WhatsNewMessage.schema.json", - "title": "WhatsNewMessage", - "description": "A template for the messages that appear in the What's New panel.", - "allOf": [{ "$ref": "file:///FxMSCommon.schema.json#/$defs/Message" }], - "type": "object", - "properties": { - "content": { - "type": "object", - "properties": { - "layout": { - "description": "Different message layouts", - "enum": ["tracking-protections"] - }, - "bucket_id": { - "type": "string", - "description": "A bucket identifier for the addon. This is used in order to anonymize telemetry for history-sensitive targeting." - }, - "published_date": { - "type": "integer", - "description": "The date/time (number of milliseconds elapsed since January 1, 1970 00:00:00 UTC) the message was published." - }, - "title": { - "$ref": "file:///FxMSCommon.schema.json#/$defs/localizableText", - "description": "Id of localized string or message override of What's New message title" - }, - "subtitle": { - "$ref": "file:///FxMSCommon.schema.json#/$defs/localizableText", - "description": "Id of localized string or message override of What's New message subtitle" - }, - "body": { - "$ref": "file:///FxMSCommon.schema.json#/$defs/localizableText", - "description": "Id of localized string or message override of What's New message body" - }, - "link_text": { - "$ref": "file:///FxMSCommon.schema.json#/$defs/localizableText", - "description": "(optional) Id of localized string or message override of What's New message link text" - }, - "cta_url": { - "description": "Target URL for the What's New message.", - "type": "string", - "format": "moz-url-format" - }, - "cta_type": { - "description": "Type of url open action", - "enum": ["OPEN_URL", "OPEN_ABOUT_PAGE", "OPEN_PROTECTION_REPORT"] - }, - "cta_where": { - "description": "How to open the cta: new window, tab, focused, unfocused.", - "enum": ["current", "tabshifted", "tab", "save", "window"] - }, - "icon_url": { - "description": "(optional) URL for the What's New message icon.", - "type": "string", - "format": "uri" - }, - "icon_alt": { - "$ref": "file:///FxMSCommon.schema.json#/$defs/localizableText", - "description": "Alt text for image." - } - }, - "additionalProperties": true, - "required": ["published_date", "title", "body", "cta_url", "bucket_id"] - }, - "template": { - "type": "string", - "const": "whatsnew_panel_message" - } - }, - "required": ["order"], - "additionalProperties": true -} diff --git a/browser/components/asrouter/content/asrouter-admin.bundle.js b/browser/components/asrouter/content/asrouter-admin.bundle.js index b38d551a17..e5a1e6dd6a 100644 --- a/browser/components/asrouter/content/asrouter-admin.bundle.js +++ b/browser/components/asrouter/content/asrouter-admin.bundle.js @@ -16,15 +16,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ ASRouterUtils: () => (/* binding */ ASRouterUtils) /* harmony export */ }); -/* harmony import */ var _modules_ActorConstants_sys_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); -/* harmony import */ var _newtab_common_Actions_sys_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3); +/* harmony import */ var _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); +/* harmony import */ var _newtab_common_Actions_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3); /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -// eslint-disable-next-line mozilla/reject-import-system-module-from-non-system -// eslint-disable-next-line mozilla/reject-import-system-module-from-non-system const ASRouterUtils = { @@ -46,54 +44,54 @@ const ASRouterUtils = { }, blockById(id, options) { return ASRouterUtils.sendMessage({ - type: _modules_ActorConstants_sys_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.BLOCK_MESSAGE_BY_ID, + type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.BLOCK_MESSAGE_BY_ID, data: { id, ...options }, }); }, modifyMessageJson(content) { return ASRouterUtils.sendMessage({ - type: _modules_ActorConstants_sys_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.MODIFY_MESSAGE_JSON, + type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.MODIFY_MESSAGE_JSON, data: { content }, }); }, executeAction(button_action) { return ASRouterUtils.sendMessage({ - type: _modules_ActorConstants_sys_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.USER_ACTION, + type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.USER_ACTION, data: button_action, }); }, unblockById(id) { return ASRouterUtils.sendMessage({ - type: _modules_ActorConstants_sys_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.UNBLOCK_MESSAGE_BY_ID, + type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.UNBLOCK_MESSAGE_BY_ID, data: { id }, }); }, blockBundle(bundle) { return ASRouterUtils.sendMessage({ - type: _modules_ActorConstants_sys_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.BLOCK_BUNDLE, + type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.BLOCK_BUNDLE, data: { bundle }, }); }, unblockBundle(bundle) { return ASRouterUtils.sendMessage({ - type: _modules_ActorConstants_sys_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.UNBLOCK_BUNDLE, + type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.UNBLOCK_BUNDLE, data: { bundle }, }); }, overrideMessage(id) { return ASRouterUtils.sendMessage({ - type: _modules_ActorConstants_sys_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.OVERRIDE_MESSAGE, + type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.OVERRIDE_MESSAGE, data: { id }, }); }, editState(key, value) { return ASRouterUtils.sendMessage({ - type: _modules_ActorConstants_sys_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.EDIT_STATE, + type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.EDIT_STATE, data: { [key]: value }, }); }, sendTelemetry(ping) { - return ASRouterUtils.sendMessage(_newtab_common_Actions_sys_mjs__WEBPACK_IMPORTED_MODULE_1__.actionCreators.ASRouterUserEvent(ping)); + return ASRouterUtils.sendMessage(_newtab_common_Actions_mjs__WEBPACK_IMPORTED_MODULE_1__.actionCreators.ASRouterUserEvent(ping)); }, getPreviewEndpoint() { return null; @@ -124,7 +122,6 @@ const MESSAGE_TYPE_LIST = [ "PBNEWTAB_MESSAGE_REQUEST", "DOORHANGER_TELEMETRY", "TOOLBAR_BADGE_TELEMETRY", - "TOOLBAR_PANEL_TELEMETRY", "MOMENTS_PAGE_TELEMETRY", "INFOBAR_TELEMETRY", "SPOTLIGHT_TELEMETRY", @@ -142,9 +139,7 @@ const MESSAGE_TYPE_LIST = [ "EVALUATE_JEXL_EXPRESSION", "EXPIRE_QUERY_CACHE", "FORCE_ATTRIBUTION", - "FORCE_WHATSNEW_PANEL", "FORCE_PRIVATE_BROWSING_WINDOW", - "CLOSE_WHATSNEW_PANEL", "OVERRIDE_MESSAGE", "MODIFY_MESSAGE_JSON", "RESET_PROVIDER_PREF", @@ -181,6 +176,8 @@ __webpack_require__.r(__webpack_exports__); * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +// This file is accessed from both content and system scopes. + const MAIN_MESSAGE_TYPE = "ActivityStream:Main"; const CONTENT_MESSAGE_TYPE = "ActivityStream:Content"; const PRELOAD_MESSAGE_TYPE = "ActivityStream:PreloadedBrowser"; @@ -337,6 +334,7 @@ for (const type of [ "UPDATE_PINNED_SEARCH_SHORTCUTS", "UPDATE_SEARCH_SHORTCUTS", "UPDATE_SECTION_PREFS", + "WALLPAPERS_SET", "WEBEXT_CLICK", "WEBEXT_DISMISS", ]) { @@ -550,8 +548,11 @@ function DiscoveryStreamLoadedContent( return importContext === UI_CODE ? AlsoToMain(action) : action; } -function SetPref(name, value, importContext = globalImportContext) { - const action = { type: actionTypes.SET_PREF, data: { name, value } }; +function SetPref(prefName, value, importContext = globalImportContext) { + const action = { + type: actionTypes.SET_PREF, + data: { name: prefName, value }, + }; return importContext === UI_CODE ? AlsoToMain(action) : action; } @@ -961,7 +962,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ ToggleMessageJSON: () => (/* binding */ ToggleMessageJSON), /* harmony export */ TogglePrefCheckbox: () => (/* binding */ TogglePrefCheckbox), /* harmony export */ ToggleStoryButton: () => (/* binding */ ToggleStoryButton), -/* harmony export */ renderASRouterAdmin: () => (/* binding */ renderASRouterAdmin) +/* harmony export */ renderASRouterAdmin: () => (/* binding */ renderASRouterAdmin), +/* harmony export */ toBinary: () => (/* binding */ toBinary) /* harmony export */ }); /* harmony import */ var _asrouter_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); @@ -985,6 +987,16 @@ function _extends() { _extends = Object.assign ? Object.assign.bind() : function const Row = props => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", _extends({ className: "message-item" }, props), props.children); + +// Convert a UTF-8 string to a string in which only one byte of each +// 16-bit unit is occupied. This is necessary to comply with `btoa` API constraints. +function toBinary(string) { + const codeUnits = new Uint16Array(string.length); + for (let i = 0; i < codeUnits.length; i++) { + codeUnits[i] = string.charCodeAt(i); + } + return btoa(String.fromCharCode(...Array.from(new Uint8Array(codeUnits.buffer)))); +} function relativeTime(timestamp) { if (!timestamp) { return ""; @@ -1427,7 +1439,7 @@ class ASRouterAdminInner extends (react__WEBPACK_IMPORTED_MODULE_1___default().P className: "button modify", onClick: () => this.modifyJson(msg) }, "Modify"), aboutMessagePreviewSupported ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(_CopyButton__WEBPACK_IMPORTED_MODULE_4__.CopyButton, { - transformer: text => `about:messagepreview?json=${encodeURIComponent(btoa(text))}`, + transformer: text => `about:messagepreview?json=${encodeURIComponent(toBinary(text))}`, label: "Share", copiedLabel: "Copied!", inputSelector: `#${msg.id}-textarea`, diff --git a/browser/components/asrouter/docs/targeting-attributes.md b/browser/components/asrouter/docs/targeting-attributes.md index 89c5a6b6c6..b0049a4f1b 100644 --- a/browser/components/asrouter/docs/targeting-attributes.md +++ b/browser/components/asrouter/docs/targeting-attributes.md @@ -44,7 +44,6 @@ Please note that some targeting attributes require stricter controls on the tele * [isFxASignedIn](#isFxASignedIn) * [isMajorUpgrade](#ismajorupgrade) * [isRTAMO](#isrtamo) -* [isWhatsNewPanelEnabled](#iswhatsnewpanelenabled) * [launchOnLoginEnabled](#launchonloginenabled) * [locale](#locale) * [localeLanguageCode](#localelanguagecode) @@ -630,16 +629,6 @@ Boolean pref that gets set the first time the user opens the FxA toolbar panel declare const hasAccessedFxAPanel: boolean; ``` -### `isWhatsNewPanelEnabled` - -Boolean pref that controls if the What's New panel feature is enabled - -#### Definition - -```ts -declare const isWhatsNewPanelEnabled: boolean; -``` - ### `totalBlockedCount` Total number of events from the content blocking database diff --git a/browser/components/asrouter/modules/ASRouter.sys.mjs b/browser/components/asrouter/modules/ASRouter.sys.mjs index e46c57f685..b36a9023e1 100644 --- a/browser/components/asrouter/modules/ASRouter.sys.mjs +++ b/browser/components/asrouter/modules/ASRouter.sys.mjs @@ -55,7 +55,6 @@ ChromeUtils.defineESModuleGetters(lazy, { Spotlight: "resource:///modules/asrouter/Spotlight.sys.mjs", ToastNotification: "resource:///modules/asrouter/ToastNotification.sys.mjs", ToolbarBadgeHub: "resource:///modules/asrouter/ToolbarBadgeHub.sys.mjs", - ToolbarPanelHub: "resource:///modules/asrouter/ToolbarPanelHub.sys.mjs", }); XPCOMUtils.defineLazyServiceGetters(lazy, { @@ -67,7 +66,7 @@ ChromeUtils.defineLazyGetter(lazy, "log", () => { ); return new Logger("ASRouter"); }); -import { actionCreators as ac } from "resource://activity-stream/common/Actions.sys.mjs"; +import { actionCreators as ac } from "resource://activity-stream/common/Actions.mjs"; import { MESSAGING_EXPERIMENTS_DEFAULT_FEATURES } from "resource:///modules/asrouter/MessagingExperimentConstants.sys.mjs"; import { CFRMessageProvider } from "resource:///modules/asrouter/CFRMessageProvider.sys.mjs"; import { OnboardingMessageProvider } from "resource:///modules/asrouter/OnboardingMessageProvider.sys.mjs"; @@ -620,7 +619,6 @@ export class _ASRouter { this._onLocaleChanged = this._onLocaleChanged.bind(this); this.isUnblockedMessage = this.isUnblockedMessage.bind(this); this.unblockAll = this.unblockAll.bind(this); - this.forceWNPanel = this.forceWNPanel.bind(this); this._onExperimentEnrollmentsUpdated = this._onExperimentEnrollmentsUpdated.bind(this); this.forcePBWindow = this.forcePBWindow.bind(this); @@ -995,10 +993,6 @@ export class _ASRouter { unblockMessageById: this.unblockMessageById, sendTelemetry: this.sendTelemetry, }); - lazy.ToolbarPanelHub.init(this.waitForInitialized, { - getMessages: this.handleMessageRequest, - sendTelemetry: this.sendTelemetry, - }); lazy.MomentsPageHub.init(this.waitForInitialized, { handleMessageRequest: this.handleMessageRequest, addImpression: this.addImpression, @@ -1055,7 +1049,6 @@ export class _ASRouter { lazy.ASRouterPreferences.removeListener(this.onPrefChange); lazy.ASRouterPreferences.uninit(); - lazy.ToolbarPanelHub.uninit(); lazy.ToolbarBadgeHub.uninit(); lazy.MomentsPageHub.uninit(); @@ -1309,16 +1302,6 @@ export class _ASRouter { return true; } - async _extraTemplateStrings(originalMessage) { - let extraTemplateStrings; - let localProvider = this._findProvider(originalMessage.provider); - if (localProvider && localProvider.getExtraAttributes) { - extraTemplateStrings = await localProvider.getExtraAttributes(); - } - - return extraTemplateStrings; - } - _findProvider(providerID) { return this._localProviders[ this.state.providers.find(i => i.id === providerID).localProvider @@ -1346,11 +1329,6 @@ export class _ASRouter { } switch (message.template) { - case "whatsnew_panel_message": - if (force) { - lazy.ToolbarPanelHub.forceShowMessage(browser, message); - } - break; case "cfr_doorhanger": case "milestone_message": if (force) { @@ -2005,29 +1983,6 @@ export class _ASRouter { ); } - async forceWNPanel(browser) { - let win = browser.ownerGlobal; - await lazy.ToolbarPanelHub.enableToolbarButton(); - - win.PanelUI.showSubView( - "PanelUI-whatsNew", - win.document.getElementById("whats-new-menu-button") - ); - - let panel = win.document.getElementById("customizationui-widget-panel"); - // Set the attribute to keep the panel open - panel.setAttribute("noautohide", true); - } - - async closeWNPanel(browser) { - let win = browser.ownerGlobal; - let panel = win.document.getElementById("customizationui-widget-panel"); - // Set the attribute to allow the panel to close - panel.setAttribute("noautohide", false); - // Removing the button is enough to close the panel. - await lazy.ToolbarPanelHub._hideToolbarButton(win); - } - async _onExperimentEnrollmentsUpdated() { const experimentProvider = this.state.providers.find( p => p.id === "messaging-experiments" diff --git a/browser/components/asrouter/modules/ASRouterParentProcessMessageHandler.sys.mjs b/browser/components/asrouter/modules/ASRouterParentProcessMessageHandler.sys.mjs index c2f5fcd884..8aa4d7dbc9 100644 --- a/browser/components/asrouter/modules/ASRouterParentProcessMessageHandler.sys.mjs +++ b/browser/components/asrouter/modules/ASRouterParentProcessMessageHandler.sys.mjs @@ -4,7 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { ASRouterPreferences } from "resource:///modules/asrouter/ASRouterPreferences.sys.mjs"; -import { MESSAGE_TYPE_HASH as msg } from "resource:///modules/asrouter/ActorConstants.sys.mjs"; +import { MESSAGE_TYPE_HASH as msg } from "resource:///modules/asrouter/ActorConstants.mjs"; export class ASRouterParentProcessMessageHandler { constructor({ @@ -27,7 +27,6 @@ export class ASRouterParentProcessMessageHandler { switch (type) { case msg.INFOBAR_TELEMETRY: case msg.TOOLBAR_BADGE_TELEMETRY: - case msg.TOOLBAR_PANEL_TELEMETRY: case msg.MOMENTS_PAGE_TELEMETRY: case msg.DOORHANGER_TELEMETRY: case msg.SPOTLIGHT_TELEMETRY: @@ -128,12 +127,6 @@ export class ASRouterParentProcessMessageHandler { case msg.FORCE_PRIVATE_BROWSING_WINDOW: { return this._router.forcePBWindow(browser, data.message); } - case msg.FORCE_WHATSNEW_PANEL: { - return this._router.forceWNPanel(browser); - } - case msg.CLOSE_WHATSNEW_PANEL: { - return this._router.closeWNPanel(browser); - } case msg.MODIFY_MESSAGE_JSON: { return this._router.routeCFRMessage(data.content, browser, data, true); } diff --git a/browser/components/asrouter/modules/ASRouterTargeting.sys.mjs b/browser/components/asrouter/modules/ASRouterTargeting.sys.mjs index d76b303fc6..9773eda270 100644 --- a/browser/components/asrouter/modules/ASRouterTargeting.sys.mjs +++ b/browser/components/asrouter/modules/ASRouterTargeting.sys.mjs @@ -74,12 +74,6 @@ XPCOMUtils.defineLazyPreferenceGetter( ); XPCOMUtils.defineLazyPreferenceGetter( lazy, - "isWhatsNewPanelEnabled", - "browser.messaging-system.whatsNewPanel.enabled", - false -); -XPCOMUtils.defineLazyPreferenceGetter( - lazy, "hasAccessedFxAPanel", "identity.fxaccounts.toolbar.accessed", false @@ -704,9 +698,6 @@ const TargetingGetters = { get hasAccessedFxAPanel() { return lazy.hasAccessedFxAPanel; }, - get isWhatsNewPanelEnabled() { - return lazy.isWhatsNewPanelEnabled; - }, get userPrefs() { return { cfrFeatures: lazy.cfrFeaturesUserPref, diff --git a/browser/components/asrouter/modules/ActorConstants.sys.mjs b/browser/components/asrouter/modules/ActorConstants.mjs index 4c996552ab..c1c18e006e 100644 --- a/browser/components/asrouter/modules/ActorConstants.sys.mjs +++ b/browser/components/asrouter/modules/ActorConstants.mjs @@ -12,7 +12,6 @@ export const MESSAGE_TYPE_LIST = [ "PBNEWTAB_MESSAGE_REQUEST", "DOORHANGER_TELEMETRY", "TOOLBAR_BADGE_TELEMETRY", - "TOOLBAR_PANEL_TELEMETRY", "MOMENTS_PAGE_TELEMETRY", "INFOBAR_TELEMETRY", "SPOTLIGHT_TELEMETRY", @@ -30,9 +29,7 @@ export const MESSAGE_TYPE_LIST = [ "EVALUATE_JEXL_EXPRESSION", "EXPIRE_QUERY_CACHE", "FORCE_ATTRIBUTION", - "FORCE_WHATSNEW_PANEL", "FORCE_PRIVATE_BROWSING_WINDOW", - "CLOSE_WHATSNEW_PANEL", "OVERRIDE_MESSAGE", "MODIFY_MESSAGE_JSON", "RESET_PROVIDER_PREF", diff --git a/browser/components/asrouter/modules/MomentsPageHub.sys.mjs b/browser/components/asrouter/modules/MomentsPageHub.sys.mjs index 84fee3b517..3a59e9d450 100644 --- a/browser/components/asrouter/modules/MomentsPageHub.sys.mjs +++ b/browser/components/asrouter/modules/MomentsPageHub.sys.mjs @@ -165,7 +165,7 @@ export class _MomentsPageHub { } /** - * ToolbarBadgeHub - singleton instance of _ToolbarBadgeHub that can initiate + * MomentsPageHub - singleton instance of _MomentsPageHub that can initiate * message requests and render messages. */ export const MomentsPageHub = new _MomentsPageHub(); diff --git a/browser/components/asrouter/modules/OnboardingMessageProvider.sys.mjs b/browser/components/asrouter/modules/OnboardingMessageProvider.sys.mjs index ceded6b755..3cfbbb3f34 100644 --- a/browser/components/asrouter/modules/OnboardingMessageProvider.sys.mjs +++ b/browser/components/asrouter/modules/OnboardingMessageProvider.sys.mjs @@ -1210,6 +1210,106 @@ const BASE_MESSAGES = () => [ id: "defaultBrowserCheck", }, }, + { + id: "SET_DEFAULT_BROWSER_GUIDANCE_NOTIFICATION_WIN10", + template: "toast_notification", + content: { + title: { + string_id: "default-browser-guidance-notification-title", + }, + body: { + string_id: + "default-browser-guidance-notification-body-instruction-win10", + }, + launch_action: { + type: "OPEN_URL", + data: { + args: "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/win-set-firefox-default-browser", + where: "tabshifted", + }, + }, + requireInteraction: true, + actions: [ + { + action: "info-page", + title: { + string_id: "default-browser-guidance-notification-info-page", + }, + launch_action: { + type: "OPEN_URL", + data: { + args: "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/win-set-firefox-default-browser", + where: "tabshifted", + }, + }, + }, + { + action: "dismiss", + title: { + string_id: "default-browser-guidance-notification-dismiss", + }, + windowsSystemActivationType: true, + }, + ], + tag: "set-default-guidance-notification", + }, + // Both Windows 10 and 11 return `os.windowsVersion == 10.0`. We limit to + // only Windows 10 with `os.windowsBuildNumber < 22000`. We need this due to + // Windows 10 and 11 having substantively different UX for Windows Settings. + targeting: + "os.isWindows && os.windowsVersion >= 10.0 && os.windowsBuildNumber < 22000", + trigger: { id: "deeplinkedToWindowsSettingsUI" }, + }, + { + id: "SET_DEFAULT_BROWSER_GUIDANCE_NOTIFICATION_WIN11", + template: "toast_notification", + content: { + title: { + string_id: "default-browser-guidance-notification-title", + }, + body: { + string_id: + "default-browser-guidance-notification-body-instruction-win11", + }, + launch_action: { + type: "OPEN_URL", + data: { + args: "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/win-set-firefox-default-browser", + where: "tabshifted", + }, + }, + requireInteraction: true, + actions: [ + { + action: "info-page", + title: { + string_id: "default-browser-guidance-notification-info-page", + }, + launch_action: { + type: "OPEN_URL", + data: { + args: "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/win-set-firefox-default-browser", + where: "tabshifted", + }, + }, + }, + { + action: "dismiss", + title: { + string_id: "default-browser-guidance-notification-dismiss", + }, + windowsSystemActivationType: true, + }, + ], + tag: "set-default-guidance-notification", + }, + // Both Windows 10 and 11 return `os.windowsVersion == 10.0`. We limit to + // only Windows 11 with `os.windowsBuildNumber >= 22000`. We need this due to + // Windows 10 and 11 having substantively different UX for Windows Settings. + targeting: + "os.isWindows && os.windowsVersion >= 10.0 && os.windowsBuildNumber >= 22000", + trigger: { id: "deeplinkedToWindowsSettingsUI" }, + }, ]; // Eventually, move Feature Callout messages to their own provider diff --git a/browser/components/asrouter/modules/PanelTestProvider.sys.mjs b/browser/components/asrouter/modules/PanelTestProvider.sys.mjs index 7a7ff1e1fc..5180e2e6a2 100644 --- a/browser/components/asrouter/modules/PanelTestProvider.sys.mjs +++ b/browser/components/asrouter/modules/PanelTestProvider.sys.mjs @@ -20,134 +20,6 @@ const MESSAGES = () => [ trigger: { id: "momentsUpdate" }, }, { - id: "WHATS_NEW_FINGERPRINTER_COUNTER_ALT", - template: "whatsnew_panel_message", - order: 6, - content: { - bucket_id: "WHATS_NEW_72", - published_date: 1574776601000, - title: "Title", - icon_url: - "chrome://activity-stream/content/data/content/assets/protection-report-icon.png", - icon_alt: { string_id: "cfr-badge-reader-label-newfeature" }, - body: "Message body", - link_text: "Click here", - cta_url: "about:blank", - cta_type: "OPEN_PROTECTION_REPORT", - }, - targeting: `firefoxVersion >= 72`, - trigger: { id: "whatsNewPanelOpened" }, - }, - { - id: "WHATS_NEW_70_1", - template: "whatsnew_panel_message", - order: 3, - content: { - bucket_id: "WHATS_NEW_70_1", - published_date: 1560969794394, - title: "Protection Is Our Focus", - icon_url: - "chrome://activity-stream/content/data/content/assets/whatsnew-send-icon.png", - icon_alt: "Firefox Send Logo", - body: "The New Enhanced Tracking Protection, gives you the best level of protection and performance. Discover how this version is the safest version of firefox ever made.", - cta_url: "https://blog.mozilla.org/", - cta_type: "OPEN_URL", - }, - targeting: `firefoxVersion > 69`, - trigger: { id: "whatsNewPanelOpened" }, - }, - { - id: "WHATS_NEW_70_2", - template: "whatsnew_panel_message", - order: 1, - content: { - bucket_id: "WHATS_NEW_70_1", - published_date: 1560969794394, - title: "Another thing new in Firefox 70", - body: "The New Enhanced Tracking Protection, gives you the best level of protection and performance. Discover how this version is the safest version of firefox ever made.", - link_text: "Learn more on our blog", - cta_url: "https://blog.mozilla.org/", - cta_type: "OPEN_URL", - }, - targeting: `firefoxVersion > 69`, - trigger: { id: "whatsNewPanelOpened" }, - }, - { - id: "WHATS_NEW_SEARCH_SHORTCUTS_84", - template: "whatsnew_panel_message", - order: 2, - content: { - bucket_id: "WHATS_NEW_SEARCH_SHORTCUTS_84", - published_date: 1560969794394, - title: "Title", - icon_url: "chrome://global/skin/icons/check.svg", - icon_alt: "", - body: "Message content", - cta_url: - "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/search-shortcuts", - cta_type: "OPEN_URL", - link_text: "Click here", - }, - targeting: "firefoxVersion >= 84", - trigger: { - id: "whatsNewPanelOpened", - }, - }, - { - id: "WHATS_NEW_PIONEER_82", - template: "whatsnew_panel_message", - order: 1, - content: { - bucket_id: "WHATS_NEW_PIONEER_82", - published_date: 1603152000000, - title: "Put your data to work for a better internet", - body: "Contribute your data to Mozilla's Pioneer program to help researchers understand pressing technology issues like misinformation, data privacy, and ethical AI.", - cta_url: "about:blank", - cta_where: "tab", - cta_type: "OPEN_ABOUT_PAGE", - link_text: "Join Pioneer", - }, - targeting: "firefoxVersion >= 82", - trigger: { - id: "whatsNewPanelOpened", - }, - }, - { - id: "WHATS_NEW_MEDIA_SESSION_82", - template: "whatsnew_panel_message", - order: 3, - content: { - bucket_id: "WHATS_NEW_MEDIA_SESSION_82", - published_date: 1603152000000, - title: "Title", - body: "Message content", - cta_url: - "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/media-keyboard-control", - cta_type: "OPEN_URL", - link_text: "Click here", - }, - targeting: "firefoxVersion >= 82", - trigger: { - id: "whatsNewPanelOpened", - }, - }, - { - id: "WHATS_NEW_69_1", - template: "whatsnew_panel_message", - order: 1, - content: { - bucket_id: "WHATS_NEW_69_1", - published_date: 1557346235089, - title: "Something new in Firefox 69", - body: "The New Enhanced Tracking Protection, gives you the best level of protection and performance. Discover how this version is the safest version of firefox ever made.", - link_text: "Learn more on our blog", - cta_url: "https://blog.mozilla.org/", - cta_type: "OPEN_URL", - }, - targeting: `firefoxVersion > 68`, - trigger: { id: "whatsNewPanelOpened" }, - }, - { id: "PERSONALIZED_CFR_MESSAGE", template: "cfr_doorhanger", groups: ["cfr"], diff --git a/browser/components/asrouter/modules/ToolbarBadgeHub.sys.mjs b/browser/components/asrouter/modules/ToolbarBadgeHub.sys.mjs index 57fd104f19..36f7ca5005 100644 --- a/browser/components/asrouter/modules/ToolbarBadgeHub.sys.mjs +++ b/browser/components/asrouter/modules/ToolbarBadgeHub.sys.mjs @@ -10,7 +10,6 @@ ChromeUtils.defineESModuleGetters(lazy, { clearTimeout: "resource://gre/modules/Timer.sys.mjs", requestIdleCallback: "resource://gre/modules/Timer.sys.mjs", setTimeout: "resource://gre/modules/Timer.sys.mjs", - ToolbarPanelHub: "resource:///modules/asrouter/ToolbarPanelHub.sys.mjs", }); let notificationsByWindow = new WeakMap(); @@ -19,9 +18,6 @@ export class _ToolbarBadgeHub { constructor() { this.id = "toolbar-badge-hub"; this.state = {}; - this.prefs = { - WHATSNEW_TOOLBAR_PANEL: "browser.messaging-system.whatsNewPanel.enabled", - }; this.removeAllNotifications = this.removeAllNotifications.bind(this); this.removeToolbarNotification = this.removeToolbarNotification.bind(this); this.addToolbarNotification = this.addToolbarNotification.bind(this); @@ -62,34 +58,12 @@ export class _ToolbarBadgeHub { triggerId: "toolbarBadgeUpdate", template: "toolbar_badge", }); - // Listen for pref changes that could trigger new badges - Services.prefs.addObserver(this.prefs.WHATSNEW_TOOLBAR_PANEL, this); - } - - observe(aSubject, aTopic, aPrefName) { - switch (aPrefName) { - case this.prefs.WHATSNEW_TOOLBAR_PANEL: - this.messageRequest({ - triggerId: "toolbarBadgeUpdate", - template: "toolbar_badge", - }); - break; - } } maybeInsertFTL(win) { win.MozXULElement.insertFTLIfNeeded("browser/newtab/asrouter.ftl"); } - executeAction({ id }) { - switch (id) { - case "show-whatsnew-button": - lazy.ToolbarPanelHub.enableToolbarButton(); - lazy.ToolbarPanelHub.enableAppmenuButton(); - break; - } - } - _clearBadgeTimeout() { if (this.state.showBadgeTimeoutId) { lazy.clearTimeout(this.state.showBadgeTimeoutId); @@ -153,9 +127,6 @@ export class _ToolbarBadgeHub { addToolbarNotification(win, message) { const document = win.browser.ownerDocument; - if (message.content.action) { - this.executeAction({ ...message.content.action, message_id: message.id }); - } let toolbarbutton = document.getElementById(message.content.target); if (toolbarbutton) { const badge = toolbarbutton.querySelector(".toolbarbutton-badge"); @@ -211,12 +182,6 @@ export class _ToolbarBadgeHub { } registerBadgeToAllWindows(message) { - if (message.template === "update_action") { - this.executeAction({ ...message.content.action, message_id: message.id }); - // No badge to set only an action to execute - return; - } - lazy.EveryWindow.registerCallback( this.id, win => { @@ -297,7 +262,6 @@ export class _ToolbarBadgeHub { this.state = {}; this._initialized = false; notificationsByWindow = new WeakMap(); - Services.prefs.removeObserver(this.prefs.WHATSNEW_TOOLBAR_PANEL, this); } } diff --git a/browser/components/asrouter/modules/ToolbarPanelHub.sys.mjs b/browser/components/asrouter/modules/ToolbarPanelHub.sys.mjs deleted file mode 100644 index 519bca8a89..0000000000 --- a/browser/components/asrouter/modules/ToolbarPanelHub.sys.mjs +++ /dev/null @@ -1,544 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const lazy = {}; - -// We use importESModule here instead of static import so that -// the Karma test environment won't choke on this module. This -// is because the Karma test environment already stubs out -// XPCOMUtils. That environment overrides importESModule to be a no-op -// (which can't be done for a static import statement). - -// eslint-disable-next-line mozilla/use-static-import -const { XPCOMUtils } = ChromeUtils.importESModule( - "resource://gre/modules/XPCOMUtils.sys.mjs" -); - -ChromeUtils.defineESModuleGetters(lazy, { - EveryWindow: "resource:///modules/EveryWindow.sys.mjs", - PanelMultiView: "resource:///modules/PanelMultiView.sys.mjs", - PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", - SpecialMessageActions: - "resource://messaging-system/lib/SpecialMessageActions.sys.mjs", - RemoteL10n: "resource:///modules/asrouter/RemoteL10n.sys.mjs", -}); - -XPCOMUtils.defineLazyServiceGetter( - lazy, - "TrackingDBService", - "@mozilla.org/tracking-db-service;1", - "nsITrackingDBService" -); - -const idToTextMap = new Map([ - [Ci.nsITrackingDBService.TRACKERS_ID, "trackerCount"], - [Ci.nsITrackingDBService.TRACKING_COOKIES_ID, "cookieCount"], - [Ci.nsITrackingDBService.CRYPTOMINERS_ID, "cryptominerCount"], - [Ci.nsITrackingDBService.FINGERPRINTERS_ID, "fingerprinterCount"], - [Ci.nsITrackingDBService.SOCIAL_ID, "socialCount"], -]); - -const WHATSNEW_ENABLED_PREF = "browser.messaging-system.whatsNewPanel.enabled"; -const PROTECTIONS_PANEL_INFOMSG_PREF = - "browser.protections_panel.infoMessage.seen"; - -const TOOLBAR_BUTTON_ID = "whats-new-menu-button"; -const APPMENU_BUTTON_ID = "appMenu-whatsnew-button"; - -const BUTTON_STRING_ID = "cfr-whatsnew-button"; -const WHATS_NEW_PANEL_SELECTOR = "PanelUI-whatsNew-message-container"; - -export class _ToolbarPanelHub { - constructor() { - this.triggerId = "whatsNewPanelOpened"; - this._showAppmenuButton = this._showAppmenuButton.bind(this); - this._hideAppmenuButton = this._hideAppmenuButton.bind(this); - this._showToolbarButton = this._showToolbarButton.bind(this); - this._hideToolbarButton = this._hideToolbarButton.bind(this); - - this.state = {}; - this._initialized = false; - } - - async init(waitForInitialized, { getMessages, sendTelemetry }) { - if (this._initialized) { - return; - } - - this._initialized = true; - this._getMessages = getMessages; - this._sendTelemetry = sendTelemetry; - // Wait for ASRouter messages to become available in order to know - // if we can show the What's New panel - await waitForInitialized; - // Enable the application menu button so that the user can access - // the panel outside of the toolbar button - await this.enableAppmenuButton(); - - this.state = { - protectionPanelMessageSeen: Services.prefs.getBoolPref( - PROTECTIONS_PANEL_INFOMSG_PREF, - false - ), - }; - } - - uninit() { - this._initialized = false; - lazy.EveryWindow.unregisterCallback(TOOLBAR_BUTTON_ID); - lazy.EveryWindow.unregisterCallback(APPMENU_BUTTON_ID); - } - - get messages() { - return this._getMessages({ - template: "whatsnew_panel_message", - triggerId: "whatsNewPanelOpened", - returnAll: true, - }); - } - - toggleWhatsNewPref(event) { - // Checkbox onclick handler gets called before the checkbox state gets toggled, - // so we have to call it with the opposite value. - let newValue = !event.target.checked; - Services.prefs.setBoolPref(WHATSNEW_ENABLED_PREF, newValue); - - this.sendUserEventTelemetry( - event.target.ownerGlobal, - "WNP_PREF_TOGGLE", - // Message id is not applicable in this case, the notification state - // is not related to a particular message - { id: "n/a" }, - { value: { prefValue: newValue } } - ); - } - - maybeInsertFTL(win) { - win.MozXULElement.insertFTLIfNeeded("browser/newtab/asrouter.ftl"); - win.MozXULElement.insertFTLIfNeeded("toolkit/branding/brandings.ftl"); - win.MozXULElement.insertFTLIfNeeded("toolkit/branding/accounts.ftl"); - } - - maybeLoadCustomElement(win) { - if (!win.customElements.get("remote-text")) { - Services.scriptloader.loadSubScript( - "resource://activity-stream/data/custom-elements/paragraph.js", - win - ); - } - } - - // Turns on the Appmenu (hamburger menu) button for all open windows and future windows. - async enableAppmenuButton() { - if ((await this.messages).length) { - lazy.EveryWindow.registerCallback( - APPMENU_BUTTON_ID, - this._showAppmenuButton, - this._hideAppmenuButton - ); - } - } - - // Removes the button from the Appmenu. - // Only used in tests. - disableAppmenuButton() { - lazy.EveryWindow.unregisterCallback(APPMENU_BUTTON_ID); - } - - // Turns on the Toolbar button for all open windows and future windows. - async enableToolbarButton() { - if ((await this.messages).length) { - lazy.EveryWindow.registerCallback( - TOOLBAR_BUTTON_ID, - this._showToolbarButton, - this._hideToolbarButton - ); - } - } - - // When the panel is hidden we want to run some cleanup - _onPanelHidden(win) { - const panelContainer = win.document.getElementById( - "customizationui-widget-panel" - ); - // When the panel is hidden we want to remove any toolbar buttons that - // might have been added as an entry point to the panel - const removeToolbarButton = () => { - lazy.EveryWindow.unregisterCallback(TOOLBAR_BUTTON_ID); - }; - if (!panelContainer) { - return; - } - panelContainer.addEventListener("popuphidden", removeToolbarButton, { - once: true, - }); - } - - // Newer messages first and use `order` field to decide between messages - // with the same timestamp - _sortWhatsNewMessages(m1, m2) { - // Sort by published_date in descending order. - if (m1.content.published_date === m2.content.published_date) { - // Ascending order - return m1.order - m2.order; - } - if (m1.content.published_date > m2.content.published_date) { - return -1; - } - return 1; - } - - // Render what's new messages into the panel. - async renderMessages(win, doc, containerId, options = {}) { - // Set the checked status of the footer checkbox - let value = Services.prefs.getBoolPref(WHATSNEW_ENABLED_PREF); - let checkbox = win.document.getElementById("panelMenu-toggleWhatsNew"); - - checkbox.checked = value; - - this.maybeLoadCustomElement(win); - const messages = - (options.force && options.messages) || - (await this.messages).sort(this._sortWhatsNewMessages); - const container = lazy.PanelMultiView.getViewNode(doc, containerId); - - if (messages) { - // Targeting attribute state might have changed making new messages - // available and old messages invalid, we need to refresh - this.removeMessages(win, containerId); - let previousDate = 0; - // Get and store any variable part of the message content - this.state.contentArguments = await this._contentArguments(); - for (let message of messages) { - container.appendChild( - this._createMessageElements(win, doc, message, previousDate) - ); - previousDate = message.content.published_date; - } - } - - this._onPanelHidden(win); - - // Panel impressions are not associated with one particular message - // but with a set of messages. We concatenate message ids and send them - // back for every impression. - const eventId = { - id: messages - .map(({ id }) => id) - .sort() - .join(","), - }; - // Check `mainview` attribute to determine if the panel is shown as a - // subview (inside the application menu) or as a toolbar dropdown. - // https://searchfox.org/mozilla-central/rev/07f7390618692fa4f2a674a96b9b677df3a13450/browser/components/customizableui/PanelMultiView.jsm#1268 - const mainview = win.PanelUI.whatsNewPanel.hasAttribute("mainview"); - this.sendUserEventTelemetry(win, "IMPRESSION", eventId, { - value: { view: mainview ? "toolbar_dropdown" : "application_menu" }, - }); - } - - removeMessages(win, containerId) { - const doc = win.document; - const messageNodes = lazy.PanelMultiView.getViewNode( - doc, - containerId - ).querySelectorAll(".whatsNew-message"); - for (const messageNode of messageNodes) { - messageNode.remove(); - } - } - - /** - * Dispatch the action defined in the message and user telemetry event. - */ - _dispatchUserAction(win, message) { - let url; - try { - // Set platform specific path variables for SUMO articles - url = Services.urlFormatter.formatURL(message.content.cta_url); - } catch (e) { - console.error(e); - url = message.content.cta_url; - } - lazy.SpecialMessageActions.handleAction( - { - type: message.content.cta_type, - data: { - args: url, - where: message.content.cta_where || "tabshifted", - }, - }, - win.browser - ); - - this.sendUserEventTelemetry(win, "CLICK", message); - } - - /** - * Attach event listener to dispatch message defined action. - */ - _attachCommandListener(win, element, message) { - // Add event listener for `mouseup` not to overlap with the - // `mousedown` & `click` events dispatched from PanelMultiView.sys.mjs - // https://searchfox.org/mozilla-central/rev/7531325c8660cfa61bf71725f83501028178cbb9/browser/components/customizableui/PanelMultiView.jsm#1830-1837 - element.addEventListener("mouseup", () => { - this._dispatchUserAction(win, message); - }); - element.addEventListener("keyup", e => { - if (e.key === "Enter" || e.key === " ") { - this._dispatchUserAction(win, message); - } - }); - } - - _createMessageElements(win, doc, message, previousDate) { - const { content } = message; - const messageEl = lazy.RemoteL10n.createElement(doc, "div"); - messageEl.classList.add("whatsNew-message"); - - // Only render date if it is different from the one rendered before. - if (content.published_date !== previousDate) { - messageEl.appendChild( - lazy.RemoteL10n.createElement(doc, "p", { - classList: "whatsNew-message-date", - content: new Date(content.published_date).toLocaleDateString( - "default", - { - month: "long", - day: "numeric", - year: "numeric", - } - ), - }) - ); - } - - const wrapperEl = lazy.RemoteL10n.createElement(doc, "div"); - wrapperEl.doCommand = () => this._dispatchUserAction(win, message); - wrapperEl.classList.add("whatsNew-message-body"); - messageEl.appendChild(wrapperEl); - - if (content.icon_url) { - wrapperEl.classList.add("has-icon"); - const iconEl = lazy.RemoteL10n.createElement(doc, "img"); - iconEl.src = content.icon_url; - iconEl.classList.add("whatsNew-message-icon"); - if (content.icon_alt && content.icon_alt.string_id) { - doc.l10n.setAttributes(iconEl, content.icon_alt.string_id); - } else { - iconEl.setAttribute("alt", content.icon_alt); - } - wrapperEl.appendChild(iconEl); - } - - wrapperEl.appendChild(this._createMessageContent(win, doc, content)); - - if (content.link_text) { - const anchorEl = lazy.RemoteL10n.createElement(doc, "a", { - classList: "text-link", - content: content.link_text, - }); - anchorEl.doCommand = () => this._dispatchUserAction(win, message); - wrapperEl.appendChild(anchorEl); - } - - // Attach event listener on entire message container - this._attachCommandListener(win, messageEl, message); - - return messageEl; - } - - /** - * Return message title (optional subtitle) and body - */ - _createMessageContent(win, doc, content) { - const wrapperEl = new win.DocumentFragment(); - - wrapperEl.appendChild( - lazy.RemoteL10n.createElement(doc, "h2", { - classList: "whatsNew-message-title", - content: content.title, - attributes: this.state.contentArguments, - }) - ); - - wrapperEl.appendChild( - lazy.RemoteL10n.createElement(doc, "p", { - content: content.body, - classList: "whatsNew-message-content", - attributes: this.state.contentArguments, - }) - ); - - return wrapperEl; - } - - _createHeroElement(win, doc, message) { - this.maybeLoadCustomElement(win); - - const messageEl = lazy.RemoteL10n.createElement(doc, "div"); - messageEl.setAttribute("id", "protections-popup-message"); - messageEl.classList.add("whatsNew-hero-message"); - const wrapperEl = lazy.RemoteL10n.createElement(doc, "div"); - wrapperEl.classList.add("whatsNew-message-body"); - messageEl.appendChild(wrapperEl); - - wrapperEl.appendChild( - lazy.RemoteL10n.createElement(doc, "h2", { - classList: "whatsNew-message-title", - content: message.content.title, - }) - ); - wrapperEl.appendChild( - lazy.RemoteL10n.createElement(doc, "p", { - classList: "protections-popup-content", - content: message.content.body, - }) - ); - - if (message.content.link_text) { - let linkEl = lazy.RemoteL10n.createElement(doc, "a", { - classList: "text-link", - content: message.content.link_text, - }); - linkEl.disabled = true; - wrapperEl.appendChild(linkEl); - this._attachCommandListener(win, linkEl, message); - } else { - this._attachCommandListener(win, wrapperEl, message); - } - - return messageEl; - } - - async _contentArguments() { - const { defaultEngine } = Services.search; - // Between now and 6 weeks ago - const dateTo = new Date(); - const dateFrom = new Date(dateTo.getTime() - 42 * 24 * 60 * 60 * 1000); - const eventsByDate = await lazy.TrackingDBService.getEventsByDateRange( - dateFrom, - dateTo - ); - // Make sure we set all types of possible values to 0 because they might - // be referenced by fluent strings - let totalEvents = { blockedCount: 0 }; - for (let blockedType of idToTextMap.values()) { - totalEvents[blockedType] = 0; - } - // Count all events in the past 6 weeks. Returns an object with: - // `blockedCount` total number of blocked resources - // {tracker|cookie|social...} breakdown by event type as defined by `idToTextMap` - totalEvents = eventsByDate.reduce((acc, day) => { - const type = day.getResultByName("type"); - const count = day.getResultByName("count"); - acc[idToTextMap.get(type)] = (acc[idToTextMap.get(type)] || 0) + count; - acc.blockedCount += count; - return acc; - }, totalEvents); - return { - // Keys need to match variable names used in asrouter.ftl - // `earliestDate` will be either 6 weeks ago or when tracking recording - // started. Whichever is more recent. - earliestDate: Math.max( - new Date(await lazy.TrackingDBService.getEarliestRecordedDate()), - dateFrom - ), - ...totalEvents, - // Passing in `undefined` as string for the Fluent variable name - // in order to match and select the message that does not require - // the variable. - searchEngineName: defaultEngine ? defaultEngine.name : "undefined", - }; - } - - async _showAppmenuButton(win) { - this.maybeInsertFTL(win); - await this._showElement( - win.browser.ownerDocument, - APPMENU_BUTTON_ID, - BUTTON_STRING_ID - ); - } - - _hideAppmenuButton(win, windowClosed) { - // No need to do something if the window is going away - if (!windowClosed) { - this._hideElement(win.browser.ownerDocument, APPMENU_BUTTON_ID); - } - } - - _showToolbarButton(win) { - const document = win.browser.ownerDocument; - this.maybeInsertFTL(win); - return this._showElement(document, TOOLBAR_BUTTON_ID, BUTTON_STRING_ID); - } - - _hideToolbarButton(win) { - this._hideElement(win.browser.ownerDocument, TOOLBAR_BUTTON_ID); - } - - _showElement(document, id, string_id) { - const el = lazy.PanelMultiView.getViewNode(document, id); - document.l10n.setAttributes(el, string_id); - el.hidden = false; - } - - _hideElement(document, id) { - const el = lazy.PanelMultiView.getViewNode(document, id); - if (el) { - el.hidden = true; - } - } - - _sendPing(ping) { - this._sendTelemetry({ - type: "TOOLBAR_PANEL_TELEMETRY", - data: { action: "whats-new-panel_user_event", ...ping }, - }); - } - - sendUserEventTelemetry(win, event, message, options = {}) { - // Only send pings for non private browsing windows - if ( - win && - !lazy.PrivateBrowsingUtils.isBrowserPrivate( - win.ownerGlobal.gBrowser.selectedBrowser - ) - ) { - this._sendPing({ - message_id: message.id, - event, - event_context: options.value, - }); - } - } - - /** - * @param {object} [browser] MessageChannel target argument as a response to a - * user action. No message is shown if undefined. - * @param {object[]} messages Messages selected from devtools page - */ - forceShowMessage(browser, messages) { - if (!browser) { - return; - } - const win = browser.ownerGlobal; - const doc = browser.ownerDocument; - this.removeMessages(win, WHATS_NEW_PANEL_SELECTOR); - this.renderMessages(win, doc, WHATS_NEW_PANEL_SELECTOR, { - force: true, - messages: Array.isArray(messages) ? messages : [messages], - }); - win.PanelUI.panel.addEventListener("popuphidden", event => - this.removeMessages(event.target.ownerGlobal, WHATS_NEW_PANEL_SELECTOR) - ); - } -} - -/** - * ToolbarPanelHub - singleton instance of _ToolbarPanelHub that can initiate - * message requests and render messages. - */ -export const ToolbarPanelHub = new _ToolbarPanelHub(); diff --git a/browser/components/asrouter/moz.build b/browser/components/asrouter/moz.build index 558ccbeb9b..cf45186619 100644 --- a/browser/components/asrouter/moz.build +++ b/browser/components/asrouter/moz.build @@ -15,7 +15,7 @@ FINAL_TARGET_FILES.actors += [ ] EXTRA_JS_MODULES.asrouter += [ - "modules/ActorConstants.sys.mjs", + "modules/ActorConstants.mjs", "modules/ASRouter.sys.mjs", "modules/ASRouterDefaultConfig.sys.mjs", "modules/ASRouterNewTabHook.sys.mjs", @@ -38,7 +38,6 @@ EXTRA_JS_MODULES.asrouter += [ "modules/Spotlight.sys.mjs", "modules/ToastNotification.sys.mjs", "modules/ToolbarBadgeHub.sys.mjs", - "modules/ToolbarPanelHub.sys.mjs", ] BROWSER_CHROME_MANIFESTS += [ @@ -57,7 +56,6 @@ TESTING_JS_MODULES += [ "content-src/templates/OnboardingMessage/Spotlight.schema.json", "content-src/templates/OnboardingMessage/ToolbarBadgeMessage.schema.json", "content-src/templates/OnboardingMessage/UpdateAction.schema.json", - "content-src/templates/OnboardingMessage/WhatsNewMessage.schema.json", "content-src/templates/PBNewtab/NewtabPromoMessage.schema.json", "content-src/templates/ToastNotification/ToastNotification.schema.json", "tests/InflightAssetsMessageProvider.sys.mjs", diff --git a/browser/components/asrouter/package.json b/browser/components/asrouter/package.json index 17bc8f7364..26c09392c2 100644 --- a/browser/components/asrouter/package.json +++ b/browser/components/asrouter/package.json @@ -60,6 +60,7 @@ "testmc:lint": "npm run lint", "testmc:build": "npm run bundle:admin", "testmc:unit": "karma start karma.mc.config.js", + "testmc:import": "npm run import-rollouts", "tddmc": "karma start karma.mc.config.js --tdd", "debugcoverage": "open logs/coverage/lcov-report/index.html", "lint": "npm-run-all lint:*", diff --git a/browser/components/asrouter/tests/InflightAssetsMessageProvider.sys.mjs b/browser/components/asrouter/tests/InflightAssetsMessageProvider.sys.mjs index e92b210c12..adb14ecc38 100644 --- a/browser/components/asrouter/tests/InflightAssetsMessageProvider.sys.mjs +++ b/browser/components/asrouter/tests/InflightAssetsMessageProvider.sys.mjs @@ -10,58 +10,10 @@ export const InflightAssetsMessageProvider = { getMessages() { return [ { - id: "MILESTONE_MESSAGE", - groups: ["cfr"], - content: { - anchor_id: "tracking-protection-icon-box", - bucket_id: "CFR_MILESTONE_MESSAGE", - buttons: { - primary: { - action: { - type: "OPEN_PROTECTION_REPORT", - }, - event: "PROTECTION", - label: { - string_id: "cfr-doorhanger-milestone-ok-button", - }, - }, - secondary: [ - { - label: { - string_id: "cfr-doorhanger-milestone-close-button", - }, - action: { - type: "CANCEL", - }, - event: "DISMISS", - }, - ], - }, - category: "cfrFeatures", - heading_text: { - string_id: "cfr-doorhanger-milestone-heading", - }, - layout: "short_message", - notification_text: "", - skip_address_bar_notifier: true, - text: "", - }, - frequency: { - lifetime: 7, - }, - targeting: - "pageLoad >= 4 && firefoxVersion < 87 && userPrefs.cfrFeatures", - template: "milestone_message", - trigger: { - id: "contentBlocking", - params: ["ContentBlockingMilestone"], - }, - }, - { id: "MILESTONE_MESSAGE_87", groups: ["cfr"], content: { - anchor_id: "tracking-protection-icon-box", + anchor_id: "tracking-protection-icon-container", bucket_id: "CFR_MILESTONE_MESSAGE", buttons: { primary: { @@ -98,7 +50,7 @@ export const InflightAssetsMessageProvider = { lifetime: 7, }, targeting: - "pageLoad >= 4 && firefoxVersion >= 87 && userPrefs.cfrFeatures", + "pageLoad >= 4 && firefoxVersion >= 115 && firefoxVersion < 121 && userPrefs.cfrFeatures", template: "milestone_message", trigger: { id: "contentBlocking", @@ -275,66 +227,6 @@ export const InflightAssetsMessageProvider = { ], }, }, - { - id: "WNP_MOMENTS_12", - groups: ["moments-pages"], - content: { - action: { - data: { - expire: 1640908800000, - url: "https://www.mozilla.org/firefox/welcome/12", - }, - id: "moments-wnp", - }, - bucket_id: "WNP_MOMENTS_12", - }, - targeting: - 'localeLanguageCode == "en" && region in ["DE", "AT", "BE", "CA", "FR", "IE", "IT", "MY", "NL", "NZ", "SG", "CH", "US", "GB", "ES"] && (addonsInfo.addons|keys intersect ["@testpilot-containers"])|length == 1 && \'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features\'|preferenceValue && \'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons\'|preferenceValue', - template: "update_action", - trigger: { - id: "momentsUpdate", - }, - }, - { - id: "WNP_MOMENTS_13", - groups: ["moments-pages"], - content: { - action: { - data: { - expire: 1640908800000, - url: "https://www.mozilla.org/firefox/welcome/13", - }, - id: "moments-wnp", - }, - bucket_id: "WNP_MOMENTS_13", - }, - targeting: - '(localeLanguageCode in ["en", "de", "fr", "nl", "it", "ms"] || locale == "es-ES") && region in ["DE", "AT", "BE", "CA", "FR", "IE", "IT", "MY", "NL", "NZ", "SG", "CH", "US", "GB", "ES"] && (addonsInfo.addons|keys intersect ["@testpilot-containers"])|length == 0 && \'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features\'|preferenceValue && \'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons\'|preferenceValue', - template: "update_action", - trigger: { - id: "momentsUpdate", - }, - }, - { - id: "WNP_MOMENTS_14", - groups: ["moments-pages"], - content: { - action: { - data: { - expire: 1668470400000, - url: "https://www.mozilla.org/firefox/welcome/14", - }, - id: "moments-wnp", - }, - bucket_id: "WNP_MOMENTS_14", - }, - targeting: - 'localeLanguageCode in ["en", "de", "fr"] && region in ["AT", "BE", "CA", "CH", "DE", "ES", "FI", "FR", "GB", "IE", "IT", "MY", "NL", "NZ", "SE", "SG", "US"] && \'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features\'|preferenceValue && \'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons\'|preferenceValue', - template: "update_action", - trigger: { - id: "momentsUpdate", - }, - }, ]; }, }; diff --git a/browser/components/asrouter/tests/NimbusRolloutMessageProvider.sys.mjs b/browser/components/asrouter/tests/NimbusRolloutMessageProvider.sys.mjs index 5bfbec9557..f2c94f7de6 100644 --- a/browser/components/asrouter/tests/NimbusRolloutMessageProvider.sys.mjs +++ b/browser/components/asrouter/tests/NimbusRolloutMessageProvider.sys.mjs @@ -12,6 +12,427 @@ export const NimbusRolloutMessageProvider = { getMessages() { return [ { + // Nimbus slug: device-migration-q4-spotlights-remaining-population-esr:treatment (message 1 of 3) + // Recipe: https://experimenter.services.mozilla.com/nimbus/device-migration-q4-spotlights-remaining-population-esr/summary#treatment + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT", + groups: ["eco"], + content: { + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT", + modal: "tab", + screens: [ + { + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT_BACKUP", + content: { + logo: { + height: "152px", + imageURL: + "https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/c92a41e4-82cf-4ad5-8480-04a138bfb3cd.png", + }, + title: { + fontSize: "24px", + string_id: "device-migration-fxa-spotlight-heavy-user-header", + letterSpacing: 0, + }, + subtitle: { + fontSize: "15px", + string_id: "device-migration-fxa-spotlight-heavy-user-body", + lineHeight: "1.4", + marginBlock: "8px 20px", + letterSpacing: 0, + paddingInline: "20px", + }, + dismiss_button: { + action: { + navigate: true, + }, + }, + primary_button: { + label: { + string_id: + "device-migration-fxa-spotlight-heavy-user-primary-button", + paddingBlock: "4px", + paddingInline: "16px", + }, + action: { + data: { + args: "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/switching-devices?utm_source=spotlight-default&utm_medium=firefox-desktop&utm_campaign=migration&utm_content=dont-forget-to-backup&entrypoint=device-migration-spotlight-experiment-v2", + where: "tabshifted", + }, + type: "OPEN_URL", + navigate: true, + }, + }, + }, + }, + ], + backdrop: "transparent", + template: "multistage", + transitions: true, + }, + trigger: { + id: "defaultBrowserCheck", + }, + template: "spotlight", + frequency: { + lifetime: 1, + }, + targeting: + "source == 'startup' && !willShowDefaultPrompt && 'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features'|preferenceValue && !usesFirefoxSync && !hasActiveEnterprisePolicies && userMonthlyActivity[userMonthlyActivity|length - 2][1]|date >= currentDate|date - (28 * 24 * 60 * 60 * 1000) && (((currentDate|date - profileAgeCreated|date) / 86400000 >= 168) || totalBookmarksCount >= 35)", + }, + { + // Nimbus slug: device-migration-q4-spotlights-remaining-population-esr:treatment (message 2 of 3) + // Recipe: https://experimenter.services.mozilla.com/nimbus/device-migration-q4-spotlights-remaining-population-esr/summary#treatment + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT", + groups: ["eco"], + content: { + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT", + modal: "tab", + screens: [ + { + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT_PEACE", + content: { + logo: { + height: "133px", + imageURL: + "https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/4a56d3ed-98c8-4a33-b853-b2cf7646efd8.png", + marginBlock: "22px -10px", + }, + title: { + fontSize: "24px", + string_id: + "device-migration-fxa-spotlight-older-device-header", + letterSpacing: 0, + }, + subtitle: { + fontSize: "15px", + string_id: "device-migration-fxa-spotlight-older-device-body", + lineHeight: "1.4", + marginBlock: "8px 20px", + letterSpacing: 0, + paddingInline: "40px", + }, + dismiss_button: { + action: { + navigate: true, + }, + }, + primary_button: { + label: { + string_id: + "device-migration-fxa-spotlight-older-device-primary-button", + marginBlock: "0 22px", + paddingBlock: "4px", + paddingInline: "16px", + }, + action: { + data: { + args: "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/switching-devices?utm_source=spotlight-default&utm_medium=firefox-desktop&utm_campaign=migration&utm_content=peace-of-mind&entrypoint=device-migration-spotlight-experiment-v2", + where: "tabshifted", + }, + type: "OPEN_URL", + navigate: true, + }, + }, + }, + }, + ], + backdrop: "transparent", + template: "multistage", + transitions: true, + }, + trigger: { + id: "defaultBrowserCheck", + }, + template: "spotlight", + frequency: { + lifetime: 1, + }, + targeting: + "source == 'startup' && !willShowDefaultPrompt && 'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features'|preferenceValue && !usesFirefoxSync && !hasActiveEnterprisePolicies && userMonthlyActivity[userMonthlyActivity|length - 2][1]|date >= currentDate|date - (28 * 24 * 60 * 60 * 1000) && !(((currentDate|date - profileAgeCreated|date) / 86400000 >= 168) || totalBookmarksCount >= 35) && os.isWindows && os.windowsVersion >= 6.1 && os.windowsBuildNumber < 22000", + }, + { + // Nimbus slug: device-migration-q4-spotlights-remaining-population-esr:treatment (message 3 of 3) + // Recipe: https://experimenter.services.mozilla.com/nimbus/device-migration-q4-spotlights-remaining-population-esr/summary#treatment + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT", + groups: ["eco"], + content: { + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT", + modal: "tab", + screens: [ + { + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT_NEW_DEVICE", + content: { + logo: { + height: "149px", + imageURL: + "https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/a43cd9cc-e8b2-477c-92f2-345557370de1.svg", + }, + title: { + fontSize: "24px", + string_id: + "device-migration-fxa-spotlight-getting-new-device-header-2", + letterSpacing: 0, + }, + subtitle: { + fontSize: "15px", + string_id: + "device-migration-fxa-spotlight-getting-new-device-body-2", + lineHeight: "1.4", + marginBlock: "8px 20px", + letterSpacing: 0, + paddingInline: "40px", + }, + dismiss_button: { + action: { + navigate: true, + }, + }, + primary_button: { + label: { + string_id: + "device-migration-fxa-spotlight-getting-new-device-primary-button", + paddingBlock: "4px", + paddingInline: "16px", + }, + action: { + data: { + args: "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/switching-devices?utm_source=spotlight-default&utm_medium=firefox-desktop&utm_campaign=migration&utm_content=new-device-in-your-future&entrypoint=device-migration-spotlight-experiment-v2", + where: "tabshifted", + }, + type: "OPEN_URL", + navigate: true, + }, + }, + }, + }, + ], + backdrop: "transparent", + template: "multistage", + transitions: true, + }, + trigger: { + id: "defaultBrowserCheck", + }, + template: "spotlight", + frequency: { + lifetime: 1, + }, + targeting: + "source == 'startup' && !willShowDefaultPrompt && 'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features'|preferenceValue && !usesFirefoxSync && !hasActiveEnterprisePolicies && userMonthlyActivity[userMonthlyActivity|length - 2][1]|date >= currentDate|date - (28 * 24 * 60 * 60 * 1000) && !(((currentDate|date - profileAgeCreated|date) / 86400000 >= 168) || totalBookmarksCount >= 35) && !(os.isWindows && os.windowsVersion >= 6.1 && os.windowsBuildNumber < 22000)", + }, + { + // Nimbus slug: device-migration-q4-spotlights-remaining-population:treatment (message 1 of 3) + // Version range: 122+ + // Recipe: https://experimenter.services.mozilla.com/nimbus/device-migration-q4-spotlights-remaining-population/summary#treatment + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT", + groups: ["eco"], + content: { + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT", + modal: "tab", + screens: [ + { + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT_BACKUP", + content: { + logo: { + height: "152px", + imageURL: + "https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/c92a41e4-82cf-4ad5-8480-04a138bfb3cd.png", + }, + title: { + fontSize: "24px", + string_id: "device-migration-fxa-spotlight-heavy-user-header", + letterSpacing: 0, + }, + subtitle: { + fontSize: "15px", + string_id: "device-migration-fxa-spotlight-heavy-user-body", + lineHeight: "1.4", + marginBlock: "8px 20px", + letterSpacing: 0, + paddingInline: "20px", + }, + dismiss_button: { + action: { + navigate: true, + }, + }, + primary_button: { + label: { + string_id: + "device-migration-fxa-spotlight-heavy-user-primary-button", + paddingBlock: "4px", + paddingInline: "16px", + }, + action: { + data: { + args: "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/switching-devices?utm_source=spotlight-default&utm_medium=firefox-desktop&utm_campaign=migration&utm_content=dont-forget-to-backup&entrypoint=device-migration-spotlight-experiment-v2", + where: "tabshifted", + }, + type: "OPEN_URL", + navigate: true, + }, + }, + }, + }, + ], + backdrop: "transparent", + template: "multistage", + transitions: true, + }, + trigger: { + id: "defaultBrowserCheck", + }, + template: "spotlight", + frequency: { + lifetime: 1, + }, + targeting: + "source == 'startup' && !willShowDefaultPrompt && 'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features'|preferenceValue && !usesFirefoxSync && !hasActiveEnterprisePolicies && userMonthlyActivity[userMonthlyActivity|length - 2][1]|date >= currentDate|date - (28 * 24 * 60 * 60 * 1000) && (((currentDate|date - profileAgeCreated|date) / 86400000 >= 168) || totalBookmarksCount >= 35)", + }, + { + // Nimbus slug: device-migration-q4-spotlights-remaining-population:treatment (message 2 of 3) + // Version range: 122+ + // Recipe: https://experimenter.services.mozilla.com/nimbus/device-migration-q4-spotlights-remaining-population/summary#treatment + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT", + groups: ["eco"], + content: { + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT", + modal: "tab", + screens: [ + { + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT_PEACE", + content: { + logo: { + height: "133px", + imageURL: + "https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/4a56d3ed-98c8-4a33-b853-b2cf7646efd8.png", + marginBlock: "22px -10px", + }, + title: { + fontSize: "24px", + string_id: + "device-migration-fxa-spotlight-older-device-header", + letterSpacing: 0, + }, + subtitle: { + fontSize: "15px", + string_id: "device-migration-fxa-spotlight-older-device-body", + lineHeight: "1.4", + marginBlock: "8px 20px", + letterSpacing: 0, + paddingInline: "40px", + }, + dismiss_button: { + action: { + navigate: true, + }, + }, + primary_button: { + label: { + string_id: + "device-migration-fxa-spotlight-older-device-primary-button", + marginBlock: "0 22px", + paddingBlock: "4px", + paddingInline: "16px", + }, + action: { + data: { + args: "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/switching-devices?utm_source=spotlight-default&utm_medium=firefox-desktop&utm_campaign=migration&utm_content=peace-of-mind&entrypoint=device-migration-spotlight-experiment-v2", + where: "tabshifted", + }, + type: "OPEN_URL", + navigate: true, + }, + }, + }, + }, + ], + backdrop: "transparent", + template: "multistage", + transitions: true, + }, + trigger: { + id: "defaultBrowserCheck", + }, + template: "spotlight", + frequency: { + lifetime: 1, + }, + targeting: + "source == 'startup' && !willShowDefaultPrompt && 'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features'|preferenceValue && !usesFirefoxSync && !hasActiveEnterprisePolicies && userMonthlyActivity[userMonthlyActivity|length - 2][1]|date >= currentDate|date - (28 * 24 * 60 * 60 * 1000) && !(((currentDate|date - profileAgeCreated|date) / 86400000 >= 168) || totalBookmarksCount >= 35) && os.isWindows && os.windowsVersion >= 6.1 && os.windowsBuildNumber < 22000", + }, + { + // Nimbus slug: device-migration-q4-spotlights-remaining-population:treatment (message 3 of 3) + // Version range: 122+ + // Recipe: https://experimenter.services.mozilla.com/nimbus/device-migration-q4-spotlights-remaining-population/summary#treatment + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT", + groups: ["eco"], + content: { + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT", + modal: "tab", + screens: [ + { + id: "Q4_DEVICE_MIGRATION_BACKUP_SPOTLIGHT_NEW_DEVICE", + content: { + logo: { + height: "149px", + imageURL: + "https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/a43cd9cc-e8b2-477c-92f2-345557370de1.svg", + }, + title: { + fontSize: "24px", + string_id: + "device-migration-fxa-spotlight-getting-new-device-header-2", + letterSpacing: 0, + }, + subtitle: { + fontSize: "15px", + string_id: + "device-migration-fxa-spotlight-getting-new-device-body-2", + lineHeight: "1.4", + marginBlock: "8px 20px", + letterSpacing: 0, + paddingInline: "40px", + }, + dismiss_button: { + action: { + navigate: true, + }, + }, + primary_button: { + label: { + string_id: + "device-migration-fxa-spotlight-getting-new-device-primary-button", + paddingBlock: "4px", + paddingInline: "16px", + }, + action: { + data: { + args: "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/switching-devices?utm_source=spotlight-default&utm_medium=firefox-desktop&utm_campaign=migration&utm_content=new-device-in-your-future&entrypoint=device-migration-spotlight-experiment-v2", + where: "tabshifted", + }, + type: "OPEN_URL", + navigate: true, + }, + }, + }, + }, + ], + backdrop: "transparent", + template: "multistage", + transitions: true, + }, + trigger: { + id: "defaultBrowserCheck", + }, + template: "spotlight", + frequency: { + lifetime: 1, + }, + targeting: + "source == 'startup' && !willShowDefaultPrompt && 'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features'|preferenceValue && !usesFirefoxSync && !hasActiveEnterprisePolicies && userMonthlyActivity[userMonthlyActivity|length - 2][1]|date >= currentDate|date - (28 * 24 * 60 * 60 * 1000) && !(((currentDate|date - profileAgeCreated|date) / 86400000 >= 168) || totalBookmarksCount >= 35) && !(os.isWindows && os.windowsVersion >= 6.1 && os.windowsBuildNumber < 22000)", + }, + { // Nimbus slug: fox-doodle-set-to-default-early-day-user-de-fr-it-treatment-a-rollout:treatment-a // Version range: 116+ // Recipe: https://experimenter.services.mozilla.com/nimbus/fox-doodle-set-to-default-early-day-user-de-fr-it-treatment-a-rollout/summary#treatment-a diff --git a/browser/components/asrouter/tests/browser/browser.toml b/browser/components/asrouter/tests/browser/browser.toml index 7bed40373d..60ce42dfd8 100644 --- a/browser/components/asrouter/tests/browser/browser.toml +++ b/browser/components/asrouter/tests/browser/browser.toml @@ -21,6 +21,11 @@ skip-if = ["os == 'linux' && bits == 64 && !debug"] # Bug 1643036 ["browser_asrouter_infobar.js"] +["browser_asrouter_keyboard_cfr.js"] +https_first_disabled = true + +["browser_asrouter_milestone_message_cfr.js"] + ["browser_asrouter_momentspagehub.js"] tags = "remote-settings" diff --git a/browser/components/asrouter/tests/browser/browser_asrouter_bug1761522.js b/browser/components/asrouter/tests/browser/browser_asrouter_bug1761522.js index 19fcb63131..d22605d589 100644 --- a/browser/components/asrouter/tests/browser/browser_asrouter_bug1761522.js +++ b/browser/components/asrouter/tests/browser/browser_asrouter_bug1761522.js @@ -20,7 +20,7 @@ const { RemoteSettings } = ChromeUtils.importESModule( ); // This pref is used to override the Remote Settings server URL in tests. -// See SERVER_URL in services/settings/Utils.jsm for more details. +// See SERVER_URL in services/settings/Utils.sys.mjs for more details. const RS_SERVER_PREF = "services.settings.server"; const FLUENT_CONTENT = "asrouter-test-string = Test Test Test\n"; diff --git a/browser/components/asrouter/tests/browser/browser_asrouter_cfr.js b/browser/components/asrouter/tests/browser/browser_asrouter_cfr.js index e29771c24f..1979c81a79 100644 --- a/browser/components/asrouter/tests/browser/browser_asrouter_cfr.js +++ b/browser/components/asrouter/tests/browser/browser_asrouter_cfr.js @@ -115,14 +115,6 @@ function checkCFRAddonsElements(notification) { ); } -function checkCFRTrackingProtectionMilestone(notification) { - Assert.ok(notification.hidden === false, "Panel should be visible"); - Assert.ok( - notification.getAttribute("data-notification-category") === "short_message", - "Panel have correct data attribute" - ); -} - function clearNotifications() { for (let notification of PopupNotifications._currentNotifications) { notification.remove(); @@ -498,59 +490,6 @@ add_task(async function test_cfr_addon_install() { Services.fog.testResetFOG(); }); -add_task( - async function test_cfr_tracking_protection_milestone_notification_remove() { - await SpecialPowers.pushPrefEnv({ - set: [ - ["browser.contentblocking.cfr-milestone.milestone-achieved", 1000], - [ - "browser.newtabpage.activity-stream.asrouter.providers.cfr", - `{"id":"cfr","enabled":true,"type":"local","localProvider":"CFRMessageProvider","updateCycleInMs":3600000}`, - ], - ], - }); - - // addRecommendation checks that scheme starts with http and host matches - let browser = gBrowser.selectedBrowser; - BrowserTestUtils.startLoadingURIString(browser, "http://example.com/"); - await BrowserTestUtils.browserLoaded(browser, false, "http://example.com/"); - - const showPanel = BrowserTestUtils.waitForEvent( - PopupNotifications.panel, - "popupshown" - ); - - Services.obs.notifyObservers( - { - wrappedJSObject: { - event: "ContentBlockingMilestone", - }, - }, - "SiteProtection:ContentBlockingMilestone" - ); - - await showPanel; - - const notification = document.getElementById( - "contextual-feature-recommendation-notification" - ); - - checkCFRTrackingProtectionMilestone(notification); - - Assert.ok(notification.secondaryButton); - let hidePanel = BrowserTestUtils.waitForEvent( - PopupNotifications.panel, - "popuphidden" - ); - - notification.secondaryButton.click(); - await hidePanel; - await SpecialPowers.popPrefEnv(); - clearNotifications(); - Services.fog.testResetFOG(); - } -); - add_task(async function test_cfr_addon_and_features_show() { // addRecommendation checks that scheme starts with http and host matches let browser = gBrowser.selectedBrowser; @@ -747,62 +686,6 @@ add_task(async function test_providerNames() { } }); -add_task(async function test_cfr_notification_keyboard() { - // addRecommendation checks that scheme starts with http and host matches - const browser = gBrowser.selectedBrowser; - BrowserTestUtils.startLoadingURIString(browser, "http://example.com/"); - await BrowserTestUtils.browserLoaded(browser, false, "http://example.com/"); - - const response = await trigger_cfr_panel(browser, "example.com"); - Assert.ok( - response, - "Should return true if addRecommendation checks were successful" - ); - - // Open the panel with the keyboard. - // Toolbar buttons aren't always focusable; toolbar keyboard navigation - // makes them focusable on demand. Therefore, we must force focus. - const button = document.getElementById("contextual-feature-recommendation"); - button.setAttribute("tabindex", "-1"); - button.focus(); - button.removeAttribute("tabindex"); - - let focused = BrowserTestUtils.waitForEvent( - PopupNotifications.panel, - "focus", - true - ); - EventUtils.synthesizeKey(" "); - await focused; - Assert.ok(true, "Focus inside panel after button pressed"); - - let hidden = BrowserTestUtils.waitForEvent( - PopupNotifications.panel, - "popuphidden" - ); - EventUtils.synthesizeKey("KEY_Escape"); - await hidden; - Assert.ok(true, "Panel hidden after Escape pressed"); - - const showPanel = BrowserTestUtils.waitForEvent( - PopupNotifications.panel, - "popupshown" - ); - // Need to dismiss the notification to clear the RecommendationMap - document.getElementById("contextual-feature-recommendation").click(); - await showPanel; - - const hidePanel = BrowserTestUtils.waitForEvent( - PopupNotifications.panel, - "popuphidden" - ); - document - .getElementById("contextual-feature-recommendation-notification") - .button.click(); - await hidePanel; - Services.fog.testResetFOG(); -}); - add_task(function test_updateCycleForProviders() { Services.prefs .getChildList("browser.newtabpage.activity-stream.asrouter.providers.") diff --git a/browser/components/asrouter/tests/browser/browser_asrouter_keyboard_cfr.js b/browser/components/asrouter/tests/browser/browser_asrouter_keyboard_cfr.js new file mode 100644 index 0000000000..c3dfc0b0bb --- /dev/null +++ b/browser/components/asrouter/tests/browser/browser_asrouter_keyboard_cfr.js @@ -0,0 +1,162 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* eslint-disable @microsoft/sdl/no-insecure-url */ +function clearNotifications() { + for (let notification of PopupNotifications._currentNotifications) { + notification.remove(); + } + + // Clicking the primary action also removes the notification + Assert.equal( + PopupNotifications._currentNotifications.length, + 0, + "Should have removed the notification" + ); +} + +add_setup(async function () { + // Store it in order to restore to the original value + const { _fetchLatestAddonVersion } = CFRPageActions; + // Prevent fetching the real addon url and making a network request + CFRPageActions._fetchLatestAddonVersion = () => "http://example.com"; + Services.fog.testResetFOG(); + + registerCleanupFunction(() => { + CFRPageActions._fetchLatestAddonVersion = _fetchLatestAddonVersion; + clearNotifications(); + CFRPageActions.clearRecommendations(); + }); +}); + +add_task(async function test_cfr_notification_keyboard() { + // addRecommendation checks that scheme starts with http and host matches + const browser = gBrowser.selectedBrowser; + BrowserTestUtils.startLoadingURIString(browser, "http://example.com/"); + await BrowserTestUtils.browserLoaded(browser, false, "http://example.com/"); + + clearNotifications(); + + let recommendation = { + template: "cfr_doorhanger", + groups: ["mochitest-group"], + content: { + layout: "addon_recommendation", + category: "cfrAddons", + anchor_id: "page-action-buttons", + icon_class: "cfr-doorhanger-medium-icon", + skip_address_bar_notifier: false, + heading_text: "Sample Mochitest", + icon: "chrome://activity-stream/content/data/content/assets/glyph-webextension-16.svg", + icon_dark_theme: + "chrome://activity-stream/content/data/content/assets/glyph-webextension-16.svg", + info_icon: { + label: { attributes: { tooltiptext: "Why am I seeing this" } }, + sumo_path: "extensionrecommendations", + }, + addon: { + id: "addon-id", + title: "Addon name", + icon: "chrome://browser/skin/addons/addon-install-downloading.svg", + author: "Author name", + amo_url: "https://example.com", + rating: "4.5", + users: "1.1M", + }, + text: "Mochitest", + buttons: { + primary: { + label: { + value: "OK", + attributes: { accesskey: "O" }, + }, + action: { + type: "CANCEL", + data: {}, + }, + }, + secondary: [ + { + label: { + value: "Cancel", + attributes: { accesskey: "C" }, + }, + action: { + type: "CANCEL", + }, + }, + ], + }, + }, + }; + + recommendation.content.notification_text = new String("Mochitest"); // eslint-disable-line + recommendation.content.notification_text.attributes = { + tooltiptext: "Mochitest tooltip", + "a11y-announcement": "Mochitest announcement", + }; + + const response = await CFRPageActions.addRecommendation( + gBrowser.selectedBrowser, + "example.com", + recommendation, + // Use the real AS dispatch method to trigger real notifications + ASRouter.dispatchCFRAction + ); + + Assert.ok( + response, + "Should return true if addRecommendation checks were successful" + ); + + // Open the panel with the keyboard. + // Toolbar buttons aren't always focusable; toolbar keyboard navigation + // makes them focusable on demand. Therefore, we must force focus. + const button = document.getElementById("contextual-feature-recommendation"); + button.setAttribute("tabindex", "-1"); + + let buttonFocused = BrowserTestUtils.waitForEvent(button, "focus"); + button.focus(); + await buttonFocused; + + Assert.ok(true, "Focus page action button"); + + let focused = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "focus", + true + ); + + EventUtils.synthesizeKey(" "); + await focused; + Assert.ok(true, "Focus inside panel after button pressed"); + + button.removeAttribute("tabindex"); + + let hidden = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popuphidden" + ); + EventUtils.synthesizeKey("KEY_Escape"); + await hidden; + Assert.ok(true, "Panel hidden after Escape pressed"); + + const showPanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popupshown" + ); + // Need to dismiss the notification to clear the RecommendationMap + document.getElementById("contextual-feature-recommendation").click(); + await showPanel; + + const hidePanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popuphidden" + ); + document + .getElementById("contextual-feature-recommendation-notification") + .button.click(); + await hidePanel; + Services.fog.testResetFOG(); +}); diff --git a/browser/components/asrouter/tests/browser/browser_asrouter_milestone_message_cfr.js b/browser/components/asrouter/tests/browser/browser_asrouter_milestone_message_cfr.js new file mode 100644 index 0000000000..6585963d6f --- /dev/null +++ b/browser/components/asrouter/tests/browser/browser_asrouter_milestone_message_cfr.js @@ -0,0 +1,78 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* eslint-disable @microsoft/sdl/no-insecure-url */ +function checkCFRTrackingProtectionMilestone(notification) { + Assert.ok(notification.hidden === false, "Panel should be visible"); + Assert.ok( + notification.getAttribute("data-notification-category") === "short_message", + "Panel have correct data attribute" + ); +} + +function clearNotifications() { + for (let notification of PopupNotifications._currentNotifications) { + notification.remove(); + } + + // Clicking the primary action also removes the notification + Assert.equal( + PopupNotifications._currentNotifications.length, + 0, + "Should have removed the notification" + ); +} + +add_task( + async function test_cfr_tracking_protection_milestone_notification_remove() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.contentblocking.cfr-milestone.milestone-achieved", 1000], + [ + "browser.newtabpage.activity-stream.asrouter.providers.cfr", + `{"id":"cfr","enabled":true,"type":"local","localProvider":"CFRMessageProvider","updateCycleInMs":3600000}`, + ], + ], + }); + + // addRecommendation checks that scheme starts with http and host matches + let browser = gBrowser.selectedBrowser; + BrowserTestUtils.startLoadingURIString(browser, "http://example.com/"); + await BrowserTestUtils.browserLoaded(browser, false, "http://example.com/"); + + const showPanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popupshown" + ); + + Services.obs.notifyObservers( + { + wrappedJSObject: { + event: "ContentBlockingMilestone", + }, + }, + "SiteProtection:ContentBlockingMilestone" + ); + + await showPanel; + + const notification = document.getElementById( + "contextual-feature-recommendation-notification" + ); + + checkCFRTrackingProtectionMilestone(notification); + + Assert.ok(notification.secondaryButton); + let hidePanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popuphidden" + ); + + notification.secondaryButton.click(); + await hidePanel; + await SpecialPowers.popPrefEnv(); + clearNotifications(); + Services.fog.testResetFOG(); + } +); diff --git a/browser/components/asrouter/tests/browser/browser_asrouter_momentspagehub.js b/browser/components/asrouter/tests/browser/browser_asrouter_momentspagehub.js index f752d01116..aea702bb61 100644 --- a/browser/components/asrouter/tests/browser/browser_asrouter_momentspagehub.js +++ b/browser/components/asrouter/tests/browser/browser_asrouter_momentspagehub.js @@ -14,11 +14,11 @@ const { ASRouter } = ChromeUtils.importESModule( const HOMEPAGE_OVERRIDE_PREF = "browser.startup.homepage_override.once"; add_task(async function test_with_rs_messages() { - // Force the WNPanel provider cache to 0 by modifying updateCycleInMs + // Force the cfr provider cache to 0 by modifying updateCycleInMs await SpecialPowers.pushPrefEnv({ set: [ [ - "browser.newtabpage.activity-stream.asrouter.providers.whats-new-panel", + "browser.newtabpage.activity-stream.asrouter.providers.cfr", `{"id":"cfr","enabled":true,"type":"remote-settings","collection":"cfr","updateCycleInMs":0}`, ], ], diff --git a/browser/components/asrouter/tests/unit/ASRouter.test.js b/browser/components/asrouter/tests/unit/ASRouter.test.js index 7df1449a14..1f5899fce1 100644 --- a/browser/components/asrouter/tests/unit/ASRouter.test.js +++ b/browser/components/asrouter/tests/unit/ASRouter.test.js @@ -48,7 +48,6 @@ describe("ASRouter", () => { let fakeAttributionCode; let fakeTargetingContext; let FakeToolbarBadgeHub; - let FakeToolbarPanelHub; let FakeMomentsPageHub; let ASRouterTargeting; let screenImpressions; @@ -151,7 +150,6 @@ describe("ASRouter", () => { cfr: "", "message-groups": "", "messaging-experiments": "", - "whats-new-panel": "", }, totalBookmarksCount: {}, firefoxVersion: 80, @@ -159,7 +157,6 @@ describe("ASRouter", () => { needsUpdate: {}, hasPinnedTabs: false, hasAccessedFxAPanel: false, - isWhatsNewPanelEnabled: true, userPrefs: { cfrFeatures: true, cfrAddons: true, @@ -203,12 +200,6 @@ describe("ASRouter", () => { writeAttributionFile: () => Promise.resolve(), getCachedAttributionData: sinon.stub(), }; - FakeToolbarPanelHub = { - init: sandbox.stub(), - uninit: sandbox.stub(), - forceShowMessage: sandbox.stub(), - enableToolbarButton: sandbox.stub(), - }; FakeToolbarBadgeHub = { init: sandbox.stub(), uninit: sandbox.stub(), @@ -252,7 +243,6 @@ describe("ASRouter", () => { PanelTestProvider, MacAttribution: { applicationPath: "" }, ToolbarBadgeHub: FakeToolbarBadgeHub, - ToolbarPanelHub: FakeToolbarPanelHub, MomentsPageHub: FakeMomentsPageHub, KintoHttpClient: class { bucket() { @@ -354,7 +344,6 @@ describe("ASRouter", () => { // ASRouter init called in `beforeEach` block above assert.calledOnce(FakeToolbarBadgeHub.init); - assert.calledOnce(FakeToolbarPanelHub.init); assert.calledOnce(FakeMomentsPageHub.init); assert.calledWithExactly( @@ -370,15 +359,6 @@ describe("ASRouter", () => { ); assert.calledWithExactly( - FakeToolbarPanelHub.init, - Router.waitForInitialized, - { - getMessages: Router.handleMessageRequest, - sendTelemetry: Router.sendTelemetry, - } - ); - - assert.calledWithExactly( FakeMomentsPageHub.init, Router.waitForInitialized, { @@ -678,25 +658,10 @@ describe("ASRouter", () => { sandbox.stub(CFRPageActions, "addRecommendation"); browser = {}; }); - it("should route whatsnew_panel_message message to the right hub", () => { - Router.routeCFRMessage( - { template: "whatsnew_panel_message" }, - browser, - "", - true - ); - - assert.calledOnce(FakeToolbarPanelHub.forceShowMessage); - assert.notCalled(FakeToolbarBadgeHub.registerBadgeNotificationListener); - assert.notCalled(CFRPageActions.addRecommendation); - assert.notCalled(CFRPageActions.forceRecommendation); - assert.notCalled(FakeMomentsPageHub.executeAction); - }); it("should route moments messages to the right hub", () => { Router.routeCFRMessage({ template: "update_action" }, browser, "", true); assert.calledOnce(FakeMomentsPageHub.executeAction); - assert.notCalled(FakeToolbarPanelHub.forceShowMessage); assert.notCalled(FakeToolbarBadgeHub.registerBadgeNotificationListener); assert.notCalled(CFRPageActions.addRecommendation); assert.notCalled(CFRPageActions.forceRecommendation); @@ -705,7 +670,6 @@ describe("ASRouter", () => { Router.routeCFRMessage({ template: "toolbar_badge" }, browser); assert.calledOnce(FakeToolbarBadgeHub.registerBadgeNotificationListener); - assert.notCalled(FakeToolbarPanelHub.forceShowMessage); assert.notCalled(CFRPageActions.addRecommendation); assert.notCalled(CFRPageActions.forceRecommendation); assert.notCalled(FakeMomentsPageHub.executeAction); @@ -721,7 +685,6 @@ describe("ASRouter", () => { assert.calledOnce(CFRPageActions.addRecommendation); assert.notCalled(CFRPageActions.forceRecommendation); assert.notCalled(FakeToolbarBadgeHub.registerBadgeNotificationListener); - assert.notCalled(FakeToolbarPanelHub.forceShowMessage); assert.notCalled(FakeMomentsPageHub.executeAction); }); it("should route cfr_doorhanger message to the right hub force = false", () => { @@ -733,7 +696,6 @@ describe("ASRouter", () => { ); assert.calledOnce(CFRPageActions.addRecommendation); - assert.notCalled(FakeToolbarPanelHub.forceShowMessage); assert.notCalled(FakeToolbarBadgeHub.registerBadgeNotificationListener); assert.notCalled(CFRPageActions.forceRecommendation); assert.notCalled(FakeMomentsPageHub.executeAction); @@ -742,7 +704,6 @@ describe("ASRouter", () => { Router.routeCFRMessage({ template: "cfr_doorhanger" }, browser, {}, true); assert.calledOnce(CFRPageActions.forceRecommendation); - assert.notCalled(FakeToolbarPanelHub.forceShowMessage); assert.notCalled(CFRPageActions.addRecommendation); assert.notCalled(FakeToolbarBadgeHub.registerBadgeNotificationListener); assert.notCalled(FakeMomentsPageHub.executeAction); @@ -759,7 +720,6 @@ describe("ASRouter", () => { const { args } = CFRPageActions.addRecommendation.firstCall; // Host should be null assert.isNull(args[1]); - assert.notCalled(FakeToolbarPanelHub.forceShowMessage); assert.notCalled(FakeToolbarBadgeHub.registerBadgeNotificationListener); assert.notCalled(CFRPageActions.forceRecommendation); assert.notCalled(FakeMomentsPageHub.executeAction); @@ -773,7 +733,6 @@ describe("ASRouter", () => { ); assert.calledOnce(CFRPageActions.forceRecommendation); - assert.notCalled(FakeToolbarPanelHub.forceShowMessage); assert.notCalled(CFRPageActions.addRecommendation); assert.notCalled(FakeToolbarBadgeHub.registerBadgeNotificationListener); assert.notCalled(FakeMomentsPageHub.executeAction); @@ -786,7 +745,6 @@ describe("ASRouter", () => { true ); - assert.notCalled(FakeToolbarPanelHub.forceShowMessage); assert.notCalled(CFRPageActions.forceRecommendation); assert.notCalled(CFRPageActions.addRecommendation); assert.notCalled(FakeToolbarBadgeHub.registerBadgeNotificationListener); @@ -961,7 +919,6 @@ describe("ASRouter", () => { type: "local", enabled: true, messages: [ - "whatsnew_panel_message", "cfr_doorhanger", "toolbar_badge", "update_action", @@ -1272,43 +1229,6 @@ describe("ASRouter", () => { Router.state.messageImpressions ); }); - it("should return all unblocked messages that match the template, trigger if returnAll=true", async () => { - const message1 = { - provider: "whats_new", - id: "1", - template: "whatsnew_panel_message", - trigger: { id: "whatsNewPanelOpened" }, - groups: ["whats_new"], - }; - const message2 = { - provider: "whats_new", - id: "2", - template: "whatsnew_panel_message", - trigger: { id: "whatsNewPanelOpened" }, - groups: ["whats_new"], - }; - const message3 = { - provider: "whats_new", - id: "3", - template: "badge", - groups: ["whats_new"], - }; - ASRouterTargeting.findMatchingMessage.callsFake(() => [ - message2, - message1, - ]); - await Router.setState({ - messages: [message3, message2, message1], - providers: [{ id: "whats_new" }], - }); - const result = await Router.handleMessageRequest({ - template: "whatsnew_panel_message", - triggerId: "whatsNewPanelOpened", - returnAll: true, - }); - - assert.deepEqual(result, [message2, message1]); - }); it("should forward trigger param info", async () => { const trigger = { triggerId: "foo", @@ -1854,33 +1774,6 @@ describe("ASRouter", () => { }); }); - describe("#forceWNPanel", () => { - let browser = { - ownerGlobal: { - document: new Document(), - PanelUI: { - showSubView: sinon.stub(), - panel: { - setAttribute: sinon.stub(), - }, - }, - }, - }; - let fakePanel = { - setAttribute: sinon.stub(), - }; - sinon - .stub(browser.ownerGlobal.document, "getElementById") - .returns(fakePanel); - - it("should call enableToolbarButton", async () => { - await Router.forceWNPanel(browser); - assert.calledOnce(FakeToolbarPanelHub.enableToolbarButton); - assert.calledOnce(browser.ownerGlobal.PanelUI.showSubView); - assert.calledWith(fakePanel.setAttribute, "noautohide", true); - }); - }); - describe("_triggerHandler", () => { it("should call #sendTriggerMessage with the correct trigger", () => { const getter = sandbox.stub(); diff --git a/browser/components/asrouter/tests/unit/ASRouterChild.test.js b/browser/components/asrouter/tests/unit/ASRouterChild.test.js index b73e56d510..c6533e073d 100644 --- a/browser/components/asrouter/tests/unit/ASRouterChild.test.js +++ b/browser/components/asrouter/tests/unit/ASRouterChild.test.js @@ -1,6 +1,6 @@ /*eslint max-nested-callbacks: ["error", 10]*/ import { ASRouterChild } from "actors/ASRouterChild.sys.mjs"; -import { MESSAGE_TYPE_HASH as msg } from "modules/ActorConstants.sys.mjs"; +import { MESSAGE_TYPE_HASH as msg } from "modules/ActorConstants.mjs"; describe("ASRouterChild", () => { let asRouterChild = null; @@ -24,7 +24,6 @@ describe("ASRouterChild", () => { msg.DISABLE_PROVIDER, msg.ENABLE_PROVIDER, msg.EXPIRE_QUERY_CACHE, - msg.FORCE_WHATSNEW_PANEL, msg.IMPRESSION, msg.RESET_PROVIDER_PREF, msg.SET_PROVIDER_USER_PREF, diff --git a/browser/components/asrouter/tests/unit/ASRouterParent.test.js b/browser/components/asrouter/tests/unit/ASRouterParent.test.js index 0358b1261c..e65d7db825 100644 --- a/browser/components/asrouter/tests/unit/ASRouterParent.test.js +++ b/browser/components/asrouter/tests/unit/ASRouterParent.test.js @@ -1,5 +1,5 @@ import { ASRouterParent } from "actors/ASRouterParent.sys.mjs"; -import { MESSAGE_TYPE_HASH as msg } from "modules/ActorConstants.sys.mjs"; +import { MESSAGE_TYPE_HASH as msg } from "modules/ActorConstants.mjs"; describe("ASRouterParent", () => { let asRouterParent = null; diff --git a/browser/components/asrouter/tests/unit/ASRouterParentProcessMessageHandler.test.js b/browser/components/asrouter/tests/unit/ASRouterParentProcessMessageHandler.test.js index 7bfec3e099..6a965c5689 100644 --- a/browser/components/asrouter/tests/unit/ASRouterParentProcessMessageHandler.test.js +++ b/browser/components/asrouter/tests/unit/ASRouterParentProcessMessageHandler.test.js @@ -1,6 +1,6 @@ import { ASRouterParentProcessMessageHandler } from "modules/ASRouterParentProcessMessageHandler.sys.mjs"; import { _ASRouter } from "modules/ASRouter.sys.mjs"; -import { MESSAGE_TYPE_HASH as msg } from "modules/ActorConstants.sys.mjs"; +import { MESSAGE_TYPE_HASH as msg } from "modules/ActorConstants.mjs"; describe("ASRouterParentProcessMessageHandler", () => { let handler = null; @@ -14,8 +14,6 @@ describe("ASRouterParentProcessMessageHandler", () => { "addImpression", "evaluateExpression", "forceAttribution", - "forceWNPanel", - "closeWNPanel", "forcePBWindow", "resetGroupsState", "resetMessageState", @@ -122,7 +120,6 @@ describe("ASRouterParentProcessMessageHandler", () => { [ msg.AS_ROUTER_TELEMETRY_USER_EVENT, msg.TOOLBAR_BADGE_TELEMETRY, - msg.TOOLBAR_PANEL_TELEMETRY, msg.MOMENTS_PAGE_TELEMETRY, msg.DOORHANGER_TELEMETRY, ].forEach(type => { @@ -309,28 +306,6 @@ describe("ASRouterParentProcessMessageHandler", () => { assert.calledOnce(config.router.forceAttribution); }); }); - describe("FORCE_WHATSNEW_PANEL action", () => { - it("default calls forceWNPanel", () => { - handler.handleMessage( - msg.FORCE_WHATSNEW_PANEL, - {}, - { browser: { ownerGlobal: {} } } - ); - assert.calledOnce(config.router.forceWNPanel); - assert.calledWith(config.router.forceWNPanel, { ownerGlobal: {} }); - }); - }); - describe("CLOSE_WHATSNEW_PANEL action", () => { - it("default calls closeWNPanel", () => { - handler.handleMessage( - msg.CLOSE_WHATSNEW_PANEL, - {}, - { browser: { ownerGlobal: {} } } - ); - assert.calledOnce(config.router.closeWNPanel); - assert.calledWith(config.router.closeWNPanel, { ownerGlobal: {} }); - }); - }); describe("FORCE_PRIVATE_BROWSING_WINDOW action", () => { it("default calls forcePBWindow", () => { handler.handleMessage( diff --git a/browser/components/asrouter/tests/unit/ToolbarBadgeHub.test.js b/browser/components/asrouter/tests/unit/ToolbarBadgeHub.test.js index 3e91b657bc..cfeac77025 100644 --- a/browser/components/asrouter/tests/unit/ToolbarBadgeHub.test.js +++ b/browser/components/asrouter/tests/unit/ToolbarBadgeHub.test.js @@ -1,10 +1,6 @@ import { _ToolbarBadgeHub } from "modules/ToolbarBadgeHub.sys.mjs"; import { GlobalOverrider } from "test/unit/utils"; import { OnboardingMessageProvider } from "modules/OnboardingMessageProvider.sys.mjs"; -import { - _ToolbarPanelHub, - ToolbarPanelHub, -} from "modules/ToolbarPanelHub.sys.mjs"; describe("ToolbarBadgeHub", () => { let sandbox; @@ -13,7 +9,6 @@ describe("ToolbarBadgeHub", () => { let fakeSendTelemetry; let isBrowserPrivateStub; let fxaMessage; - let whatsnewMessage; let fakeElement; let globals; let everyWindowStub; @@ -36,28 +31,6 @@ describe("ToolbarBadgeHub", () => { const onboardingMsgs = await OnboardingMessageProvider.getUntranslatedMessages(); fxaMessage = onboardingMsgs.find(({ id }) => id === "FXA_ACCOUNTS_BADGE"); - whatsnewMessage = { - id: `WHATS_NEW_BADGE_71`, - template: "toolbar_badge", - content: { - delay: 1000, - target: "whats-new-menu-button", - action: { id: "show-whatsnew-button" }, - badgeDescription: { string_id: "cfr-badge-reader-label-newfeature" }, - }, - priority: 1, - trigger: { id: "toolbarBadgeUpdate" }, - frequency: { - // Makes it so that we track impressions for this message while at the - // same time it can have unlimited impressions - lifetime: Infinity, - }, - // Never saw this message or saw it in the past 4 days or more recent - targeting: `isWhatsNewPanelEnabled && - (!messageImpressions['WHATS_NEW_BADGE_71'] || - (messageImpressions['WHATS_NEW_BADGE_71']|length >= 1 && - currentDate|date - messageImpressions['WHATS_NEW_BADGE_71'][0] <= 4 * 24 * 3600 * 1000))`, - }; fakeElement = { classList: { add: sandbox.stub(), @@ -93,7 +66,6 @@ describe("ToolbarBadgeHub", () => { setStringPrefStub = sandbox.stub(); requestIdleCallbackStub = sandbox.stub().callsFake(fn => fn()); globals.set({ - ToolbarPanelHub, requestIdleCallback: requestIdleCallbackStub, EveryWindow: everyWindowStub, PrivateBrowsingUtils: { isBrowserPrivate: isBrowserPrivateStub }, @@ -139,16 +111,6 @@ describe("ToolbarBadgeHub", () => { assert.calledTwice(instance.messageRequest); }); - it("should add a pref observer", async () => { - await instance.init(sandbox.stub().resolves(), {}); - - assert.calledOnce(addObserverStub); - assert.calledWithExactly( - addObserverStub, - instance.prefs.WHATSNEW_TOOLBAR_PANEL, - instance - ); - }); }); describe("#uninit", () => { beforeEach(async () => { @@ -164,16 +126,6 @@ describe("ToolbarBadgeHub", () => { assert.calledOnce(clearTimeoutStub); assert.calledWithExactly(clearTimeoutStub, 2); }); - it("should remove the pref observer", () => { - instance.uninit(); - - assert.calledOnce(removeObserverStub); - assert.calledWithExactly( - removeObserverStub, - instance.prefs.WHATSNEW_TOOLBAR_PANEL, - instance - ); - }); }); describe("messageRequest", () => { let handleMessageRequestStub; @@ -293,66 +245,6 @@ describe("ToolbarBadgeHub", () => { instance.removeAllNotifications ); }); - it("should execute actions if they exist", () => { - sandbox.stub(instance, "executeAction"); - instance.addToolbarNotification(target, whatsnewMessage); - - assert.calledOnce(instance.executeAction); - assert.calledWithExactly(instance.executeAction, { - ...whatsnewMessage.content.action, - message_id: whatsnewMessage.id, - }); - }); - it("should create a description element", () => { - sandbox.stub(instance, "executeAction"); - instance.addToolbarNotification(target, whatsnewMessage); - - assert.calledOnce(fakeDocument.createElement); - assert.calledWithExactly(fakeDocument.createElement, "span"); - }); - it("should set description id to element and to button", () => { - sandbox.stub(instance, "executeAction"); - instance.addToolbarNotification(target, whatsnewMessage); - - assert.calledWithExactly( - fakeElement.setAttribute, - "id", - "toolbarbutton-notification-description" - ); - assert.calledWithExactly( - fakeElement.setAttribute, - "aria-labelledby", - `toolbarbutton-notification-description ${whatsnewMessage.content.target}` - ); - }); - it("should attach fluent id to description", () => { - sandbox.stub(instance, "executeAction"); - instance.addToolbarNotification(target, whatsnewMessage); - - assert.calledOnce(fakeDocument.l10n.setAttributes); - assert.calledWithExactly( - fakeDocument.l10n.setAttributes, - fakeElement, - whatsnewMessage.content.badgeDescription.string_id - ); - }); - it("should add an impression for the message", () => { - instance.addToolbarNotification(target, whatsnewMessage); - - assert.calledOnce(instance._addImpression); - assert.calledWithExactly(instance._addImpression, whatsnewMessage); - }); - it("should send an impression ping", async () => { - sandbox.stub(instance, "sendUserEventTelemetry"); - instance.addToolbarNotification(target, whatsnewMessage); - - assert.calledOnce(instance.sendUserEventTelemetry); - assert.calledWithExactly( - instance.sendUserEventTelemetry, - "IMPRESSION", - whatsnewMessage - ); - }); }); describe("registerBadgeNotificationListener", () => { let msg_no_delay; @@ -410,44 +302,6 @@ describe("ToolbarBadgeHub", () => { assert.calledOnce(everyWindowStub.unregisterCallback); assert.calledWithExactly(everyWindowStub.unregisterCallback, instance.id); }); - it("should only call executeAction for 'update_action' messages", () => { - const stub = sandbox.stub(instance, "executeAction"); - const updateActionMsg = { ...msg_no_delay, template: "update_action" }; - - instance.registerBadgeNotificationListener(updateActionMsg); - - assert.notCalled(everyWindowStub.registerCallback); - assert.calledOnce(stub); - }); - }); - describe("executeAction", () => { - let blockMessageByIdStub; - beforeEach(async () => { - blockMessageByIdStub = sandbox.stub(); - await instance.init(sandbox.stub().resolves(), { - blockMessageById: blockMessageByIdStub, - }); - }); - it("should call ToolbarPanelHub.enableToolbarButton", () => { - const stub = sandbox.stub( - _ToolbarPanelHub.prototype, - "enableToolbarButton" - ); - - instance.executeAction({ id: "show-whatsnew-button" }); - - assert.calledOnce(stub); - }); - it("should call ToolbarPanelHub.enableAppmenuButton", () => { - const stub = sandbox.stub( - _ToolbarPanelHub.prototype, - "enableAppmenuButton" - ); - - instance.executeAction({ id: "show-whatsnew-button" }); - - assert.calledOnce(stub); - }); }); describe("removeToolbarNotification", () => { it("should remove the notification", () => { @@ -629,24 +483,4 @@ describe("ToolbarBadgeHub", () => { assert.propertyVal(ping.data, "event", "CLICK"); }); }); - describe("#observe", () => { - it("should make a message request when the whats new pref is changed", () => { - sandbox.stub(instance, "messageRequest"); - - instance.observe("", "", instance.prefs.WHATSNEW_TOOLBAR_PANEL); - - assert.calledOnce(instance.messageRequest); - assert.calledWithExactly(instance.messageRequest, { - template: "toolbar_badge", - triggerId: "toolbarBadgeUpdate", - }); - }); - it("should not react to other pref changes", () => { - sandbox.stub(instance, "messageRequest"); - - instance.observe("", "", "foo"); - - assert.notCalled(instance.messageRequest); - }); - }); }); diff --git a/browser/components/asrouter/tests/unit/ToolbarPanelHub.test.js b/browser/components/asrouter/tests/unit/ToolbarPanelHub.test.js deleted file mode 100644 index 1755f62308..0000000000 --- a/browser/components/asrouter/tests/unit/ToolbarPanelHub.test.js +++ /dev/null @@ -1,760 +0,0 @@ -import { _ToolbarPanelHub } from "modules/ToolbarPanelHub.sys.mjs"; -import { GlobalOverrider } from "test/unit/utils"; -import { PanelTestProvider } from "modules/PanelTestProvider.sys.mjs"; - -describe("ToolbarPanelHub", () => { - let globals; - let sandbox; - let instance; - let everyWindowStub; - let fakeDocument; - let fakeWindow; - let fakeElementById; - let fakeElementByTagName; - let createdCustomElements = []; - let eventListeners = {}; - let addObserverStub; - let removeObserverStub; - let getBoolPrefStub; - let setBoolPrefStub; - let waitForInitializedStub; - let isBrowserPrivateStub; - let fakeSendTelemetry; - let getEarliestRecordedDateStub; - let getEventsByDateRangeStub; - let defaultSearchStub; - let scriptloaderStub; - let fakeRemoteL10n; - let getViewNodeStub; - - beforeEach(async () => { - sandbox = sinon.createSandbox(); - globals = new GlobalOverrider(); - instance = new _ToolbarPanelHub(); - waitForInitializedStub = sandbox.stub().resolves(); - fakeElementById = { - setAttribute: sandbox.stub(), - removeAttribute: sandbox.stub(), - querySelector: sandbox.stub().returns(null), - querySelectorAll: sandbox.stub().returns([]), - appendChild: sandbox.stub(), - addEventListener: sandbox.stub(), - hasAttribute: sandbox.stub(), - toggleAttribute: sandbox.stub(), - remove: sandbox.stub(), - removeChild: sandbox.stub(), - }; - fakeElementByTagName = { - setAttribute: sandbox.stub(), - removeAttribute: sandbox.stub(), - querySelector: sandbox.stub().returns(null), - querySelectorAll: sandbox.stub().returns([]), - appendChild: sandbox.stub(), - addEventListener: sandbox.stub(), - hasAttribute: sandbox.stub(), - toggleAttribute: sandbox.stub(), - remove: sandbox.stub(), - removeChild: sandbox.stub(), - }; - fakeDocument = { - getElementById: sandbox.stub().returns(fakeElementById), - getElementsByTagName: sandbox.stub().returns(fakeElementByTagName), - querySelector: sandbox.stub().returns({}), - createElement: tagName => { - const element = { - tagName, - classList: {}, - addEventListener: (ev, fn) => { - eventListeners[ev] = fn; - }, - appendChild: sandbox.stub(), - setAttribute: sandbox.stub(), - textContent: "", - }; - element.classList.add = sandbox.stub(); - element.classList.includes = className => - element.classList.add.firstCall.args[0] === className; - createdCustomElements.push(element); - return element; - }, - l10n: { - translateElements: sandbox.stub(), - translateFragment: sandbox.stub(), - formatMessages: sandbox.stub().resolves([{}]), - setAttributes: sandbox.stub(), - }, - }; - fakeWindow = { - // eslint-disable-next-line object-shorthand - DocumentFragment: function () { - return fakeElementById; - }, - document: fakeDocument, - browser: { - ownerDocument: fakeDocument, - }, - MozXULElement: { insertFTLIfNeeded: sandbox.stub() }, - ownerGlobal: { - openLinkIn: sandbox.stub(), - gBrowser: "gBrowser", - }, - PanelUI: { - panel: fakeElementById, - whatsNewPanel: fakeElementById, - }, - customElements: { get: sandbox.stub() }, - }; - everyWindowStub = { - registerCallback: sandbox.stub(), - unregisterCallback: sandbox.stub(), - }; - scriptloaderStub = { loadSubScript: sandbox.stub() }; - addObserverStub = sandbox.stub(); - removeObserverStub = sandbox.stub(); - getBoolPrefStub = sandbox.stub(); - setBoolPrefStub = sandbox.stub(); - fakeSendTelemetry = sandbox.stub(); - isBrowserPrivateStub = sandbox.stub(); - getEarliestRecordedDateStub = sandbox.stub().returns( - // A random date that's not the current timestamp - new Date() - 500 - ); - getEventsByDateRangeStub = sandbox.stub().returns([]); - getViewNodeStub = sandbox.stub().returns(fakeElementById); - defaultSearchStub = { defaultEngine: { name: "DDG" } }; - fakeRemoteL10n = { - l10n: {}, - reloadL10n: sandbox.stub(), - createElement: sandbox - .stub() - .callsFake((doc, el) => fakeDocument.createElement(el)), - }; - globals.set({ - EveryWindow: everyWindowStub, - Services: { - ...Services, - prefs: { - addObserver: addObserverStub, - removeObserver: removeObserverStub, - getBoolPref: getBoolPrefStub, - setBoolPref: setBoolPrefStub, - }, - search: defaultSearchStub, - scriptloader: scriptloaderStub, - }, - PrivateBrowsingUtils: { - isBrowserPrivate: isBrowserPrivateStub, - }, - TrackingDBService: { - getEarliestRecordedDate: getEarliestRecordedDateStub, - getEventsByDateRange: getEventsByDateRangeStub, - }, - SpecialMessageActions: { - handleAction: sandbox.stub(), - }, - RemoteL10n: fakeRemoteL10n, - PanelMultiView: { - getViewNode: getViewNodeStub, - }, - }); - }); - afterEach(() => { - instance.uninit(); - sandbox.restore(); - globals.restore(); - eventListeners = {}; - createdCustomElements = []; - }); - it("should create an instance", () => { - assert.ok(instance); - }); - it("should enableAppmenuButton() on init() just once", async () => { - instance.enableAppmenuButton = sandbox.stub(); - - await instance.init(waitForInitializedStub, { getMessages: () => {} }); - await instance.init(waitForInitializedStub, { getMessages: () => {} }); - - assert.calledOnce(instance.enableAppmenuButton); - - instance.uninit(); - - await instance.init(waitForInitializedStub, { getMessages: () => {} }); - - assert.calledTwice(instance.enableAppmenuButton); - }); - it("should unregisterCallback on uninit()", () => { - instance.uninit(); - assert.calledTwice(everyWindowStub.unregisterCallback); - }); - describe("#maybeLoadCustomElement", () => { - it("should not load customElements a second time", () => { - instance.maybeLoadCustomElement({ customElements: new Map() }); - instance.maybeLoadCustomElement({ - customElements: new Map([["remote-text", true]]), - }); - - assert.calledOnce(scriptloaderStub.loadSubScript); - }); - }); - describe("#toggleWhatsNewPref", () => { - it("should call Services.prefs.setBoolPref() with the opposite value", () => { - let checkbox = {}; - let event = { target: checkbox }; - // checkbox starts false - checkbox.checked = false; - - // toggling the checkbox to set the value to true; - // Preferences.set() gets called before the checkbox changes, - // so we have to call it with the opposite value. - instance.toggleWhatsNewPref(event); - - assert.calledOnce(setBoolPrefStub); - assert.calledWith( - setBoolPrefStub, - "browser.messaging-system.whatsNewPanel.enabled", - !checkbox.checked - ); - }); - it("should report telemetry with the opposite value", () => { - let sendUserEventTelemetryStub = sandbox.stub( - instance, - "sendUserEventTelemetry" - ); - let event = { - target: { checked: true, ownerGlobal: fakeWindow }, - }; - - instance.toggleWhatsNewPref(event); - - assert.calledOnce(sendUserEventTelemetryStub); - const { args } = sendUserEventTelemetryStub.firstCall; - assert.equal(args[1], "WNP_PREF_TOGGLE"); - assert.propertyVal(args[3].value, "prefValue", false); - }); - }); - describe("#enableAppmenuButton", () => { - it("should registerCallback on enableAppmenuButton() if there are messages", async () => { - await instance.init(waitForInitializedStub, { - getMessages: sandbox.stub().resolves([{}, {}]), - }); - // init calls `enableAppmenuButton` - everyWindowStub.registerCallback.resetHistory(); - - await instance.enableAppmenuButton(); - - assert.calledOnce(everyWindowStub.registerCallback); - assert.calledWithExactly( - everyWindowStub.registerCallback, - "appMenu-whatsnew-button", - sinon.match.func, - sinon.match.func - ); - }); - it("should not registerCallback on enableAppmenuButton() if there are no messages", async () => { - instance.init(waitForInitializedStub, { - getMessages: sandbox.stub().resolves([]), - }); - // init calls `enableAppmenuButton` - everyWindowStub.registerCallback.resetHistory(); - - await instance.enableAppmenuButton(); - - assert.notCalled(everyWindowStub.registerCallback); - }); - }); - describe("#disableAppmenuButton", () => { - it("should call the unregisterCallback", () => { - assert.notCalled(everyWindowStub.unregisterCallback); - - instance.disableAppmenuButton(); - - assert.calledOnce(everyWindowStub.unregisterCallback); - assert.calledWithExactly( - everyWindowStub.unregisterCallback, - "appMenu-whatsnew-button" - ); - }); - }); - describe("#enableToolbarButton", () => { - it("should registerCallback on enableToolbarButton if messages.length", async () => { - await instance.init(waitForInitializedStub, { - getMessages: sandbox.stub().resolves([{}, {}]), - }); - // init calls `enableAppmenuButton` - everyWindowStub.registerCallback.resetHistory(); - - await instance.enableToolbarButton(); - - assert.calledOnce(everyWindowStub.registerCallback); - assert.calledWithExactly( - everyWindowStub.registerCallback, - "whats-new-menu-button", - sinon.match.func, - sinon.match.func - ); - }); - it("should not registerCallback on enableToolbarButton if no messages", async () => { - await instance.init(waitForInitializedStub, { - getMessages: sandbox.stub().resolves([]), - }); - - await instance.enableToolbarButton(); - - assert.notCalled(everyWindowStub.registerCallback); - }); - }); - describe("Show/Hide functions", () => { - it("should unhide appmenu button on _showAppmenuButton()", async () => { - await instance._showAppmenuButton(fakeWindow); - - assert.equal(fakeElementById.hidden, false); - }); - it("should hide appmenu button on _hideAppmenuButton()", () => { - instance._hideAppmenuButton(fakeWindow); - assert.equal(fakeElementById.hidden, true); - }); - it("should not do anything if the window is closed", () => { - instance._hideAppmenuButton(fakeWindow, true); - assert.notCalled(global.PanelMultiView.getViewNode); - }); - it("should not throw if the element does not exist", () => { - let fn = instance._hideAppmenuButton.bind(null, { - browser: { ownerDocument: {} }, - }); - getViewNodeStub.returns(undefined); - assert.doesNotThrow(fn); - }); - it("should unhide toolbar button on _showToolbarButton()", async () => { - await instance._showToolbarButton(fakeWindow); - - assert.equal(fakeElementById.hidden, false); - }); - it("should hide toolbar button on _hideToolbarButton()", () => { - instance._hideToolbarButton(fakeWindow); - assert.equal(fakeElementById.hidden, true); - }); - }); - describe("#renderMessages", () => { - let getMessagesStub; - beforeEach(() => { - getMessagesStub = sandbox.stub(); - instance.init(waitForInitializedStub, { - getMessages: getMessagesStub, - sendTelemetry: fakeSendTelemetry, - }); - }); - it("should have correct state", async () => { - const messages = (await PanelTestProvider.getMessages()).filter( - m => m.template === "whatsnew_panel_message" - ); - - getMessagesStub.returns(messages); - const ev1 = sandbox.stub(); - ev1.withArgs("type").returns(1); // tracker - ev1.withArgs("count").returns(4); - const ev2 = sandbox.stub(); - ev2.withArgs("type").returns(4); // fingerprinter - ev2.withArgs("count").returns(3); - getEventsByDateRangeStub.returns([ - { getResultByName: ev1 }, - { getResultByName: ev2 }, - ]); - - await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - - assert.propertyVal(instance.state.contentArguments, "trackerCount", 4); - assert.propertyVal( - instance.state.contentArguments, - "fingerprinterCount", - 3 - ); - }); - it("should render messages to the panel on renderMessages()", async () => { - const messages = (await PanelTestProvider.getMessages()).filter( - m => m.template === "whatsnew_panel_message" - ); - messages[0].content.link_text = { string_id: "link_text_id" }; - - getMessagesStub.returns(messages); - const ev1 = sandbox.stub(); - ev1.withArgs("type").returns(1); // tracker - ev1.withArgs("count").returns(4); - const ev2 = sandbox.stub(); - ev2.withArgs("type").returns(4); // fingerprinter - ev2.withArgs("count").returns(3); - getEventsByDateRangeStub.returns([ - { getResultByName: ev1 }, - { getResultByName: ev2 }, - ]); - - await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - - for (let message of messages) { - assert.ok( - fakeRemoteL10n.createElement.args.find( - ([, , args]) => args && args.classList === "whatsNew-message-title" - ) - ); - if (message.content.layout === "tracking-protections") { - assert.ok( - fakeRemoteL10n.createElement.args.find( - ([, , args]) => - args && args.classList === "whatsNew-message-subtitle" - ) - ); - } - if (message.id === "WHATS_NEW_FINGERPRINTER_COUNTER_72") { - assert.ok( - fakeRemoteL10n.createElement.args.find( - ([, el, args]) => el === "h2" && args.content === 3 - ) - ); - } - assert.ok( - fakeRemoteL10n.createElement.args.find( - ([, , args]) => - args && args.classList === "whatsNew-message-content" - ) - ); - } - // Call the click handler to make coverage happy. - eventListeners.mouseup(); - assert.calledOnce(global.SpecialMessageActions.handleAction); - }); - it("should clear previous messages on 2nd renderMessages()", async () => { - const messages = (await PanelTestProvider.getMessages()).filter( - m => m.template === "whatsnew_panel_message" - ); - const removeStub = sandbox.stub(); - fakeElementById.querySelectorAll.onCall(0).returns([]); - fakeElementById.querySelectorAll - .onCall(1) - .returns([{ remove: removeStub }, { remove: removeStub }]); - - getMessagesStub.returns(messages); - - await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - - assert.calledTwice(removeStub); - }); - it("should sort based on order field value", async () => { - const messages = (await PanelTestProvider.getMessages()).filter( - m => - m.template === "whatsnew_panel_message" && - m.content.published_date === 1560969794394 - ); - - messages.forEach(m => (m.content.title = m.order)); - - getMessagesStub.returns(messages); - - await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - - // Select the title elements that are supposed to be set to the same - // value as the `order` field of the message - const titleEls = fakeRemoteL10n.createElement.args - .filter( - ([, , args]) => args && args.classList === "whatsNew-message-title" - ) - .map(([, , args]) => args.content); - assert.deepEqual(titleEls, [1, 2, 3]); - }); - it("should accept string for image attributes", async () => { - const messages = (await PanelTestProvider.getMessages()).filter( - m => m.id === "WHATS_NEW_70_1" - ); - getMessagesStub.returns(messages); - - await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - - const imageEl = createdCustomElements.find(el => el.tagName === "img"); - assert.calledOnce(imageEl.setAttribute); - assert.calledWithExactly( - imageEl.setAttribute, - "alt", - "Firefox Send Logo" - ); - }); - it("should set state values as data-attribute", async () => { - const message = (await PanelTestProvider.getMessages()).find( - m => m.template === "whatsnew_panel_message" - ); - getMessagesStub.returns([message]); - instance.state.contentArguments = { foo: "foo", bar: "bar" }; - - await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - - const [, , args] = fakeRemoteL10n.createElement.args.find( - ([, , elArgs]) => elArgs && elArgs.attributes - ); - assert.ok(args); - // Currently this.state.contentArguments has 8 different entries - assert.lengthOf(Object.keys(args.attributes), 8); - assert.equal( - args.attributes.searchEngineName, - defaultSearchStub.defaultEngine.name - ); - }); - it("should only render unique dates (no duplicates)", async () => { - const messages = (await PanelTestProvider.getMessages()).filter( - m => m.template === "whatsnew_panel_message" - ); - const uniqueDates = [ - ...new Set(messages.map(m => m.content.published_date)), - ]; - getMessagesStub.returns(messages); - - await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - - const dateElements = fakeRemoteL10n.createElement.args.filter( - ([, el, args]) => - el === "p" && args.classList === "whatsNew-message-date" - ); - assert.lengthOf(dateElements, uniqueDates.length); - }); - it("should listen for panelhidden and remove the toolbar button", async () => { - getMessagesStub.returns([]); - fakeDocument.getElementById - .withArgs("customizationui-widget-panel") - .returns(null); - - await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - - assert.notCalled(fakeElementById.addEventListener); - }); - it("should attach doCommand cbs that handle user actions", async () => { - const messages = (await PanelTestProvider.getMessages()).filter( - m => m.template === "whatsnew_panel_message" - ); - getMessagesStub.returns(messages); - - await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - - const messageEl = createdCustomElements.find( - el => - el.tagName === "div" && el.classList.includes("whatsNew-message-body") - ); - const anchorEl = createdCustomElements.find(el => el.tagName === "a"); - - assert.notCalled(global.SpecialMessageActions.handleAction); - - messageEl.doCommand(); - anchorEl.doCommand(); - - assert.calledTwice(global.SpecialMessageActions.handleAction); - }); - it("should listen for panelhidden and remove the toolbar button", async () => { - getMessagesStub.returns([]); - - await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - - assert.calledOnce(fakeElementById.addEventListener); - assert.calledWithExactly( - fakeElementById.addEventListener, - "popuphidden", - sinon.match.func, - { - once: true, - } - ); - const [, cb] = fakeElementById.addEventListener.firstCall.args; - - assert.notCalled(everyWindowStub.unregisterCallback); - - cb(); - - assert.calledOnce(everyWindowStub.unregisterCallback); - assert.calledWithExactly( - everyWindowStub.unregisterCallback, - "whats-new-menu-button" - ); - }); - describe("#IMPRESSION", () => { - it("should dispatch a IMPRESSION for messages", async () => { - // means panel is triggered from the toolbar button - fakeElementById.hasAttribute.returns(true); - const messages = (await PanelTestProvider.getMessages()).filter( - m => m.template === "whatsnew_panel_message" - ); - getMessagesStub.returns(messages); - const spy = sandbox.spy(instance, "sendUserEventTelemetry"); - - await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - - assert.calledOnce(spy); - assert.calledOnce(fakeSendTelemetry); - assert.propertyVal( - spy.firstCall.args[2], - "id", - messages - .map(({ id }) => id) - .sort() - .join(",") - ); - }); - it("should dispatch a CLICK for clicking a message", async () => { - // means panel is triggered from the toolbar button - fakeElementById.hasAttribute.returns(true); - // Force to render the message - fakeElementById.querySelector.returns(null); - const messages = (await PanelTestProvider.getMessages()).filter( - m => m.template === "whatsnew_panel_message" - ); - getMessagesStub.returns([messages[0]]); - const spy = sandbox.spy(instance, "sendUserEventTelemetry"); - - await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - - assert.calledOnce(spy); - assert.calledOnce(fakeSendTelemetry); - - spy.resetHistory(); - - // Message click event listener cb - eventListeners.mouseup(); - - assert.calledOnce(spy); - assert.calledWithExactly(spy, fakeWindow, "CLICK", messages[0]); - }); - it("should dispatch a IMPRESSION with toolbar_dropdown", async () => { - // means panel is triggered from the toolbar button - fakeElementById.hasAttribute.returns(true); - const messages = (await PanelTestProvider.getMessages()).filter( - m => m.template === "whatsnew_panel_message" - ); - getMessagesStub.resolves(messages); - const spy = sandbox.spy(instance, "sendUserEventTelemetry"); - const panelPingId = messages - .map(({ id }) => id) - .sort() - .join(","); - - await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - - assert.calledOnce(spy); - assert.calledWithExactly( - spy, - fakeWindow, - "IMPRESSION", - { - id: panelPingId, - }, - { - value: { - view: "toolbar_dropdown", - }, - } - ); - assert.calledOnce(fakeSendTelemetry); - const { - args: [dispatchPayload], - } = fakeSendTelemetry.lastCall; - assert.propertyVal(dispatchPayload, "type", "TOOLBAR_PANEL_TELEMETRY"); - assert.propertyVal(dispatchPayload.data, "message_id", panelPingId); - assert.deepEqual(dispatchPayload.data.event_context, { - view: "toolbar_dropdown", - }); - }); - it("should dispatch a IMPRESSION with application_menu", async () => { - // means panel is triggered as a subview in the application menu - fakeElementById.hasAttribute.returns(false); - const messages = (await PanelTestProvider.getMessages()).filter( - m => m.template === "whatsnew_panel_message" - ); - getMessagesStub.resolves(messages); - const spy = sandbox.spy(instance, "sendUserEventTelemetry"); - const panelPingId = messages - .map(({ id }) => id) - .sort() - .join(","); - - await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - - assert.calledOnce(spy); - assert.calledWithExactly( - spy, - fakeWindow, - "IMPRESSION", - { - id: panelPingId, - }, - { - value: { - view: "application_menu", - }, - } - ); - assert.calledOnce(fakeSendTelemetry); - const { - args: [dispatchPayload], - } = fakeSendTelemetry.lastCall; - assert.propertyVal(dispatchPayload, "type", "TOOLBAR_PANEL_TELEMETRY"); - assert.propertyVal(dispatchPayload.data, "message_id", panelPingId); - assert.deepEqual(dispatchPayload.data.event_context, { - view: "application_menu", - }); - }); - }); - describe("#forceShowMessage", () => { - const panelSelector = "PanelUI-whatsNew-message-container"; - let removeMessagesSpy; - let renderMessagesStub; - let addEventListenerStub; - let messages; - let browser; - beforeEach(async () => { - messages = (await PanelTestProvider.getMessages()).find( - m => m.id === "WHATS_NEW_70_1" - ); - removeMessagesSpy = sandbox.spy(instance, "removeMessages"); - renderMessagesStub = sandbox.spy(instance, "renderMessages"); - addEventListenerStub = fakeElementById.addEventListener; - browser = { ownerGlobal: fakeWindow, ownerDocument: fakeDocument }; - fakeElementById.querySelectorAll.returns([fakeElementById]); - }); - it("should call removeMessages when forcing a message to show", () => { - instance.forceShowMessage(browser, messages); - - assert.calledWithExactly(removeMessagesSpy, fakeWindow, panelSelector); - }); - it("should call renderMessages when forcing a message to show", () => { - instance.forceShowMessage(browser, messages); - - assert.calledOnce(renderMessagesStub); - assert.calledWithExactly( - renderMessagesStub, - fakeWindow, - fakeDocument, - panelSelector, - { - force: true, - messages: Array.isArray(messages) ? messages : [messages], - } - ); - }); - it("should cleanup after the panel is hidden when forcing a message to show", () => { - instance.forceShowMessage(browser, messages); - - assert.calledOnce(addEventListenerStub); - assert.calledWithExactly( - addEventListenerStub, - "popuphidden", - sinon.match.func - ); - - const [, cb] = addEventListenerStub.firstCall.args; - // Reset the call count from the first `forceShowMessage` call - removeMessagesSpy.resetHistory(); - cb({ target: { ownerGlobal: fakeWindow } }); - - assert.calledOnce(removeMessagesSpy); - assert.calledWithExactly(removeMessagesSpy, fakeWindow, panelSelector); - }); - it("should exit gracefully if called before a browser exists", () => { - instance.forceShowMessage(null, messages); - assert.neverCalledWith(removeMessagesSpy, fakeWindow, panelSelector); - }); - }); - }); -}); diff --git a/browser/components/asrouter/tests/unit/content-src/components/ASRouterAdmin.test.jsx b/browser/components/asrouter/tests/unit/content-src/components/ASRouterAdmin.test.jsx index 46d5704107..c5b0d09b39 100644 --- a/browser/components/asrouter/tests/unit/content-src/components/ASRouterAdmin.test.jsx +++ b/browser/components/asrouter/tests/unit/content-src/components/ASRouterAdmin.test.jsx @@ -1,4 +1,7 @@ -import { ASRouterAdminInner } from "content-src/components/ASRouterAdmin/ASRouterAdmin"; +import { + ASRouterAdminInner, + toBinary, +} from "content-src/components/ASRouterAdmin/ASRouterAdmin"; import { ASRouterUtils } from "content-src/asrouter-utils"; import { GlobalOverrider } from "test/unit/utils"; import React from "react"; @@ -259,4 +262,43 @@ describe("ASRouterAdmin", () => { }); }); }); + describe("toBinary", () => { + // Bringing the 'fromBinary' function over from + // messagepreview to prove it works + function fromBinary(encoded) { + const binary = atob(decodeURIComponent(encoded)); + const bytes = new Uint8Array(binary.length); + for (let i = 0; i < bytes.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return String.fromCharCode(...new Uint16Array(bytes.buffer)); + } + + it("correctly encodes a latin string", () => { + const testString = "Hi I am a test string"; + const expectedResult = + "SABpACAASQAgAGEAbQAgAGEAIAB0AGUAcwB0ACAAcwB0AHIAaQBuAGcA"; + + const encodedResult = toBinary(testString); + + assert.equal(encodedResult, expectedResult); + + const decodedResult = fromBinary(encodedResult); + + assert.equal(decodedResult, testString); + }); + + it("correctly encodes a non-latin string", () => { + const nonLatinString = "тестовое сообщение"; + const expectedResult = "QgQ1BEEEQgQ+BDIEPgQ1BCAAQQQ+BD4EMQRJBDUEPQQ4BDUE"; + + const encodedResult = toBinary("тестовое сообщение"); + + assert.equal(encodedResult, expectedResult); + + const decodedResult = fromBinary(encodedResult); + + assert.equal(decodedResult, nonLatinString); + }); + }); }); diff --git a/browser/components/asrouter/tests/unit/unit-entry.js b/browser/components/asrouter/tests/unit/unit-entry.js index f2046a81cb..2464b02c58 100644 --- a/browser/components/asrouter/tests/unit/unit-entry.js +++ b/browser/components/asrouter/tests/unit/unit-entry.js @@ -14,7 +14,7 @@ import FxMSCommonSchema from "../../content-src/schemas/FxMSCommon.schema.json"; import { MESSAGE_TYPE_LIST, MESSAGE_TYPE_HASH, -} from "modules/ActorConstants.sys.mjs"; +} from "modules/ActorConstants.mjs"; enzyme.configure({ adapter: new Adapter() }); diff --git a/browser/components/asrouter/tests/xpcshell/head.js b/browser/components/asrouter/tests/xpcshell/head.js index 0c6cec1ac8..fa361a00b9 100644 --- a/browser/components/asrouter/tests/xpcshell/head.js +++ b/browser/components/asrouter/tests/xpcshell/head.js @@ -81,10 +81,6 @@ async function makeValidators() { "resource://testing-common/UpdateAction.schema.json", { common: true } ), - whatsnew_panel_message: await schemaValidatorFor( - "resource://testing-common/WhatsNewMessage.schema.json", - { common: true } - ), feature_callout: await schemaValidatorFor( // For now, Feature Callout and Spotlight share a common schema "resource://testing-common/Spotlight.schema.json", diff --git a/browser/components/asrouter/tests/xpcshell/test_PanelTestProvider.js b/browser/components/asrouter/tests/xpcshell/test_PanelTestProvider.js index 3523355659..7e9892a595 100644 --- a/browser/components/asrouter/tests/xpcshell/test_PanelTestProvider.js +++ b/browser/components/asrouter/tests/xpcshell/test_PanelTestProvider.js @@ -22,7 +22,6 @@ add_task(async function test_PanelTestProvider() { cfr_doorhanger: 1, milestone_message: 0, update_action: 1, - whatsnew_panel_message: 7, spotlight: 3, feature_callout: 1, pb_newtab: 2, diff --git a/browser/components/asrouter/yamscripts.yml b/browser/components/asrouter/yamscripts.yml index de16c269a4..e52063c911 100644 --- a/browser/components/asrouter/yamscripts.yml +++ b/browser/components/asrouter/yamscripts.yml @@ -19,6 +19,7 @@ scripts: lint: =>lint build: =>bundle:admin unit: karma start karma.mc.config.js + import: =>import-rollouts tddmc: karma start karma.mc.config.js --tdd |