Bug 1724300 - Remove What's New Panel, ToolbarPanelHub components & related tests & references r=desktop-theme-reviewers,omc-reviewers,aminomancer,pdahiya,emilio,devtools-reviewers,firefox-desktop-core-reviewers ,home-newtab-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D201867
This commit is contained in:
Emily McMinn 2024-03-21 22:05:28 +00:00
parent d9fed649f4
commit 079ff41016
43 changed files with 28 additions and 2162 deletions

View File

@ -1705,7 +1705,6 @@ pref("browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts",
// ASRouter provider configuration
pref("browser.newtabpage.activity-stream.asrouter.providers.cfr", "{\"id\":\"cfr\",\"enabled\":true,\"type\":\"remote-settings\",\"collection\":\"cfr\",\"updateCycleInMs\":3600000}");
pref("browser.newtabpage.activity-stream.asrouter.providers.whats-new-panel", "{\"id\":\"whats-new-panel\",\"enabled\":false,\"type\":\"remote-settings\",\"collection\":\"whats-new-panel\",\"updateCycleInMs\":3600000}");
pref("browser.newtabpage.activity-stream.asrouter.providers.message-groups", "{\"id\":\"message-groups\",\"enabled\":true,\"type\":\"remote-settings\",\"collection\":\"message-groups\",\"updateCycleInMs\":3600000}");
pref("browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments", "{\"id\":\"messaging-experiments\",\"enabled\":true,\"type\":\"remote-experiments\",\"updateCycleInMs\":3600000}");
@ -1817,9 +1816,6 @@ pref("browser.aboutwelcome.screens", "");
// Used to enable window modal onboarding
pref("browser.aboutwelcome.showModal", false);
// The pref that controls if the What's New panel is enabled.
pref("browser.messaging-system.whatsNewPanel.enabled", true);
// Experiment Manager
// See Console.sys.mjs LOG_LEVELS for all possible values
pref("messaging-system.log", "warn");

View File

@ -3,16 +3,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<html:template id="appMenu-viewCache">
<panelview id="appMenu-mainView" class="PanelUI-subView">
<vbox class="panel-subview-body">
<toolbarbutton id="appMenu-whatsnew-button"
class="subviewbutton subviewbutton-iconic subviewbutton-nav"
hidden="true"
closemenu="none"
oncommand="PanelUI.showSubView('PanelUI-whatsNew', this)"/>
</vbox>
</panelview>
<!-- This is a placeholder app menu which should be replaced with the "real"
Proton app menu before the Proton pref starts getting enabled. -->
<panelview id="appMenu-protonMainView" class="PanelUI-subView"
@ -759,29 +749,6 @@
</vbox>
</panelview>
<panelview id="PanelUI-whatsNew" class="PanelUI-subView" mainview-with-header="true">
<hbox id="PanelUI-whatsNew-title" class="panel-header">
<html:h1>
<html:span data-l10n-id="whatsnew-panel-header"></html:span>
</html:h1>
</hbox>
<toolbarseparator/>
<vbox class="panel-subview-body">
<toolbaritem id="PanelUI-whatsNew-content"
orient="vertical"
smoothscroll="false">
<html:div id="PanelUI-whatsNew-message-container" role="document">
<!-- What's New messages will be rendered here -->
</html:div>
</toolbaritem>
</vbox>
<toolbarseparator/>
<checkbox id="panelMenu-toggleWhatsNew"
class="panelMenu-toggleWhatsNew-checkbox"
onclick="ToolbarPanelHub.toggleWhatsNewPref(event)"
data-l10n-id="whatsnew-panel-footer-checkbox"/>
</panelview>
<panelview id="reset-pbm-panel" class="PanelUI-subView" role="document">
<vbox id="reset-pbm-panel-container" role="alertdialog" aria-labelledby="reset-pbm-panel-header">
<hbox id="reset-pbm-panel-header">

View File

@ -2529,12 +2529,12 @@ var gProtectionsHandler = {
};
const doc = event.target.ownerDocument;
const container = doc.getElementById("messaging-system-message-container");
const container = doc.getElementById("info-message-container");
const infoButton = doc.getElementById("protections-popup-info-button");
const panelContainer = doc.getElementById("protections-popup");
const toggleMessage = () => {
const learnMoreLink = doc.querySelector(
"#messaging-system-message-container .text-link"
"#info-message-container .text-link"
);
if (learnMoreLink) {
container.toggleAttribute("disabled");

View File

@ -250,8 +250,7 @@ toolbar[customizing] > .overflow-button {
display: none;
}
toolbar[customizing] #ion-button,
toolbar[customizing] #whats-new-menu-button {
toolbar[customizing] #ion-button {
display: none;
}

View File

@ -491,13 +491,6 @@
tooltiptext="Ion"
onmousedown="switchToTabHavingURI('about:ion', true);"
onkeypress="switchToTabHavingURI('about:ion', true);"/>
<toolbarbutton id="whats-new-menu-button"
class="toolbarbutton-1"
delegatesanchor="true"
hidden="true"
badged="true"
onmousedown="PanelUI.showSubView('PanelUI-whatsNew', this, event);"
onkeypress="PanelUI.showSubView('PanelUI-whatsNew', this, event);"/>
<toolbarbutton id="PanelUI-menu-button"
class="toolbarbutton-1"
delegatesanchor="true"

View File

@ -51,10 +51,10 @@ add_task(async function testPanelInfoMessage() {
});
// Test that the info message is displayed when the panel opens
let container = document.getElementById("messaging-system-message-container");
let container = document.getElementById("info-message-container");
let message = document.getElementById("protections-popup-message");
let learnMoreLink = document.querySelector(
"#messaging-system-message-container .text-link"
"#info-message-container .text-link"
);
// Check the visibility of the info message.

View File

@ -4462,14 +4462,6 @@ BrowserGlue.prototype = {
// Check the default branch as enterprise policies can set prefs there.
const defaultPrefs = Services.prefs.getDefaultBranch("");
if (
!defaultPrefs.getBoolPref(
"browser.messaging-system.whatsNewPanel.enabled",
true
)
) {
return "no-whatsNew";
}
if (!defaultPrefs.getBoolPref("browser.aboutwelcome.enabled", true)) {
return "no-welcome";
}

View File

@ -101,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:

View File

@ -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",

View File

@ -982,89 +982,6 @@
},
"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."
}
},
"additionalProperties": true,
"required": [
"published_date",
"title",
"body",
"cta_url",
"bucket_id"
]
},
"template": {
"type": "string",
"const": "whatsnew_panel_message"
}
},
"required": ["order"],
"additionalProperties": true
},
"Message": {
"type": "object",
"properties": {
@ -1093,8 +1010,7 @@
"feature_callout",
"toast_notification",
"toolbar_badge",
"update_action",
"whatsnew_panel_message"
"update_action"
]
},
"frequency": {
@ -1326,21 +1242,6 @@
"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"
}
}
]
},

