summaryrefslogtreecommitdiffstats
path: root/browser/components/asrouter
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/asrouter')
-rw-r--r--browser/components/asrouter/.eslintrc.js2
-rw-r--r--browser/components/asrouter/actors/ASRouterChild.sys.mjs6
-rw-r--r--browser/components/asrouter/bin/import-rollouts.js4
-rw-r--r--browser/components/asrouter/content-src/asrouter-utils.mjs6
-rw-r--r--browser/components/asrouter/content-src/components/ASRouterAdmin/ASRouterAdmin.jsx16
-rw-r--r--browser/components/asrouter/content-src/schemas/BackgroundTaskMessagingExperiment.schema.json69
-rw-r--r--browser/components/asrouter/content-src/schemas/MessagingExperiment.schema.json415
-rwxr-xr-xbrowser/components/asrouter/content-src/schemas/make-schemas.py3
-rw-r--r--browser/components/asrouter/content-src/styles/_feature-callout.scss6
-rw-r--r--browser/components/asrouter/content-src/templates/OnboardingMessage/WhatsNewMessage.schema.json73
-rw-r--r--browser/components/asrouter/content/asrouter-admin.bundle.js52
-rw-r--r--browser/components/asrouter/docs/targeting-attributes.md11
-rw-r--r--browser/components/asrouter/modules/ASRouter.sys.mjs47
-rw-r--r--browser/components/asrouter/modules/ASRouterParentProcessMessageHandler.sys.mjs9
-rw-r--r--browser/components/asrouter/modules/ASRouterTargeting.sys.mjs9
-rw-r--r--browser/components/asrouter/modules/ActorConstants.mjs (renamed from browser/components/asrouter/modules/ActorConstants.sys.mjs)3
-rw-r--r--browser/components/asrouter/modules/FeatureCallout.sys.mjs8
-rw-r--r--browser/components/asrouter/modules/MomentsPageHub.sys.mjs2
-rw-r--r--browser/components/asrouter/modules/OnboardingMessageProvider.sys.mjs100
-rw-r--r--browser/components/asrouter/modules/PanelTestProvider.sys.mjs128
-rw-r--r--browser/components/asrouter/modules/ToolbarBadgeHub.sys.mjs36
-rw-r--r--browser/components/asrouter/modules/ToolbarPanelHub.sys.mjs544
-rw-r--r--browser/components/asrouter/moz.build4
-rw-r--r--browser/components/asrouter/package.json1
-rw-r--r--browser/components/asrouter/tests/InflightAssetsMessageProvider.sys.mjs112
-rw-r--r--browser/components/asrouter/tests/NimbusRolloutMessageProvider.sys.mjs421
-rw-r--r--browser/components/asrouter/tests/browser/browser.toml5
-rw-r--r--browser/components/asrouter/tests/browser/browser_asrouter_bug1761522.js2
-rw-r--r--browser/components/asrouter/tests/browser/browser_asrouter_cfr.js117
-rw-r--r--browser/components/asrouter/tests/browser/browser_asrouter_keyboard_cfr.js162
-rw-r--r--browser/components/asrouter/tests/browser/browser_asrouter_milestone_message_cfr.js78
-rw-r--r--browser/components/asrouter/tests/browser/browser_asrouter_momentspagehub.js4
-rw-r--r--browser/components/asrouter/tests/unit/ASRouter.test.js107
-rw-r--r--browser/components/asrouter/tests/unit/ASRouterChild.test.js3
-rw-r--r--browser/components/asrouter/tests/unit/ASRouterParent.test.js2
-rw-r--r--browser/components/asrouter/tests/unit/ASRouterParentProcessMessageHandler.test.js27
-rw-r--r--browser/components/asrouter/tests/unit/ToolbarBadgeHub.test.js166
-rw-r--r--browser/components/asrouter/tests/unit/ToolbarPanelHub.test.js760
-rw-r--r--browser/components/asrouter/tests/unit/content-src/components/ASRouterAdmin.test.jsx44
-rw-r--r--browser/components/asrouter/tests/unit/unit-entry.js2
-rw-r--r--browser/components/asrouter/tests/xpcshell/head.js4
-rw-r--r--browser/components/asrouter/tests/xpcshell/test_PanelTestProvider.js1
-rw-r--r--browser/components/asrouter/yamscripts.yml1
43 files changed, 1188 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/styles/_feature-callout.scss b/browser/components/asrouter/content-src/styles/_feature-callout.scss
index 66770c2238..40137fd29a 100644
--- a/browser/components/asrouter/content-src/styles/_feature-callout.scss
+++ b/browser/components/asrouter/content-src/styles/_feature-callout.scss
@@ -42,6 +42,12 @@
@include dark-theme;
}
+ @at-root :root[lwt-newtab-brighttext] #{&}.lwtNewtab {
+ color-scheme: dark;
+
+ @include dark-theme;
+ }
+
@media (prefers-contrast) {
@include hcm-theme;
}
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/FeatureCallout.sys.mjs b/browser/components/asrouter/modules/FeatureCallout.sys.mjs
index 01998662f6..5f0e266a4e 100644
--- a/browser/components/asrouter/modules/FeatureCallout.sys.mjs
+++ b/browser/components/asrouter/modules/FeatureCallout.sys.mjs
@@ -1834,6 +1834,14 @@ export class FeatureCallout {
"simulateContent",
!!this.theme.simulateContent
);
+ this._container.classList.toggle(
+ "lwtNewtab",
+ !!(
+ this.theme.lwtNewtab !== false &&
+ this.theme.simulateContent &&
+ ["themed-content", "newtab"].includes(this.theme.preset)
+ )
+ );
for (const type of ["light", "dark", "hcm"]) {
const scheme = this.theme[type];
for (const name of FeatureCallout.themePropNames) {
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