Bug 1920799 - Add a new messaging surface to the AppMenu and PXI menus for describing the value of signing into an FxA. r=pdahiya,Gijs,desktop-theme-reviewers,omc-reviewers,home-newtab-reviewers,fluent-reviewers,hjones,skhamis,nbarrett
Developing tests in a later patch in this series. Differential Revision: https://phabricator.services.mozilla.com/D223409
This commit is contained in:
@@ -10,6 +10,10 @@
|
|||||||
<toolbarbutton id="appMenu-update-banner" class="panel-banner-item subviewbutton"
|
<toolbarbutton id="appMenu-update-banner" class="panel-banner-item subviewbutton"
|
||||||
wrap="true"
|
wrap="true"
|
||||||
hidden="true"/>
|
hidden="true"/>
|
||||||
|
|
||||||
|
<toolbaritem id="appMenu-fxa-menu-message"
|
||||||
|
closemenu="none">
|
||||||
|
</toolbaritem>
|
||||||
<toolbaritem id="appMenu-fxa-status2"
|
<toolbaritem id="appMenu-fxa-status2"
|
||||||
closemenu="none"
|
closemenu="none"
|
||||||
class="subviewbutton toolbaritem-combined-buttons">
|
class="subviewbutton toolbaritem-combined-buttons">
|
||||||
@@ -568,6 +572,9 @@
|
|||||||
|
|
||||||
<panelview id="PanelUI-fxa" class="PanelUI-subView">
|
<panelview id="PanelUI-fxa" class="PanelUI-subView">
|
||||||
<vbox id="PanelUI-fxa-menu" class="panel-subview-body">
|
<vbox id="PanelUI-fxa-menu" class="panel-subview-body">
|
||||||
|
<toolbaritem id="PanelUI-fxa-menu-message"
|
||||||
|
closemenu="none">
|
||||||
|
</toolbaritem>
|
||||||
<toolbarbutton id="fxa-manage-account-button"
|
<toolbarbutton id="fxa-manage-account-button"
|
||||||
align="center"
|
align="center"
|
||||||
class="subviewbutton"
|
class="subviewbutton"
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ const { UIState } = ChromeUtils.importESModule(
|
|||||||
);
|
);
|
||||||
|
|
||||||
ChromeUtils.defineESModuleGetters(this, {
|
ChromeUtils.defineESModuleGetters(this, {
|
||||||
|
ASRouter: "resource:///modules/asrouter/ASRouter.sys.mjs",
|
||||||
EnsureFxAccountsWebChannel:
|
EnsureFxAccountsWebChannel:
|
||||||
"resource://gre/modules/FxAccountsWebChannel.sys.mjs",
|
"resource://gre/modules/FxAccountsWebChannel.sys.mjs",
|
||||||
|
|
||||||
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
|
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||||
FxAccounts: "resource://gre/modules/FxAccounts.sys.mjs",
|
FxAccounts: "resource://gre/modules/FxAccounts.sys.mjs",
|
||||||
|
MenuMessage: "resource:///modules/asrouter/MenuMessage.sys.mjs",
|
||||||
SyncedTabs: "resource://services-sync/SyncedTabs.sys.mjs",
|
SyncedTabs: "resource://services-sync/SyncedTabs.sys.mjs",
|
||||||
SyncedTabsManagement: "resource://services-sync/SyncedTabs.sys.mjs",
|
SyncedTabsManagement: "resource://services-sync/SyncedTabs.sys.mjs",
|
||||||
Weave: "resource://services-sync/main.sys.mjs",
|
Weave: "resource://services-sync/main.sys.mjs",
|
||||||
@@ -697,6 +699,19 @@ var gSync = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onFxAPanelViewShowing(panelview) {
|
onFxAPanelViewShowing(panelview) {
|
||||||
|
let messageId = panelview.getAttribute(
|
||||||
|
MenuMessage.SHOWING_FXA_MENU_MESSAGE_ATTR
|
||||||
|
);
|
||||||
|
if (messageId) {
|
||||||
|
MenuMessage.recordMenuMessageTelemetry(
|
||||||
|
"IMPRESSION",
|
||||||
|
MenuMessage.SOURCES.PXI_MENU,
|
||||||
|
messageId
|
||||||
|
);
|
||||||
|
let message = ASRouter.getMessageById(messageId);
|
||||||
|
ASRouter.addImpression(message);
|
||||||
|
}
|
||||||
|
|
||||||
let syncNowBtn = panelview.querySelector(".syncnow-label");
|
let syncNowBtn = panelview.querySelector(".syncnow-label");
|
||||||
let l10nId = syncNowBtn.getAttribute(
|
let l10nId = syncNowBtn.getAttribute(
|
||||||
this._isCurrentlySyncing
|
this._isCurrentlySyncing
|
||||||
@@ -730,6 +745,7 @@ var gSync = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onFxAPanelViewHiding(panelview) {
|
onFxAPanelViewHiding(panelview) {
|
||||||
|
MenuMessage.hidePxiMenuMessage(gBrowser.selectedBrowser);
|
||||||
panelview.syncedTabsPanelList.destroy();
|
panelview.syncedTabsPanelList.destroy();
|
||||||
panelview.syncedTabsPanelList = null;
|
panelview.syncedTabsPanelList = null;
|
||||||
},
|
},
|
||||||
@@ -954,7 +970,7 @@ var gSync = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleAccountPanel(
|
async toggleAccountPanel(
|
||||||
anchor = document.getElementById("fxa-toolbar-menu-button"),
|
anchor = document.getElementById("fxa-toolbar-menu-button"),
|
||||||
aEvent
|
aEvent
|
||||||
) {
|
) {
|
||||||
@@ -972,6 +988,19 @@ var gSync = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
anchor == document.getElementById("fxa-toolbar-menu-button") &&
|
||||||
|
anchor.getAttribute("open") != "true"
|
||||||
|
) {
|
||||||
|
if (ASRouter.initialized) {
|
||||||
|
await ASRouter.sendTriggerMessage({
|
||||||
|
browser: gBrowser.selectedBrowser,
|
||||||
|
id: "menuOpened",
|
||||||
|
context: { source: MenuMessage.SOURCES.PXI_MENU },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We read the state that's been set on the root node, since that makes
|
// We read the state that's been set on the root node, since that makes
|
||||||
// it easier to test the various front-end states without having to actually
|
// it easier to test the various front-end states without having to actually
|
||||||
// have UIState know about it.
|
// have UIState know about it.
|
||||||
|
|||||||
@@ -637,6 +637,13 @@ customElements.setElementCreationCallback("screenshots-buttons", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
customElements.setElementCreationCallback("fxa-menu-message", () => {
|
||||||
|
ChromeUtils.importESModule(
|
||||||
|
"chrome://browser/content/asrouter/components/fxa-menu-message.mjs",
|
||||||
|
{ global: "current" }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
var gBrowser;
|
var gBrowser;
|
||||||
var gContextMenu = null; // nsContextMenu instance
|
var gContextMenu = null; // nsContextMenu instance
|
||||||
var gMultiProcessBrowser = window.docShell.QueryInterface(
|
var gMultiProcessBrowser = window.docShell.QueryInterface(
|
||||||
|
|||||||
@@ -135,6 +135,10 @@ async function getMessageValidators(skipValidation) {
|
|||||||
"./content-src/templates/OnboardingMessage/Spotlight.schema.json",
|
"./content-src/templates/OnboardingMessage/Spotlight.schema.json",
|
||||||
{ common: true }
|
{ common: true }
|
||||||
),
|
),
|
||||||
|
menu_message: await getValidator(
|
||||||
|
"./content-src/templates/OnboardingMessage/MenuMessage.schema.json",
|
||||||
|
{ common: true }
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
messageValidators.milestone_message = messageValidators.cfr_doorhanger;
|
messageValidators.milestone_message = messageValidators.cfr_doorhanger;
|
||||||
|
|||||||
@@ -638,6 +638,98 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"MenuMessage": {
|
||||||
|
"$schema": "https://json-schema.org/draft/2019-09/schema",
|
||||||
|
"$id": "file:///MenuMessage.schema.json",
|
||||||
|
"title": "MenuMessage",
|
||||||
|
"description": "A template for messages that appear within our menus.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/Message"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"content": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"messageType": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The subtype of the message.",
|
||||||
|
"enum": [
|
||||||
|
"fxa_cta"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"primaryText": {
|
||||||
|
"$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/localizableText",
|
||||||
|
"description": "The primary text for the message, which offers the value proposition to the user."
|
||||||
|
},
|
||||||
|
"secondaryText": {
|
||||||
|
"$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/localizableText",
|
||||||
|
"description": "The second text for the message, which offers more detail on the value proposition to the user."
|
||||||
|
},
|
||||||
|
"closeAction": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Action dispatched by the button."
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"additionalProperties": true,
|
||||||
|
"description": "The action to take upon clicking the close button."
|
||||||
|
},
|
||||||
|
"primaryAction": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Action dispatched by the button."
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"additionalProperties": true,
|
||||||
|
"description": "The action to take upon clicking the primary action button."
|
||||||
|
},
|
||||||
|
"primaryActionText": {
|
||||||
|
"$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/localizableText",
|
||||||
|
"description": "The label for the primary action."
|
||||||
|
},
|
||||||
|
"imageURL": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "URL for image to use with the content."
|
||||||
|
},
|
||||||
|
"imageVerticalOffset": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "The margin-block-start value to apply to the image in pixels."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"template": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "menu_message"
|
||||||
|
},
|
||||||
|
"testingTriggerContext": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"app_menu",
|
||||||
|
"pxi_menu"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
"NewtabPromoMessage": {
|
"NewtabPromoMessage": {
|
||||||
"$schema": "https://json-schema.org/draft/2019-09/schema",
|
"$schema": "https://json-schema.org/draft/2019-09/schema",
|
||||||
"$id": "file:///NewtabPromoMessage.schema.json",
|
"$id": "file:///NewtabPromoMessage.schema.json",
|
||||||
@@ -1180,6 +1272,7 @@
|
|||||||
"cfr_doorhanger",
|
"cfr_doorhanger",
|
||||||
"milestone_message",
|
"milestone_message",
|
||||||
"infobar",
|
"infobar",
|
||||||
|
"menu_message",
|
||||||
"pb_newtab",
|
"pb_newtab",
|
||||||
"spotlight",
|
"spotlight",
|
||||||
"feature_callout",
|
"feature_callout",
|
||||||
@@ -1388,6 +1481,25 @@
|
|||||||
"$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/InfoBar"
|
"$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/InfoBar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"if": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"template": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"menu_message"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"template"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/MenuMessage"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"if": {
|
"if": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ SCHEMAS = [
|
|||||||
SCHEMA_DIR / "CFR" / "templates" / "ExtensionDoorhanger.schema.json"
|
SCHEMA_DIR / "CFR" / "templates" / "ExtensionDoorhanger.schema.json"
|
||||||
),
|
),
|
||||||
"InfoBar": SCHEMA_DIR / "CFR" / "templates" / "InfoBar.schema.json",
|
"InfoBar": SCHEMA_DIR / "CFR" / "templates" / "InfoBar.schema.json",
|
||||||
|
"MenuMessage": (
|
||||||
|
SCHEMA_DIR / "OnboardingMessage" / "MenuMessage.schema.json"
|
||||||
|
),
|
||||||
"NewtabPromoMessage": (
|
"NewtabPromoMessage": (
|
||||||
SCHEMA_DIR / "PBNewtab" / "NewtabPromoMessage.schema.json"
|
SCHEMA_DIR / "PBNewtab" / "NewtabPromoMessage.schema.json"
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2019-09/schema",
|
||||||
|
"$id": "file:///MenuMessage.schema.json",
|
||||||
|
"title": "MenuMessage",
|
||||||
|
"description": "A template for messages that appear within our menus.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "file:///FxMSCommon.schema.json#/$defs/Message"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"content": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"messageType": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The subtype of the message.",
|
||||||
|
"enum": ["fxa_cta"]
|
||||||
|
},
|
||||||
|
"primaryText": {
|
||||||
|
"$ref": "file:///FxMSCommon.schema.json#/$defs/localizableText",
|
||||||
|
"description": "The primary text for the message, which offers the value proposition to the user."
|
||||||
|
},
|
||||||
|
"secondaryText": {
|
||||||
|
"$ref": "file:///FxMSCommon.schema.json#/$defs/localizableText",
|
||||||
|
"description": "The second text for the message, which offers more detail on the value proposition to the user."
|
||||||
|
},
|
||||||
|
"closeAction": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Action dispatched by the button."
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["type"],
|
||||||
|
"additionalProperties": true,
|
||||||
|
"description": "The action to take upon clicking the close button."
|
||||||
|
},
|
||||||
|
"primaryAction": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Action dispatched by the button."
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["type"],
|
||||||
|
"additionalProperties": true,
|
||||||
|
"description": "The action to take upon clicking the primary action button."
|
||||||
|
},
|
||||||
|
"primaryActionText": {
|
||||||
|
"$ref": "file:///FxMSCommon.schema.json#/$defs/localizableText",
|
||||||
|
"description": "The label for the primary action."
|
||||||
|
},
|
||||||
|
"imageURL": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "URL for image to use with the content."
|
||||||
|
},
|
||||||
|
"imageVerticalOffset": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "The margin-block-start value to apply to the image in pixels."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"template": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "menu_message"
|
||||||
|
},
|
||||||
|
"testingTriggerContext": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["app_menu", "pxi_menu"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
||||||
@@ -153,6 +153,7 @@ const MESSAGE_TYPE_LIST = [
|
|||||||
"INFOBAR_TELEMETRY",
|
"INFOBAR_TELEMETRY",
|
||||||
"SPOTLIGHT_TELEMETRY",
|
"SPOTLIGHT_TELEMETRY",
|
||||||
"TOAST_NOTIFICATION_TELEMETRY",
|
"TOAST_NOTIFICATION_TELEMETRY",
|
||||||
|
"MENU_MESSAGE_TELEMETRY",
|
||||||
"AS_ROUTER_TELEMETRY_USER_EVENT",
|
"AS_ROUTER_TELEMETRY_USER_EVENT",
|
||||||
|
|
||||||
// Admin types
|
// Admin types
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
:host {
|
||||||
|
margin-inline: var(--arrowpanel-menuitem-margin-inline, var(--space-xsmall));
|
||||||
|
margin-block: var(--space-xsmall);
|
||||||
|
--illustration-margin-block-offset: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: var(--background-color-information);
|
||||||
|
border-radius: var(--border-radius-small);
|
||||||
|
padding-block: var(--arrowpanel-menuitem-padding-block, var(--space-small));
|
||||||
|
padding-inline: var(--arrowpanel-menuitem-padding-inline, var(--space-small));
|
||||||
|
margin-block-start: var(--space-small);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#close-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
inset-inline-end: 0;
|
||||||
|
padding-block: var(--arrowpanel-menuitem-padding-block, var(--space-small));
|
||||||
|
padding-inline: var(--arrowpanel-menuitem-padding-inline, var(--space-small));
|
||||||
|
}
|
||||||
|
|
||||||
|
#container:not([has-image]) > #illustration-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container[has-image] > #illustration-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-block-start: var(--illustration-margin-block-offset);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#primary {
|
||||||
|
font-size: 1.154em;
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
margin-block-end: var(--space-xsmall);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sign-up-button {
|
||||||
|
margin-block-start: var(--space-medium);
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import { html } from "chrome://global/content/vendor/lit.all.mjs";
|
||||||
|
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
|
||||||
|
// eslint-disable-next-line import/no-unassigned-import
|
||||||
|
import "chrome://global/content/elements/moz-button.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This widget is for a message that can be displayed in panelview menus when
|
||||||
|
* the user is signed out to encourage them to sign in.
|
||||||
|
*/
|
||||||
|
export default class FxAMenuMessage extends MozLitElement {
|
||||||
|
static shadowRootOptions = {
|
||||||
|
...MozLitElement.shadowRootOptions,
|
||||||
|
delegatesFocus: true,
|
||||||
|
};
|
||||||
|
static properties = {
|
||||||
|
imageURL: { type: String },
|
||||||
|
buttonText: { type: String },
|
||||||
|
primaryText: { type: String },
|
||||||
|
secondaryText: { type: String },
|
||||||
|
};
|
||||||
|
static queries = {
|
||||||
|
signUpButton: "#sign-up-button",
|
||||||
|
closeButton: "#close-button",
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.addEventListener(
|
||||||
|
"keydown",
|
||||||
|
event => {
|
||||||
|
let keyCode = event.code;
|
||||||
|
switch (keyCode) {
|
||||||
|
case "ArrowLeft":
|
||||||
|
// Intentional fall-through
|
||||||
|
case "ArrowRight":
|
||||||
|
// Intentional fall-through
|
||||||
|
case "ArrowUp":
|
||||||
|
// Intentional fall-through
|
||||||
|
case "ArrowDown": {
|
||||||
|
if (this.shadowRoot.activeElement === this.signUpButton) {
|
||||||
|
this.closeButton.focus();
|
||||||
|
} else {
|
||||||
|
this.signUpButton.focus();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ capture: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClose(event) {
|
||||||
|
// Keep the menu open by stopping the click event from
|
||||||
|
// propagating up.
|
||||||
|
event.stopPropagation();
|
||||||
|
this.dispatchEvent(new CustomEvent("FxAMenuMessage:Close"), {
|
||||||
|
bubbles: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSignUp() {
|
||||||
|
this.dispatchEvent(new CustomEvent("FxAMenuMessage:SignUp"), {
|
||||||
|
bubbles: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="chrome://browser/content/asrouter/components/fxa-menu-message.css"
|
||||||
|
/>
|
||||||
|
<div id="container" ?has-image="${this.imageURL}">
|
||||||
|
<moz-button
|
||||||
|
id="close-button"
|
||||||
|
@click=${this.handleClose}
|
||||||
|
type="ghost"
|
||||||
|
iconsrc="chrome://global/skin/icons/close-12.svg"
|
||||||
|
tabindex="2"
|
||||||
|
data-l10n-id="fxa-menu-message-close-button"
|
||||||
|
>
|
||||||
|
</moz-button>
|
||||||
|
<div id="illustration-container">
|
||||||
|
<img id="illustration" role="presentation" src="${this.imageURL}" />
|
||||||
|
</div>
|
||||||
|
<div id="primary">${this.primaryText}</div>
|
||||||
|
<div id="secondary">${this.secondaryText}</div>
|
||||||
|
<moz-button
|
||||||
|
id="sign-up-button"
|
||||||
|
@click=${this.handleSignUp}
|
||||||
|
type="primary"
|
||||||
|
tabindex="1"
|
||||||
|
autofocus
|
||||||
|
>${this.buttonText}</moz-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("fxa-menu-message", FxAMenuMessage);
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/* 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 import/no-unresolved
|
||||||
|
import { html } from "lit.all.mjs";
|
||||||
|
import "chrome://global/content/elements/moz-card.mjs";
|
||||||
|
import "./fxa-menu-message.mjs";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Domain-specific UI Widgets/ASRouter/FxA Menu Message",
|
||||||
|
component: "fxa-menu-message",
|
||||||
|
argTypes: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const Template = ({
|
||||||
|
buttonText,
|
||||||
|
imageURL,
|
||||||
|
primaryText,
|
||||||
|
secondaryText,
|
||||||
|
imageVerticalOffset,
|
||||||
|
}) => html`
|
||||||
|
<moz-card style="width: 22.5rem;">
|
||||||
|
<fxa-menu-message
|
||||||
|
buttonText="${buttonText}"
|
||||||
|
primaryText="${primaryText}"
|
||||||
|
secondaryText="${secondaryText}"
|
||||||
|
imageURL="${imageURL}"
|
||||||
|
style="--illustration-margin-block-offset: ${imageVerticalOffset}px"
|
||||||
|
>
|
||||||
|
</fxa-menu-message>
|
||||||
|
</moz-card>
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
Default.args = {
|
||||||
|
buttonText: "Sign up",
|
||||||
|
imageURL:
|
||||||
|
"chrome://activity-stream/content/data/content/assets/fox-doodle-waving-static.png",
|
||||||
|
primaryText: "Bounce between devices",
|
||||||
|
secondaryText:
|
||||||
|
"Sync and encrypt your bookmarks, passwords, and more on all your devices.",
|
||||||
|
imageVerticalOffset: -24,
|
||||||
|
};
|
||||||
@@ -6,6 +6,8 @@ browser.jar:
|
|||||||
content/browser/asrouter/asrouter-admin.html (content/asrouter-admin.html)
|
content/browser/asrouter/asrouter-admin.html (content/asrouter-admin.html)
|
||||||
content/browser/asrouter/asrouter-admin.bundle.js (content/asrouter-admin.bundle.js)
|
content/browser/asrouter/asrouter-admin.bundle.js (content/asrouter-admin.bundle.js)
|
||||||
content/browser/asrouter/components/ASRouterAdmin/ASRouterAdmin.css (content/components/ASRouterAdmin/ASRouterAdmin.css)
|
content/browser/asrouter/components/ASRouterAdmin/ASRouterAdmin.css (content/components/ASRouterAdmin/ASRouterAdmin.css)
|
||||||
|
content/browser/asrouter/components/fxa-menu-message.mjs (content/components/fxa-menu-message/fxa-menu-message.mjs)
|
||||||
|
content/browser/asrouter/components/fxa-menu-message.css (content/components/fxa-menu-message/fxa-menu-message.css)
|
||||||
content/browser/asrouter/components/remote-text.js (content/components/remote-text.js)
|
content/browser/asrouter/components/remote-text.js (content/components/remote-text.js)
|
||||||
content/browser/asrouter/render.js (content/render.js)
|
content/browser/asrouter/render.js (content/render.js)
|
||||||
content/browser/asrouter/schemas/BackgroundTaskMessagingExperiment.schema.json (content-src/schemas/BackgroundTaskMessagingExperiment.schema.json)
|
content/browser/asrouter/schemas/BackgroundTaskMessagingExperiment.schema.json (content-src/schemas/BackgroundTaskMessagingExperiment.schema.json)
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
|||||||
InfoBar: "resource:///modules/asrouter/InfoBar.sys.mjs",
|
InfoBar: "resource:///modules/asrouter/InfoBar.sys.mjs",
|
||||||
KintoHttpClient: "resource://services-common/kinto-http-client.sys.mjs",
|
KintoHttpClient: "resource://services-common/kinto-http-client.sys.mjs",
|
||||||
MacAttribution: "resource:///modules/MacAttribution.sys.mjs",
|
MacAttribution: "resource:///modules/MacAttribution.sys.mjs",
|
||||||
|
MenuMessage: "resource:///modules/asrouter/MenuMessage.sys.mjs",
|
||||||
MomentsPageHub: "resource:///modules/asrouter/MomentsPageHub.sys.mjs",
|
MomentsPageHub: "resource:///modules/asrouter/MomentsPageHub.sys.mjs",
|
||||||
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||||
PanelTestProvider: "resource:///modules/asrouter/PanelTestProvider.sys.mjs",
|
PanelTestProvider: "resource:///modules/asrouter/PanelTestProvider.sys.mjs",
|
||||||
@@ -1478,6 +1479,9 @@ export class _ASRouter {
|
|||||||
case "bookmarks_bar_button":
|
case "bookmarks_bar_button":
|
||||||
lazy.BookmarksBarButton.showBookmarksBarButton(browser, message);
|
lazy.BookmarksBarButton.showBookmarksBarButton(browser, message);
|
||||||
break;
|
break;
|
||||||
|
case "menu_message":
|
||||||
|
lazy.MenuMessage.showMenuMessage(browser, message, trigger, force);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { message };
|
return { message };
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export class ASRouterParentProcessMessageHandler {
|
|||||||
case msg.MOMENTS_PAGE_TELEMETRY:
|
case msg.MOMENTS_PAGE_TELEMETRY:
|
||||||
case msg.DOORHANGER_TELEMETRY:
|
case msg.DOORHANGER_TELEMETRY:
|
||||||
case msg.SPOTLIGHT_TELEMETRY:
|
case msg.SPOTLIGHT_TELEMETRY:
|
||||||
|
case msg.MENU_MESSAGE_TELEMETRY:
|
||||||
case msg.TOAST_NOTIFICATION_TELEMETRY: {
|
case msg.TOAST_NOTIFICATION_TELEMETRY: {
|
||||||
return this.handleTelemetry({ type, data });
|
return this.handleTelemetry({ type, data });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export const MESSAGE_TYPE_LIST = [
|
|||||||
"INFOBAR_TELEMETRY",
|
"INFOBAR_TELEMETRY",
|
||||||
"SPOTLIGHT_TELEMETRY",
|
"SPOTLIGHT_TELEMETRY",
|
||||||
"TOAST_NOTIFICATION_TELEMETRY",
|
"TOAST_NOTIFICATION_TELEMETRY",
|
||||||
|
"MENU_MESSAGE_TELEMETRY",
|
||||||
"AS_ROUTER_TELEMETRY_USER_EVENT",
|
"AS_ROUTER_TELEMETRY_USER_EVENT",
|
||||||
|
|
||||||
// Admin types
|
// Admin types
|
||||||
|
|||||||
232
browser/components/asrouter/modules/MenuMessage.sys.mjs
Normal file
232
browser/components/asrouter/modules/MenuMessage.sys.mjs
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
/* 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 = {};
|
||||||
|
|
||||||
|
ChromeUtils.defineESModuleGetters(lazy, {
|
||||||
|
AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.sys.mjs",
|
||||||
|
ASRouter: "resource:///modules/asrouter/ASRouter.sys.mjs",
|
||||||
|
PanelMultiView: "resource:///modules/PanelMultiView.sys.mjs",
|
||||||
|
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
|
||||||
|
RemoteL10n: "resource:///modules/asrouter/RemoteL10n.sys.mjs",
|
||||||
|
SpecialMessageActions:
|
||||||
|
"resource://messaging-system/lib/SpecialMessageActions.sys.mjs",
|
||||||
|
UIState: "resource://services-sync/UIState.sys.mjs",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const MenuMessage = {
|
||||||
|
SOURCES: Object.freeze({
|
||||||
|
APP_MENU: "app_menu",
|
||||||
|
PXI_MENU: "pxi_menu",
|
||||||
|
}),
|
||||||
|
|
||||||
|
SHOWING_FXA_MENU_MESSAGE_ATTR: "showing-fxa-menu-message",
|
||||||
|
|
||||||
|
async showMenuMessage(browser, message, trigger, force) {
|
||||||
|
if (!browser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let win = browser.ownerGlobal;
|
||||||
|
|
||||||
|
if (!win || lazy.PrivateBrowsingUtils.isWindowPrivate(win)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let source = trigger?.context?.source || message.testingTriggerContext;
|
||||||
|
|
||||||
|
switch (source) {
|
||||||
|
case MenuMessage.SOURCES.APP_MENU: {
|
||||||
|
this.showAppMenuMessage(browser, message, force);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MenuMessage.SOURCES.PXI_MENU: {
|
||||||
|
this.showPxiMenuMessage(browser, message, force);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async showAppMenuMessage(browser, message, force) {
|
||||||
|
const win = browser.ownerGlobal;
|
||||||
|
const msgContainer = this.hideAppMenuMessage(browser);
|
||||||
|
|
||||||
|
// This version of the browser only supports the fxa_cta version
|
||||||
|
// of this message in the AppMenu. We also don't draw focus away from any
|
||||||
|
// existing AppMenuNotifications.
|
||||||
|
if (
|
||||||
|
!message ||
|
||||||
|
message.content.messageType !== "fxa_cta" ||
|
||||||
|
lazy.AppMenuNotifications.activeNotification
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since we know this is an fxa_cta message, we know that if we're already
|
||||||
|
// signed in, we don't want to show it in the AppMenu.
|
||||||
|
if (lazy.UIState.get().status === lazy.UIState.STATUS_SIGNED_IN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let msgElement = await this.constructFxAMessage(
|
||||||
|
win,
|
||||||
|
message,
|
||||||
|
MenuMessage.SOURCES.APP_MENU
|
||||||
|
);
|
||||||
|
|
||||||
|
msgElement.addEventListener("FxAMenuMessage:Close", () => {
|
||||||
|
win.PanelUI.mainView.removeAttribute(
|
||||||
|
MenuMessage.SHOWING_FXA_MENU_MESSAGE_ATTR
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
msgContainer.appendChild(msgElement);
|
||||||
|
|
||||||
|
win.PanelUI.mainView.setAttribute(
|
||||||
|
MenuMessage.SHOWING_FXA_MENU_MESSAGE_ATTR,
|
||||||
|
message.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (force) {
|
||||||
|
win.PanelUI.show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
hideAppMenuMessage(browser) {
|
||||||
|
const win = browser.ownerGlobal;
|
||||||
|
const document = browser.ownerDocument;
|
||||||
|
const msgContainer = lazy.PanelMultiView.getViewNode(
|
||||||
|
document,
|
||||||
|
"appMenu-fxa-menu-message"
|
||||||
|
);
|
||||||
|
msgContainer.innerHTML = "";
|
||||||
|
win.PanelUI.mainView.removeAttribute(
|
||||||
|
MenuMessage.SHOWING_FXA_MENU_MESSAGE_ATTR
|
||||||
|
);
|
||||||
|
|
||||||
|
return msgContainer;
|
||||||
|
},
|
||||||
|
|
||||||
|
async showPxiMenuMessage(browser, message, force) {
|
||||||
|
const win = browser.ownerGlobal;
|
||||||
|
const { document } = win;
|
||||||
|
const msgContainer = this.hidePxiMenuMessage(browser);
|
||||||
|
|
||||||
|
// This version of the browser only supports the fxa_cta version
|
||||||
|
// of this message in the PXI menu.
|
||||||
|
if (!message || message.content.messageType !== "fxa_cta") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since we know this is an fxa_cta message, we know that if we're already
|
||||||
|
// signed in, we don't want to show it in the AppMenu.
|
||||||
|
if (lazy.UIState.get().status === lazy.UIState.STATUS_SIGNED_IN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let msgElement = await this.constructFxAMessage(
|
||||||
|
win,
|
||||||
|
message,
|
||||||
|
MenuMessage.SOURCES.PXI_MENU
|
||||||
|
);
|
||||||
|
let fxaPanelView = lazy.PanelMultiView.getViewNode(document, "PanelUI-fxa");
|
||||||
|
|
||||||
|
msgElement.addEventListener("FxAMenuMessage:Close", () => {
|
||||||
|
fxaPanelView.removeAttribute(MenuMessage.SHOWING_FXA_MENU_MESSAGE_ATTR);
|
||||||
|
});
|
||||||
|
|
||||||
|
msgContainer.appendChild(msgElement);
|
||||||
|
|
||||||
|
fxaPanelView.setAttribute(
|
||||||
|
MenuMessage.SHOWING_FXA_MENU_MESSAGE_ATTR,
|
||||||
|
message.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (force) {
|
||||||
|
await win.gSync.toggleAccountPanel(
|
||||||
|
document.getElementById("fxa-toolbar-menu-button"),
|
||||||
|
new MouseEvent("mousedown")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
hidePxiMenuMessage(browser) {
|
||||||
|
const document = browser.ownerDocument;
|
||||||
|
const msgContainer = lazy.PanelMultiView.getViewNode(
|
||||||
|
document,
|
||||||
|
"PanelUI-fxa-menu-message"
|
||||||
|
);
|
||||||
|
msgContainer.innerHTML = "";
|
||||||
|
let fxaPanelView = lazy.PanelMultiView.getViewNode(document, "PanelUI-fxa");
|
||||||
|
fxaPanelView.removeAttribute(MenuMessage.SHOWING_FXA_MENU_MESSAGE_ATTR);
|
||||||
|
return msgContainer;
|
||||||
|
},
|
||||||
|
|
||||||
|
async constructFxAMessage(win, message, source) {
|
||||||
|
let { document, gBrowser } = win;
|
||||||
|
|
||||||
|
win.MozXULElement.insertFTLIfNeeded("browser/newtab/asrouter.ftl");
|
||||||
|
|
||||||
|
const msgElement = document.createElement("fxa-menu-message");
|
||||||
|
msgElement.imageURL = message.content.imageURL;
|
||||||
|
msgElement.buttonText = await lazy.RemoteL10n.formatLocalizableText(
|
||||||
|
message.content.primaryActionText
|
||||||
|
);
|
||||||
|
msgElement.primaryText = await lazy.RemoteL10n.formatLocalizableText(
|
||||||
|
message.content.primaryText
|
||||||
|
);
|
||||||
|
msgElement.secondaryText = await lazy.RemoteL10n.formatLocalizableText(
|
||||||
|
message.content.secondaryText
|
||||||
|
);
|
||||||
|
msgElement.dataset.navigableWithTabOnly = "true";
|
||||||
|
msgElement.style.setProperty(
|
||||||
|
"--illustration-margin-block-offset",
|
||||||
|
`${message.content.imageVerticalOffset}px`
|
||||||
|
);
|
||||||
|
|
||||||
|
msgElement.addEventListener("FxAMenuMessage:Close", () => {
|
||||||
|
msgElement.remove();
|
||||||
|
|
||||||
|
this.recordMenuMessageTelemetry("DISMISS", source, message.id);
|
||||||
|
|
||||||
|
lazy.SpecialMessageActions.handleAction(
|
||||||
|
message.content.closeAction,
|
||||||
|
gBrowser.selectedBrowser
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
msgElement.addEventListener("FxAMenuMessage:SignUp", () => {
|
||||||
|
this.recordMenuMessageTelemetry("CLICK", source, message.id);
|
||||||
|
|
||||||
|
// Depending on the source that showed the message, we'll want to set
|
||||||
|
// a particular entrypoint in the data payload in the event that we're
|
||||||
|
// opening up the FxA sign-up page.
|
||||||
|
let clonedPrimaryAction = structuredClone(message.content.primaryAction);
|
||||||
|
if (source === MenuMessage.SOURCES.APP_MENU) {
|
||||||
|
clonedPrimaryAction.data.entrypoint = "fxa_app_menu";
|
||||||
|
} else if (source === MenuMessage.SOURCES.PXI_MENU) {
|
||||||
|
clonedPrimaryAction.data.entrypoint = "fxa_avatar_menu";
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy.SpecialMessageActions.handleAction(
|
||||||
|
clonedPrimaryAction,
|
||||||
|
gBrowser.selectedBrowser
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return msgElement;
|
||||||
|
},
|
||||||
|
|
||||||
|
recordMenuMessageTelemetry(event, source, messageId) {
|
||||||
|
let ping = {
|
||||||
|
message_id: messageId,
|
||||||
|
event,
|
||||||
|
source,
|
||||||
|
};
|
||||||
|
lazy.ASRouter.dispatchCFRAction({
|
||||||
|
type: "MENU_MESSAGE_TELEMETRY",
|
||||||
|
data: { action: "menu_message_user_event", ...ping },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -809,6 +809,44 @@ const MESSAGES = () => [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "FXA_ACCOUNTS_APPMENU_PROTECT_BROWSING_DATA",
|
||||||
|
template: "menu_message",
|
||||||
|
content: {
|
||||||
|
messageType: "fxa_cta",
|
||||||
|
primaryText: "Bounce between devices",
|
||||||
|
secondaryText:
|
||||||
|
"Sync and encrypt your bookmarks, passwords, and more on all your devices.",
|
||||||
|
primaryActionText: "Sign up",
|
||||||
|
primaryAction: {
|
||||||
|
type: "FXA_SIGNIN_FLOW",
|
||||||
|
data: {
|
||||||
|
where: "tab",
|
||||||
|
extraParams: {
|
||||||
|
utm_source: "firefox-desktop",
|
||||||
|
utm_medium: "product",
|
||||||
|
utm_campaign: "some-campaign",
|
||||||
|
utm_content: "some-content",
|
||||||
|
},
|
||||||
|
autoClose: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
closeAction: {
|
||||||
|
type: "BLOCK_MESSAGE",
|
||||||
|
data: {
|
||||||
|
id: "FXA_ACCOUNTS_APPMENU_PROTECT_BROWSING_DATA",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
imageURL:
|
||||||
|
"chrome://activity-stream/content/data/content/assets/fox-doodle-waving-static.png",
|
||||||
|
imageVerticalOffset: -24,
|
||||||
|
},
|
||||||
|
skip_in_tests: "TODO",
|
||||||
|
trigger: {
|
||||||
|
id: "menuOpened",
|
||||||
|
},
|
||||||
|
testingTriggerContext: "app_menu",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const PanelTestProvider = {
|
export const PanelTestProvider = {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ EXTRA_JS_MODULES.asrouter += [
|
|||||||
"modules/FeatureCalloutBroker.sys.mjs",
|
"modules/FeatureCalloutBroker.sys.mjs",
|
||||||
"modules/FeatureCalloutMessages.sys.mjs",
|
"modules/FeatureCalloutMessages.sys.mjs",
|
||||||
"modules/InfoBar.sys.mjs",
|
"modules/InfoBar.sys.mjs",
|
||||||
|
"modules/MenuMessage.sys.mjs",
|
||||||
"modules/MessagingExperimentConstants.sys.mjs",
|
"modules/MessagingExperimentConstants.sys.mjs",
|
||||||
"modules/MomentsPageHub.sys.mjs",
|
"modules/MomentsPageHub.sys.mjs",
|
||||||
"modules/OnboardingMessageProvider.sys.mjs",
|
"modules/OnboardingMessageProvider.sys.mjs",
|
||||||
@@ -55,6 +56,7 @@ TESTING_JS_MODULES += [
|
|||||||
"content-src/templates/CFR/templates/ExtensionDoorhanger.schema.json",
|
"content-src/templates/CFR/templates/ExtensionDoorhanger.schema.json",
|
||||||
"content-src/templates/CFR/templates/InfoBar.schema.json",
|
"content-src/templates/CFR/templates/InfoBar.schema.json",
|
||||||
"content-src/templates/OnboardingMessage/BookmarksBarButton.schema.json",
|
"content-src/templates/OnboardingMessage/BookmarksBarButton.schema.json",
|
||||||
|
"content-src/templates/OnboardingMessage/MenuMessage.schema.json",
|
||||||
"content-src/templates/OnboardingMessage/Spotlight.schema.json",
|
"content-src/templates/OnboardingMessage/Spotlight.schema.json",
|
||||||
"content-src/templates/OnboardingMessage/ToolbarBadgeMessage.schema.json",
|
"content-src/templates/OnboardingMessage/ToolbarBadgeMessage.schema.json",
|
||||||
"content-src/templates/OnboardingMessage/UpdateAction.schema.json",
|
"content-src/templates/OnboardingMessage/UpdateAction.schema.json",
|
||||||
|
|||||||
@@ -65,6 +65,10 @@ async function makeValidators() {
|
|||||||
"resource://testing-common/InfoBar.schema.json",
|
"resource://testing-common/InfoBar.schema.json",
|
||||||
{ common: true }
|
{ common: true }
|
||||||
),
|
),
|
||||||
|
menu_message: await schemaValidatorFor(
|
||||||
|
"resource://testing-common/MenuMessage.schema.json",
|
||||||
|
{ common: true }
|
||||||
|
),
|
||||||
pb_newtab: await schemaValidatorFor(
|
pb_newtab: await schemaValidatorFor(
|
||||||
"resource://testing-common/NewtabPromoMessage.schema.json",
|
"resource://testing-common/NewtabPromoMessage.schema.json",
|
||||||
{ common: true }
|
{ common: true }
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ add_task(async function test_PanelTestProvider() {
|
|||||||
pb_newtab: 2,
|
pb_newtab: 2,
|
||||||
toast_notification: 3,
|
toast_notification: 3,
|
||||||
bookmarks_bar_button: 1,
|
bookmarks_bar_button: 1,
|
||||||
|
menu_message: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const EXPECTED_TOTAL_MESSAGE_COUNT = Object.values(
|
const EXPECTED_TOTAL_MESSAGE_COUNT = Object.values(
|
||||||
|
|||||||
@@ -2409,6 +2409,14 @@ var CustomizableUIInternal = {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip out of shadow roots
|
||||||
|
if (
|
||||||
|
target.nodeType == target.DOCUMENT_FRAGMENT_NODE &&
|
||||||
|
target.containingShadowRoot == target
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Break out of the loop immediately for disabled items, as we need to
|
// Break out of the loop immediately for disabled items, as we need to
|
||||||
// keep the menu open in that case.
|
// keep the menu open in that case.
|
||||||
if (target.getAttribute("disabled") == "true") {
|
if (target.getAttribute("disabled") == "true") {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
ChromeUtils.defineESModuleGetters(this, {
|
ChromeUtils.defineESModuleGetters(this, {
|
||||||
AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.sys.mjs",
|
AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.sys.mjs",
|
||||||
|
ASRouter: "resource:///modules/asrouter/ASRouter.sys.mjs",
|
||||||
|
MenuMessage: "resource:///modules/asrouter/MenuMessage.sys.mjs",
|
||||||
NewTabUtils: "resource://gre/modules/NewTabUtils.sys.mjs",
|
NewTabUtils: "resource://gre/modules/NewTabUtils.sys.mjs",
|
||||||
PanelMultiView: "resource:///modules/PanelMultiView.sys.mjs",
|
PanelMultiView: "resource:///modules/PanelMultiView.sys.mjs",
|
||||||
});
|
});
|
||||||
@@ -137,6 +139,7 @@ const PanelUI = {
|
|||||||
"appMenu-libraryView"
|
"appMenu-libraryView"
|
||||||
).addEventListener("command", this._onLibraryCommand);
|
).addEventListener("command", this._onLibraryCommand);
|
||||||
this.mainView.addEventListener("command", this);
|
this.mainView.addEventListener("command", this);
|
||||||
|
this.mainView.addEventListener("ViewShowing", this._onMainViewShow);
|
||||||
this._eventListenersAdded = true;
|
this._eventListenersAdded = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -216,6 +219,14 @@ const PanelUI = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ASRouter.initialized) {
|
||||||
|
await ASRouter.sendTriggerMessage({
|
||||||
|
browser: gBrowser.selectedBrowser,
|
||||||
|
id: "menuOpened",
|
||||||
|
context: { source: MenuMessage.SOURCES.APP_MENU },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let domEvent = null;
|
let domEvent = null;
|
||||||
if (aEvent && aEvent.type != "command") {
|
if (aEvent && aEvent.type != "command") {
|
||||||
domEvent = aEvent;
|
domEvent = aEvent;
|
||||||
@@ -284,6 +295,7 @@ const PanelUI = {
|
|||||||
this._updatePanelButton(aEvent.target);
|
this._updatePanelButton(aEvent.target);
|
||||||
if (aEvent.type == "popuphidden") {
|
if (aEvent.type == "popuphidden") {
|
||||||
CustomizableUI.removePanelCloseListeners(this.panel);
|
CustomizableUI.removePanelCloseListeners(this.panel);
|
||||||
|
MenuMessage.hideAppMenuMessage(gBrowser.selectedBrowser);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "mousedown":
|
case "mousedown":
|
||||||
@@ -619,6 +631,22 @@ const PanelUI = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onMainViewShow(event) {
|
||||||
|
let panelview = event.target;
|
||||||
|
let messageId = panelview.getAttribute(
|
||||||
|
MenuMessage.SHOWING_FXA_MENU_MESSAGE_ATTR
|
||||||
|
);
|
||||||
|
if (messageId) {
|
||||||
|
MenuMessage.recordMenuMessageTelemetry(
|
||||||
|
"IMPRESSION",
|
||||||
|
MenuMessage.SOURCES.APP_MENU,
|
||||||
|
messageId
|
||||||
|
);
|
||||||
|
let message = ASRouter.getMessageById(messageId);
|
||||||
|
ASRouter.addImpression(message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_onHelpViewShow() {
|
_onHelpViewShow() {
|
||||||
// Call global menu setup function
|
// Call global menu setup function
|
||||||
buildHelpMenu();
|
buildHelpMenu();
|
||||||
|
|||||||
@@ -504,6 +504,9 @@ export class TelemetryFeed {
|
|||||||
case "onboarding_user_event":
|
case "onboarding_user_event":
|
||||||
event = await this.applyOnboardingPolicy(event, session);
|
event = await this.applyOnboardingPolicy(event, session);
|
||||||
break;
|
break;
|
||||||
|
case "menu_message_user_event":
|
||||||
|
event = await this.applyMenuMessagePolicy(event);
|
||||||
|
break;
|
||||||
case "asrouter_undesired_event":
|
case "asrouter_undesired_event":
|
||||||
event = this.applyUndesiredEventPolicy(event);
|
event = this.applyUndesiredEventPolicy(event);
|
||||||
break;
|
break;
|
||||||
@@ -570,6 +573,13 @@ export class TelemetryFeed {
|
|||||||
return { ping, pingType: "toast_notification" };
|
return { ping, pingType: "toast_notification" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async applyMenuMessagePolicy(ping) {
|
||||||
|
ping.client_id = await this.telemetryClientId;
|
||||||
|
ping.browser_session_id = lazy.browserSessionId;
|
||||||
|
delete ping.action;
|
||||||
|
return { ping, pingType: "menu" };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per Bug 1484035, Moments metrics comply with following policies:
|
* Per Bug 1484035, Moments metrics comply with following policies:
|
||||||
* 1). In release, it collects impression_id, and treats bucket_id as message_id
|
* 1). In release, it collects impression_id, and treats bucket_id as message_id
|
||||||
@@ -1104,6 +1114,8 @@ export class TelemetryFeed {
|
|||||||
// Intentional fall-through
|
// Intentional fall-through
|
||||||
case msg.TOAST_NOTIFICATION_TELEMETRY:
|
case msg.TOAST_NOTIFICATION_TELEMETRY:
|
||||||
// Intentional fall-through
|
// Intentional fall-through
|
||||||
|
case msg.MENU_MESSAGE_TELEMETRY:
|
||||||
|
// Intentional fall-through
|
||||||
case msg.AS_ROUTER_TELEMETRY_USER_EVENT:
|
case msg.AS_ROUTER_TELEMETRY_USER_EVENT:
|
||||||
this.handleASRouterUserEvent(action);
|
this.handleASRouterUserEvent(action);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ module.exports = {
|
|||||||
`${projectRoot}/toolkit/content/widgets/**/*.stories.@(js|jsx|mjs|ts|tsx|md)`,
|
`${projectRoot}/toolkit/content/widgets/**/*.stories.@(js|jsx|mjs|ts|tsx|md)`,
|
||||||
// about:logins components stories
|
// about:logins components stories
|
||||||
`${projectRoot}/browser/components/aboutlogins/content/components/**/*.stories.mjs`,
|
`${projectRoot}/browser/components/aboutlogins/content/components/**/*.stories.mjs`,
|
||||||
|
// ASRouter components stories
|
||||||
|
`${projectRoot}/browser/components/asrouter/content/**/*.stories.mjs`,
|
||||||
// Backup components stories
|
// Backup components stories
|
||||||
`${projectRoot}/browser/components/backup/content/**/*.stories.mjs`,
|
`${projectRoot}/browser/components/backup/content/**/*.stories.mjs`,
|
||||||
// Reader View components stories
|
// Reader View components stories
|
||||||
|
|||||||
@@ -265,3 +265,9 @@ root-certificate-succession-infobar-primary-button =
|
|||||||
root-certificate-succession-infobar-secondary-button =
|
root-certificate-succession-infobar-secondary-button =
|
||||||
.label = Later
|
.label = Later
|
||||||
.accesskey = L
|
.accesskey = L
|
||||||
|
|
||||||
|
## FxA Menu Message variants
|
||||||
|
|
||||||
|
fxa-menu-message-close-button =
|
||||||
|
.title = Close
|
||||||
|
.aria-label = Close
|
||||||
|
|||||||
@@ -648,6 +648,22 @@ toolbarbutton[constrain-size="true"][cui-areatype="panel"] > .toolbarbutton-badg
|
|||||||
|
|
||||||
/* Handle different UI states. */
|
/* Handle different UI states. */
|
||||||
|
|
||||||
|
#appMenu-mainView:not([showing-fxa-menu-message]) #appMenu-fxa-menu-message,
|
||||||
|
#PanelUI-fxa:not([showing-fxa-menu-message]) #PanelUI-fxa-menu-message {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#appMenu-mainView[showing-fxa-menu-message] {
|
||||||
|
& #appMenu-fxa-status2,
|
||||||
|
& #appMenu-fxa-separator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#PanelUI-fxa[showing-fxa-menu-message] #fxa-manage-account-button {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
|
||||||
:root:not([fxastatus="signedin"]) #PanelUI-fxa-menu-syncnow-button {
|
:root:not([fxastatus="signedin"]) #PanelUI-fxa-menu-syncnow-button {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user