View File

@ -83,9 +83,6 @@ SCHEMAS = [
"UpdateAction": (
SCHEMA_DIR / "OnboardingMessage" / "UpdateAction.schema.json"
),
"WhatsNewMessage": (
SCHEMA_DIR / "OnboardingMessage" / "WhatsNewMessage.schema.json"
),
},
bundle_common=True,
test_corpus={

View File

@ -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
}

View File

@ -122,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",
@ -140,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",

View File

@ -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

View File

@ -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, {
@ -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"

View File

@ -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);
}

View File

@ -72,12 +72,6 @@ XPCOMUtils.defineLazyPreferenceGetter(
"browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
true
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"isWhatsNewPanelEnabled",
"browser.messaging-system.whatsNewPanel.enabled",
false
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"hasAccessedFxAPanel",
@ -704,9 +698,6 @@ const TargetingGetters = {
get hasAccessedFxAPanel() {
return lazy.hasAccessedFxAPanel;
},
get isWhatsNewPanelEnabled() {
return lazy.isWhatsNewPanelEnabled;
},
get userPrefs() {
return {
cfrFeatures: lazy.cfrFeaturesUserPref,

View File

@ -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",

View File

@ -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();

View File

@ -19,134 +19,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",

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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",

View File

@ -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}`,
],
],

View File

@ -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(
@ -369,15 +358,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();

View File

@ -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,

View File

@ -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(

View File

@ -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);
});
});
});

View File

@ -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);
});
});
});
});

View File

@ -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",

View File

@ -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,

View File

@ -36,8 +36,8 @@
</box>
<toolbarseparator></toolbarseparator>
<html:div id="messaging-system-message-container" disabled="true">
<!-- Messaging System Messages will render in this container -->
<html:div id="info-message-container" disabled="true">
<!-- Info message will render in this container -->
</html:div>
</vbox>

View File

@ -6,7 +6,6 @@ ChromeUtils.defineESModuleGetters(this, {
AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.sys.mjs",
NewTabUtils: "resource://gre/modules/NewTabUtils.sys.mjs",
PanelMultiView: "resource:///modules/PanelMultiView.sys.mjs",
ToolbarPanelHub: "resource:///modules/asrouter/ToolbarPanelHub.jsm",
});
/**
@ -167,9 +166,6 @@ const PanelUI = {
this.menuButton.removeEventListener("mousedown", this);
this.menuButton.removeEventListener("keypress", this);
CustomizableUI.removeListener(this);
if (this.whatsNewPanel) {
this.whatsNewPanel.removeEventListener("ViewShowing", this);
}
},
/**
@ -303,11 +299,6 @@ const PanelUI = {
case "activate":
this.updateNotifications();
break;
case "ViewShowing":
if (aEvent.target == this.whatsNewPanel) {
this.onWhatsNewPanelShowing();
}
break;
}
},
@ -412,7 +403,6 @@ const PanelUI = {
return;
}
this.ensureWhatsNewInitialized(viewNode);
this.ensurePanicViewInitialized(viewNode);
let container = aAnchor.closest("panelmultiview");
@ -496,24 +486,6 @@ const PanelUI = {
}
},
/**
* Sets up the event listener for when the What's New panel is shown.
*
* @param {panelview} panelView The What's New panelview.
*/
ensureWhatsNewInitialized(panelView) {
if (panelView.id != "PanelUI-whatsNew" || panelView._initialized) {
return;
}
if (!this.whatsNewPanel) {
this.whatsNewPanel = panelView;
}
panelView._initialized = true;
panelView.addEventListener("ViewShowing", this);
},
/**
* Adds FTL before appending the panic view markup to the main DOM.
*
@ -532,17 +504,6 @@ const PanelUI = {
panelView._initialized = true;
},
/**
* When the What's New panel is showing, we fetch the messages to show.
*/
onWhatsNewPanelShowing() {
ToolbarPanelHub.renderMessages(
window,
document,
"PanelUI-whatsNew-message-container"
);
},
/**
* NB: The enable- and disableSingleSubviewPanelAnimations methods only
* affect the hiding/showing animations of single-subview panels (tempPanel

View File

@ -454,8 +454,7 @@ export class TelemetryFeed {
event = await this.applyCFRPolicy(event);
break;
case "badge_user_event":
case "whats-new-panel_user_event":
event = await this.applyWhatsNewPolicy(event);
event = await this.applyToolbarBadgePolicy(event);
break;
case "infobar_user_event":
event = await this.applyInfoBarPolicy(event);
@ -509,12 +508,12 @@ export class TelemetryFeed {
* Per Bug 1482134, all the metrics for What's New panel use client_id in
* all the release channels
*/
async applyWhatsNewPolicy(ping) {
async applyToolbarBadgePolicy(ping) {
ping.client_id = await this.telemetryClientId;
ping.browser_session_id = lazy.browserSessionId;
// Attach page info to `event_context` if there is a session associated with this ping
delete ping.action;
return { ping, pingType: "whats-new-panel" };
return { ping, pingType: "toolbar-badge" };
}
async applyInfoBarPolicy(ping) {

View File

@ -1010,7 +1010,7 @@ messaging_system:
type: string
description: >
Type of event the ping is capturing.
e.g. "cfr", "whats-new-panel", "onboarding"
e.g. "cfr", "onboarding"
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1825863
data_reviews:

View File

@ -947,18 +947,18 @@ add_task(
}
);
add_task(async function test_applyWhatsNewPolicy() {
add_task(async function test_applyToolbarBadgePolicy() {
info(
"TelemetryFeed.applyWhatsNewPolicy should set client_id and set pingType"
"TelemetryFeed.applyToolbarBadgePolicy should set client_id and set pingType"
);
let instance = new TelemetryFeed();
let { ping, pingType } = await instance.applyWhatsNewPolicy({});
let { ping, pingType } = await instance.applyToolbarBadgePolicy({});
Assert.equal(
ping.client_id,
Services.prefs.getCharPref("toolkit.telemetry.cachedClientID")
);
Assert.equal(pingType, "whats-new-panel");
Assert.equal(pingType, "toolbar-badge");
});
add_task(async function test_applyInfoBarPolicy() {
@ -1288,10 +1288,10 @@ add_task(async function test_createASRouterEvent_call_correctPolicy() {
message_id: "onboarding_message_01",
});
testCallCorrectPolicy("applyWhatsNewPolicy", {
action: "whats-new-panel_user_event",
event: "CLICK_BUTTON",
message_id: "whats-new-panel_message_01",
testCallCorrectPolicy("applyToolbarBadgePolicy", {
action: "badge_user_event",
event: "IMPRESSION",
message_id: "badge_message_01",
});
testCallCorrectPolicy("applyMomentsPolicy", {

View File

@ -8,10 +8,6 @@
scrollbar-color: color-mix(in srgb, currentColor 26%, transparent) transparent;
}
#appMenu-mainView > .panel-subview-body > .panel-banner-item {
padding-inline-start: 18px;
}
.subviewbutton:not([image],[targetURI],.bookmark-item) > .menu-iconic-left {
display: none;
}

View File

@ -1655,13 +1655,13 @@ radiogroup:focus-visible > .subviewradio[focused="true"] {
}
#protections-popup {
#messaging-system-message-container {
#info-message-container {
height: 260px;
overflow: hidden;
transition: margin-bottom .25s;
}
#messaging-system-message-container[disabled] {
#info-message-container[disabled] {
/* Offset the height when hidden. This makes the panel content
* cover the info message and reveal it as it slides down, rather
* than the info message growing in height. */
@ -1669,7 +1669,7 @@ radiogroup:focus-visible > .subviewradio[focused="true"] {
pointer-events: none;
}
#messaging-system-message-container[disabled] #protections-popup-message {
#info-message-container[disabled] #protections-popup-message {
opacity: 0;
}
}

View File

@ -451,10 +451,6 @@ toolbarbutton.bookmark-item {
list-style-image: url("chrome://global/skin/icons/folder.svg");
}
#whats-new-menu-button {
list-style-image: url("chrome://global/skin/icons/whatsnew.svg");
}
#ion-button {
list-style-image: url("chrome://browser/skin/ion.svg");
}

View File

@ -116,6 +116,7 @@ devtools.jar:
skin/images/import.svg (themes/images/import.svg)
skin/images/pane-collapse.svg (themes/images/pane-collapse.svg)
skin/images/pane-expand.svg (themes/images/pane-expand.svg)
skin/images/whatsnew.svg (themes/images/whatsnew.svg)
skin/images/help.svg (themes/images/help.svg)
skin/images/report.svg (themes/images/report.svg)
skin/images/reveal.svg (themes/images/reveal.svg)

View File

@ -87,7 +87,7 @@
}
.notificationbox .messageImage[data-type="new"] {
background-image: url("chrome://global/skin/icons/whatsnew.svg");
background-image: url("chrome://devtools/skin/images/whatsnew.svg");
fill: var(--theme-highlight-blue);
}

View File

Before

Width:  |  Height:  |  Size: 897 B

After

Width:  |  Height:  |  Size: 897 B

View File

@ -124,7 +124,6 @@
skin/classic/global/icons/arrow-up.svg (../../shared/icons/arrow-up.svg)
skin/classic/global/icons/warning.svg (../../shared/icons/warning.svg)
skin/classic/global/icons/warning-fill-12.svg (../../shared/icons/warning-fill-12.svg)
skin/classic/global/icons/whatsnew.svg (../../shared/icons/whatsnew.svg)
skin/classic/global/illustrations/about-rights.svg (../../shared/illustrations/about-rights.svg)
skin/classic/global/illustrations/about-license.svg (../../shared/illustrations/about-license.svg)
skin/classic/global/illustrations/error-malformed-url.svg (../../shared/illustrations/error-malformed-url.svg)