Bug 1951426 - Request messages from OMC in newtab r=home-newtab-reviewers,omc-reviewers,mconley,aminomancer,maxx
Differential Revision: https://phabricator.services.mozilla.com/D240175
This commit is contained in:
@@ -50,6 +50,10 @@ export class AboutNewTabParent extends JSWindowActorParent {
|
||||
id: "defaultBrowserCheck",
|
||||
context: { source: "newtab" },
|
||||
});
|
||||
lazy.ASRouter.sendTriggerMessage({
|
||||
browser: this.browsingContext.top.embedderElement,
|
||||
id: "newtabMessageCheck",
|
||||
});
|
||||
break;
|
||||
|
||||
case "Init": {
|
||||
|
||||
@@ -139,6 +139,10 @@ async function getMessageValidators(skipValidation) {
|
||||
"./content-src/templates/OnboardingMessage/MenuMessage.schema.json",
|
||||
{ common: true }
|
||||
),
|
||||
newtab_message: await getValidator(
|
||||
"./content-src/templates/OnboardingMessage/NewtabMessage.schema.json",
|
||||
{ common: true }
|
||||
),
|
||||
};
|
||||
|
||||
messageValidators.milestone_message = messageValidators.cfr_doorhanger;
|
||||
|
||||
@@ -924,6 +924,41 @@
|
||||
"targeting"
|
||||
]
|
||||
},
|
||||
"NewtabMessage": {
|
||||
"$schema": "https://json-schema.org/draft/2019-09/schema",
|
||||
"$id": "file:///NewtabMessage.schema.json",
|
||||
"title": "NewtabMessage",
|
||||
"description": "A template for messages that are rendered within newtab",
|
||||
"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."
|
||||
}
|
||||
},
|
||||
"additionalProperties": true,
|
||||
"required": [
|
||||
"messageType"
|
||||
]
|
||||
},
|
||||
"template": {
|
||||
"type": "string",
|
||||
"const": "newtab_message"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true,
|
||||
"required": [
|
||||
"targeting"
|
||||
]
|
||||
},
|
||||
"Spotlight": {
|
||||
"$schema": "https://json-schema.org/draft/2019-09/schema",
|
||||
"$id": "file:///Spotlight.schema.json",
|
||||
@@ -1278,6 +1313,7 @@
|
||||
"infobar",
|
||||
"menu_message",
|
||||
"pb_newtab",
|
||||
"newtab_message",
|
||||
"spotlight",
|
||||
"feature_callout",
|
||||
"toast_notification",
|
||||
@@ -1523,6 +1559,25 @@
|
||||
"$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/NewtabPromoMessage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"template": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"newtab_message"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"template"
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"$ref": "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json#/$defs/NewtabMessage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"type": "object",
|
||||
|
||||
@@ -79,6 +79,9 @@ SCHEMAS = [
|
||||
"NewtabPromoMessage": (
|
||||
SCHEMA_DIR / "PBNewtab" / "NewtabPromoMessage.schema.json"
|
||||
),
|
||||
"NewtabMessage": (
|
||||
SCHEMA_DIR / "OnboardingMessage" / "NewtabMessage.schema.json"
|
||||
),
|
||||
"Spotlight": SCHEMA_DIR / "OnboardingMessage" / "Spotlight.schema.json",
|
||||
"ToastNotification": (
|
||||
SCHEMA_DIR / "ToastNotification" / "ToastNotification.schema.json"
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2019-09/schema",
|
||||
"$id": "file:///NewtabMessage.schema.json",
|
||||
"title": "NewtabMessage",
|
||||
"description": "A template for messages that are rendered within newtab",
|
||||
"allOf": [{ "$ref": "file:///FxMSCommon.schema.json#/$defs/Message" }],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"messageType": {
|
||||
"type": "string",
|
||||
"description": "The subtype of the message."
|
||||
}
|
||||
},
|
||||
"additionalProperties": true,
|
||||
"required": ["messageType"]
|
||||
},
|
||||
"template": {
|
||||
"type": "string",
|
||||
"const": "newtab_message"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true,
|
||||
"required": ["targeting"]
|
||||
}
|
||||
@@ -154,6 +154,7 @@ const MESSAGE_TYPE_LIST = [
|
||||
"SPOTLIGHT_TELEMETRY",
|
||||
"TOAST_NOTIFICATION_TELEMETRY",
|
||||
"MENU_MESSAGE_TELEMETRY",
|
||||
"NEWTAB_MESSAGE_TELEMETRY",
|
||||
"AS_ROUTER_TELEMETRY_USER_EVENT",
|
||||
|
||||
// Admin types
|
||||
|
||||
@@ -1477,6 +1477,16 @@ export class _ASRouter {
|
||||
case "menu_message":
|
||||
lazy.MenuMessage.showMenuMessage(browser, message, trigger, force);
|
||||
break;
|
||||
case "newtab_message": {
|
||||
let targetBrowser = force ? null : browser;
|
||||
let messageWithBrowser = {
|
||||
targetBrowser,
|
||||
message,
|
||||
dispatch: this.dispatchCFRAction,
|
||||
};
|
||||
Services.obs.notifyObservers(messageWithBrowser, "newtab-message");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return { message };
|
||||
|
||||
@@ -31,6 +31,7 @@ export class ASRouterParentProcessMessageHandler {
|
||||
case msg.DOORHANGER_TELEMETRY:
|
||||
case msg.SPOTLIGHT_TELEMETRY:
|
||||
case msg.MENU_MESSAGE_TELEMETRY:
|
||||
case msg.NEWTAB_MESSAGE_TELEMETRY:
|
||||
case msg.TOAST_NOTIFICATION_TELEMETRY: {
|
||||
return this.handleTelemetry({ type, data });
|
||||
}
|
||||
|
||||
@@ -125,6 +125,9 @@ export class ASRouterTelemetry {
|
||||
case "asrouter_undesired_event":
|
||||
event = this.applyUndesiredEventPolicy(event);
|
||||
break;
|
||||
case "newtab_message_user_event":
|
||||
event = await this.applyNewtabMessagePolicy(event);
|
||||
break;
|
||||
default:
|
||||
event = { ping: event };
|
||||
break;
|
||||
@@ -215,6 +218,15 @@ export class ASRouterTelemetry {
|
||||
return { ping, pingType: "moments" };
|
||||
}
|
||||
|
||||
async applyNewtabMessagePolicy(ping) {
|
||||
ping.client_id = await this.telemetryClientId;
|
||||
ping.browser_session_id = lazy.browserSessionId;
|
||||
ping.addon_version = Services.appinfo.appBuildID;
|
||||
ping.locale = Services.locale.appLocaleAsBCP47;
|
||||
delete ping.action;
|
||||
return { ping, pingType: "newtab_message" };
|
||||
}
|
||||
|
||||
applyUndesiredEventPolicy(ping) {
|
||||
ping.impression_id = this._impressionId;
|
||||
delete ping.action;
|
||||
@@ -264,6 +276,8 @@ export class ASRouterTelemetry {
|
||||
// Intentional fall-through
|
||||
case msg.MENU_MESSAGE_TELEMETRY:
|
||||
// Intentional fall-through
|
||||
case msg.NEWTAB_MESSAGE_TELEMETRY:
|
||||
// Intentional fall-through
|
||||
case msg.AS_ROUTER_TELEMETRY_USER_EVENT:
|
||||
this.handleASRouterUserEvent(action);
|
||||
break;
|
||||
|
||||
@@ -17,6 +17,7 @@ export const MESSAGE_TYPE_LIST = [
|
||||
"SPOTLIGHT_TELEMETRY",
|
||||
"TOAST_NOTIFICATION_TELEMETRY",
|
||||
"MENU_MESSAGE_TELEMETRY",
|
||||
"NEWTAB_MESSAGE_TELEMETRY",
|
||||
"AS_ROUTER_TELEMETRY_USER_EVENT",
|
||||
|
||||
// Admin types
|
||||
|
||||
@@ -1350,6 +1350,17 @@ const MESSAGES = () => [
|
||||
},
|
||||
testingTriggerContext: "app_menu",
|
||||
},
|
||||
{
|
||||
id: "TEST_NEWTAB_MESSAGE",
|
||||
template: "newtab_message",
|
||||
content: {
|
||||
messageType: "CustomWallpaperHighlight",
|
||||
},
|
||||
trigger: {
|
||||
id: "newtabMessageCheck",
|
||||
},
|
||||
groups: [],
|
||||
},
|
||||
];
|
||||
|
||||
export const PanelTestProvider = {
|
||||
|
||||
@@ -61,6 +61,7 @@ TESTING_JS_MODULES += [
|
||||
"content-src/templates/CFR/templates/InfoBar.schema.json",
|
||||
"content-src/templates/OnboardingMessage/BookmarksBarButton.schema.json",
|
||||
"content-src/templates/OnboardingMessage/MenuMessage.schema.json",
|
||||
"content-src/templates/OnboardingMessage/NewtabMessage.schema.json",
|
||||
"content-src/templates/OnboardingMessage/Spotlight.schema.json",
|
||||
"content-src/templates/OnboardingMessage/ToolbarBadgeMessage.schema.json",
|
||||
"content-src/templates/OnboardingMessage/UpdateAction.schema.json",
|
||||
|
||||
@@ -69,6 +69,10 @@ async function makeValidators() {
|
||||
"resource://testing-common/MenuMessage.schema.json",
|
||||
{ common: true }
|
||||
),
|
||||
newtab_message: await schemaValidatorFor(
|
||||
"resource://testing-common/NewtabMessage.schema.json",
|
||||
{ common: true }
|
||||
),
|
||||
pb_newtab: await schemaValidatorFor(
|
||||
"resource://testing-common/NewtabPromoMessage.schema.json",
|
||||
{ common: true }
|
||||
|
||||
@@ -28,6 +28,7 @@ add_task(async function test_PanelTestProvider() {
|
||||
toast_notification: 3,
|
||||
bookmarks_bar_button: 1,
|
||||
menu_message: 1,
|
||||
newtab_message: 1,
|
||||
};
|
||||
|
||||
const EXPECTED_TOTAL_MESSAGE_COUNT = Object.values(
|
||||
|
||||
@@ -100,6 +100,11 @@ for (const type of [
|
||||
"INIT",
|
||||
"INLINE_SELECTION_CLICK",
|
||||
"INLINE_SELECTION_IMPRESSION",
|
||||
"MESSAGE_CLICK",
|
||||
"MESSAGE_DISMISS",
|
||||
"MESSAGE_IMPRESSION",
|
||||
"MESSAGE_SET",
|
||||
"MESSAGE_TOGGLE_VISIBILITY",
|
||||
"NEW_TAB_INIT",
|
||||
"NEW_TAB_INITIAL_STATE",
|
||||
"NEW_TAB_LOAD",
|
||||
|
||||
@@ -103,6 +103,15 @@ export const INITIAL_STATE = {
|
||||
recentSavesEnabled: false,
|
||||
showTopicSelection: false,
|
||||
},
|
||||
// Messages received from ASRouter to render in newtab
|
||||
Messages: {
|
||||
// messages received from ASRouter are initially visible
|
||||
isHidden: false,
|
||||
// portID for that tab that was sent the message
|
||||
portID: "",
|
||||
// READONLY Message data received from ASRouter
|
||||
messageData: {},
|
||||
},
|
||||
Notifications: {
|
||||
showNotifications: false,
|
||||
toastCounter: 0,
|
||||
@@ -534,6 +543,24 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
|
||||
}
|
||||
}
|
||||
|
||||
function Messages(prevState = INITIAL_STATE.Messages, action) {
|
||||
switch (action.type) {
|
||||
case at.MESSAGE_SET:
|
||||
if (prevState.messageData.messageType) {
|
||||
return prevState;
|
||||
}
|
||||
return {
|
||||
...prevState,
|
||||
messageData: action.data.message,
|
||||
portID: action.data.portID || "",
|
||||
};
|
||||
case at.MESSAGE_TOGGLE_VISIBILITY:
|
||||
return { ...prevState, isHidden: action.data };
|
||||
default:
|
||||
return prevState;
|
||||
}
|
||||
}
|
||||
|
||||
function Pocket(prevState = INITIAL_STATE.Pocket, action) {
|
||||
switch (action.type) {
|
||||
case at.POCKET_WAITING_FOR_SPOC:
|
||||
@@ -979,6 +1006,7 @@ export const reducers = {
|
||||
Prefs,
|
||||
Dialog,
|
||||
Sections,
|
||||
Messages,
|
||||
Notifications,
|
||||
Pocket,
|
||||
Personalization,
|
||||
|
||||
@@ -780,6 +780,7 @@ export const Base = connect(state => ({
|
||||
Prefs: state.Prefs,
|
||||
Sections: state.Sections,
|
||||
DiscoveryStream: state.DiscoveryStream,
|
||||
Messages: state.Messages,
|
||||
Notifications: state.Notifications,
|
||||
Search: state.Search,
|
||||
Wallpapers: state.Wallpapers,
|
||||
|
||||
@@ -173,6 +173,11 @@ for (const type of [
|
||||
"INIT",
|
||||
"INLINE_SELECTION_CLICK",
|
||||
"INLINE_SELECTION_IMPRESSION",
|
||||
"MESSAGE_CLICK",
|
||||
"MESSAGE_DISMISS",
|
||||
"MESSAGE_IMPRESSION",
|
||||
"MESSAGE_SET",
|
||||
"MESSAGE_TOGGLE_VISIBILITY",
|
||||
"NEW_TAB_INIT",
|
||||
"NEW_TAB_INITIAL_STATE",
|
||||
"NEW_TAB_LOAD",
|
||||
@@ -6805,6 +6810,15 @@ const INITIAL_STATE = {
|
||||
recentSavesEnabled: false,
|
||||
showTopicSelection: false,
|
||||
},
|
||||
// Messages received from ASRouter to render in newtab
|
||||
Messages: {
|
||||
// messages received from ASRouter are initially visible
|
||||
isHidden: false,
|
||||
// portID for that tab that was sent the message
|
||||
portID: "",
|
||||
// READONLY Message data received from ASRouter
|
||||
messageData: {},
|
||||
},
|
||||
Notifications: {
|
||||
showNotifications: false,
|
||||
toastCounter: 0,
|
||||
@@ -7236,6 +7250,24 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
|
||||
}
|
||||
}
|
||||
|
||||
function Messages(prevState = INITIAL_STATE.Messages, action) {
|
||||
switch (action.type) {
|
||||
case actionTypes.MESSAGE_SET:
|
||||
if (prevState.messageData.messageType) {
|
||||
return prevState;
|
||||
}
|
||||
return {
|
||||
...prevState,
|
||||
messageData: action.data.message,
|
||||
portID: action.data.portID || "",
|
||||
};
|
||||
case actionTypes.MESSAGE_TOGGLE_VISIBILITY:
|
||||
return { ...prevState, isHidden: action.data };
|
||||
default:
|
||||
return prevState;
|
||||
}
|
||||
}
|
||||
|
||||
function Pocket(prevState = INITIAL_STATE.Pocket, action) {
|
||||
switch (action.type) {
|
||||
case actionTypes.POCKET_WAITING_FOR_SPOC:
|
||||
@@ -7681,6 +7713,7 @@ const reducers = {
|
||||
Prefs,
|
||||
Dialog,
|
||||
Sections,
|
||||
Messages,
|
||||
Notifications,
|
||||
Pocket,
|
||||
Personalization: Reducers_sys_Personalization,
|
||||
@@ -13920,6 +13953,7 @@ const Base = (0,external_ReactRedux_namespaceObject.connect)(state => ({
|
||||
Prefs: state.Prefs,
|
||||
Sections: state.Sections,
|
||||
DiscoveryStream: state.DiscoveryStream,
|
||||
Messages: state.Messages,
|
||||
Notifications: state.Notifications,
|
||||
Search: state.Search,
|
||||
Wallpapers: state.Wallpapers,
|
||||
|
||||
@@ -24,6 +24,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
FaviconFeed: "resource://newtab/lib/FaviconFeed.sys.mjs",
|
||||
HighlightsFeed: "resource://newtab/lib/HighlightsFeed.sys.mjs",
|
||||
NewTabInit: "resource://newtab/lib/NewTabInit.sys.mjs",
|
||||
NewTabMessaging: "resource://newtab/lib/NewTabMessaging.sys.mjs",
|
||||
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
PrefsFeed: "resource://newtab/lib/PrefsFeed.sys.mjs",
|
||||
PlacesFeed: "resource://newtab/lib/PlacesFeed.sys.mjs",
|
||||
@@ -1218,6 +1219,12 @@ const FEEDS_DATA = [
|
||||
title: "Handles fetching and caching ads data",
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
name: "newtabmessaging",
|
||||
factory: () => new lazy.NewTabMessaging(),
|
||||
title: "Handles fetching and triggering ASRouter messages in newtab",
|
||||
value: true,
|
||||
},
|
||||
];
|
||||
|
||||
const FEEDS_CONFIG = new Map();
|
||||
|
||||
122
browser/extensions/newtab/lib/NewTabMessaging.sys.mjs
Normal file
122
browser/extensions/newtab/lib/NewTabMessaging.sys.mjs
Normal file
@@ -0,0 +1,122 @@
|
||||
/* 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/. */
|
||||
|
||||
import {
|
||||
actionTypes as at,
|
||||
actionCreators as ac,
|
||||
} from "resource://newtab/common/Actions.mjs";
|
||||
|
||||
export class NewTabMessaging {
|
||||
constructor() {
|
||||
this.initialized = false;
|
||||
this.ASRouterDispatch = null;
|
||||
}
|
||||
|
||||
init() {
|
||||
if (!this.initialized) {
|
||||
Services.obs.addObserver(this, "newtab-message");
|
||||
this.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
uninit() {
|
||||
Services.obs.removeObserver(this, "newtab-message");
|
||||
}
|
||||
|
||||
observe(subject, topic, _data) {
|
||||
if (topic === "newtab-message") {
|
||||
let { targetBrowser, message, dispatch } = subject.wrappedJSObject;
|
||||
this.ASRouterDispatch = dispatch;
|
||||
this.showMessage(targetBrowser, message);
|
||||
}
|
||||
}
|
||||
|
||||
async showMessage(targetBrowser, message) {
|
||||
if (targetBrowser) {
|
||||
let actor =
|
||||
targetBrowser.browsingContext.currentWindowGlobal.getActor(
|
||||
"AboutNewTab"
|
||||
);
|
||||
if (actor) {
|
||||
let tabDetails = actor.getTabDetails();
|
||||
if (tabDetails) {
|
||||
// Only send the message for the tab that triggered the message
|
||||
this.store.dispatch(
|
||||
ac.OnlyToOneContent(
|
||||
{
|
||||
type: at.MESSAGE_SET,
|
||||
// send along portID as well so that the child process can easily just update the single tab
|
||||
data: {
|
||||
message,
|
||||
portID: tabDetails.portID,
|
||||
},
|
||||
},
|
||||
tabDetails.portID
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if targetBrowser is null, send to the preloaded tab / main process
|
||||
// This should only run if message is triggered from asrouter during dev
|
||||
this.store.dispatch(
|
||||
ac.AlsoToPreloaded({
|
||||
type: at.MESSAGE_SET,
|
||||
data: {
|
||||
message,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send impression to ASRouter
|
||||
* @param {Object} message
|
||||
*/
|
||||
handleImpression(message) {
|
||||
this.sendTelemetry("IMPRESSION", message);
|
||||
this.ASRouterDispatch?.({
|
||||
type: "IMPRESSION",
|
||||
data: message,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends telemetry data through ASRouter to
|
||||
* match pattern with ASRouterTelemetry
|
||||
*/
|
||||
sendTelemetry(event, message, source = "newtab") {
|
||||
const data = {
|
||||
action: "newtab_message_user_event",
|
||||
event,
|
||||
event_context: { source, page: "about:newtab" },
|
||||
message_id: message.id,
|
||||
};
|
||||
this.ASRouterDispatch?.({
|
||||
type: "NEWTAB_MESSAGE_TELEMETRY",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
async onAction(action) {
|
||||
switch (action.type) {
|
||||
case at.INIT:
|
||||
this.init();
|
||||
break;
|
||||
case at.UNINIT:
|
||||
this.uninit();
|
||||
break;
|
||||
case at.MESSAGE_IMPRESSION:
|
||||
this.handleImpression(action.data);
|
||||
break;
|
||||
case at.MESSAGE_DISMISS:
|
||||
this.sendTelemetry("DISMISS", action.data.message);
|
||||
break;
|
||||
case at.MESSAGE_CLICK:
|
||||
this.sendTelemetry("CLICK", action.data.source, action.data.message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,6 +169,7 @@ Triggers and actions
|
||||
.. _whats_new_schema: https://searchfox.org/mozilla-central/source/browser/components/asrouter/content-src/templates/OnboardingMessage/WhatsNewMessage.schema.json
|
||||
.. _protections_panel_schema: https://searchfox.org/mozilla-central/source/browser/components/asrouter/content-src/templates/OnboardingMessage/ProtectionsPanelMessage.schema.json
|
||||
.. _pbnewtab_promo_schema: https://searchfox.org/mozilla-central/source/browser/components/asrouter/content-src/templates/PBNewtab/NewtabPromoMessage.schema.json
|
||||
.. _newtab_message_schema: https://searchfox.org/mozilla-central/source/browser/components/asrouter/content-src/templates/OnboardingMessage/NewtabMessage.schema.json
|
||||
.. _messaging_experiments_schema: https://searchfox.org/mozilla-central/source/browser/components/asrouter/content-src/schemas/MessagingExperiment.schema.json
|
||||
.. _common_schema: https://searchfox.org/mozilla-central/source/browser/components/asrouter/content-src/schemas/FxMSCommon.schema.json
|
||||
|
||||
|
||||
Reference in New Issue
Block a user