Bug 1754832 - Update PBM to support opening spotlight tab modal r=Mardak,mviar,fluent-reviewers,flod

Differential Revision: https://phabricator.services.mozilla.com/D140565
This commit is contained in:
Punam Dahiya
2022-03-17 06:41:47 +00:00
parent 0d7fa051a3
commit 241b77481c
17 changed files with 443 additions and 246 deletions

View File

@@ -31,6 +31,8 @@ XPCOMUtils.defineLazyPreferenceGetter(
XPCOMUtils.defineLazyModuleGetters(this, {
UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
SpecialMessageActions:
"resource://messaging-system/lib/SpecialMessageActions.jsm",
});
// We only show the private search banner once per browser session.
@@ -165,6 +167,9 @@ class AboutPrivateBrowsingParent extends JSWindowActorParent {
case "ShouldShowVPNPromo": {
return BrowserUtils.shouldShowVPNPromo();
}
case "SpecialMessageActionDispatch": {
SpecialMessageActions.handleAction(aMessage.data, browser);
}
}
return undefined;

View File

@@ -126,7 +126,9 @@ function renderMultistage(ready) {
window.AWGetImportableSites = () => "[]";
window.AWGetRegion = receive("GET_REGION");
window.AWGetSelectedTheme = receive("GET_SELECTED_THEME");
window.AWSendEventTelemetry = receive("TELEMETRY_EVENT");
// Do not send telemetry if message (e.g. spotlight in PBM) config sets metrics as 'block'.
window.AWSendEventTelemetry =
CONFIG?.metrics === "block" ? () => {} : receive("TELEMETRY_EVENT");
window.AWSendToParent = (name, data) => receive(name)(data);
window.AWFinish = () => {
window.close();

View File

@@ -125,10 +125,6 @@ var whitelist = [
// browser/components/preferences/moreFromMozilla.js
// These files URLs are constructed programatically at run time.
{
file:
"chrome://browser/content/preferences/more-from-mozilla-qr-code-advanced.svg",
},
{
file:
"chrome://browser/content/preferences/more-from-mozilla-qr-code-simple.svg",

View File

@@ -37,6 +37,84 @@ const ONBOARDING_MESSAGES = () => [
},
trigger: { id: "protectionsPanelOpen" },
},
{
id: "PB_NEWTAB_FOCUS_PROMO",
template: "pb_newtab",
groups: ["pbNewtab"],
content: {
infoBody: "fluent:about-private-browsing-info-description-simplified",
infoEnabled: true,
infoIcon: "chrome://global/skin/icons/indicator-private-browsing.svg",
infoLinkText: "fluent:about-private-browsing-learn-more-link",
infoTitle: "",
infoTitleEnabled: false,
promoEnabled: true,
promoHeader: "fluent:about-private-browsing-focus-promo-header",
promoImageLarge: "chrome://browser/content/assets/focus-promo.png",
promoLinkText: "fluent:about-private-browsing-focus-promo-cta",
promoLinkType: "button",
promoSectionStyle: "below-search",
promoTitle: "fluent:about-private-browsing-focus-promo-text",
promoTitleEnabled: true,
promoButton: {
action: {
type: "SHOW_SPOTLIGHT",
data: {
content: {
id: "FOCUS_PROMO",
template: "multistage",
modal: "tab",
metrics: "block",
backdrop: "transparent",
screens: [
{
id: "DEFAULT_MODAL_UI",
order: 0,
content: {
logo: {
imageURL:
"chrome://browser/content/preferences/more-from-mozilla-qr-code-advanced.svg",
height: "100px",
},
title: "Get Firefox Focus",
subtitle: "Scan the QR Code to Download",
primary_button: {
label: "Email yourself a link",
action: {
type: "OPEN_URL",
data: {
args:
"https://www.mozilla.org/firefox/mobile/get-app/",
where: "tabshifted",
},
},
},
secondary_button: {
label: "Close",
action: {
navigate: true,
},
},
},
},
],
},
},
},
},
},
priority: 2,
frequency: {
custom: [
{
cap: 1,
period: 604800000, // Max 1 per week
},
],
lifetime: 3,
},
targeting: "true",
},
{
id: "PB_NEWTAB_INFO_SECTION",
template: "pb_newtab",

View File

@@ -25,12 +25,19 @@ const Spotlight = {
});
},
async showSpotlightDialog(browser, message, dispatchCFRAction) {
/**
* Shows spotlight tab or window modal specific to the given browser
* @param browser The browser for spotlight display
* @param message Message containing content to show
* @param dispatchCFRAction A function to dispatch resulting actions
* @return boolean value capturing if spotlight was displayed
*/
async showSpotlightDialog(browser, message, dispatchCFRAction = () => {}) {
const win = browser.ownerGlobal;
if (win.gDialogBox.isOpen) {
return false;
}
const spotlight_url = "chrome://browser/content/spotlight.html";
let params = { primaryBtn: false, secondaryBtn: false };
// There are two events named `IMPRESSION` the first one refers to telemetry
@@ -40,10 +47,18 @@ const Spotlight = {
const unload = await RemoteImages.patchMessage(message.content.logo);
await win.gDialogBox.open("chrome://browser/content/spotlight.html", [
message.content,
params,
]);
if (message.content?.modal === "tab") {
await win.gBrowser.getTabDialogBox(browser).open(
spotlight_url,
{
features: "resizable=no",
allowDuplicateDialogs: false,
},
[message.content, params]
);
} else {
await win.gDialogBox.open(spotlight_url, [message.content, params]);
}
if (unload) {
unload();

View File

@@ -87,6 +87,7 @@ async function renderPromo({
promoHeader,
promoImageLarge,
promoImageSmall,
promoButton = null,
} = {}) {
const container = document.querySelector(".promo");
if (promoEnabled === false) {
@@ -102,20 +103,22 @@ async function renderPromo({
const promoImageSmallEl = document.querySelector(".promo-image-small img");
const dismissBtn = document.querySelector("#dismiss-btn");
// Setup the private browsing VPN link.
const vpnPromoUrl =
promoLinkUrl || RPMGetFormatURLPref("browser.privatebrowsing.vpnpromourl");
if (promoLinkType === "button") {
linkEl.classList.add("button");
}
if (vpnPromoUrl) {
linkEl.setAttribute("href", vpnPromoUrl);
if (promoLinkUrl) {
linkEl.setAttribute("href", promoLinkUrl);
linkEl.setAttribute("target", "_blank");
linkEl.addEventListener("click", () => {
window.PrivateBrowsingRecordClick("promo_link");
});
} else if (promoButton?.action?.type === "SHOW_SPOTLIGHT") {
linkEl.setAttribute("href", "#");
linkEl.addEventListener("click", async () => {
window.PrivateBrowsingRecordClick("promo_link");
await RPMSendQuery("SpecialMessageActionDispatch", promoButton.action);
});
} else {
// If the link is undefined, remove the promo completely
container.remove();

View File

@@ -21,6 +21,7 @@ support-files =
[browser_privatebrowsing_DownloadLastDirWithCPS.js]
[browser_privatebrowsing_about_nimbus.js]
[browser_privatebrowsing_about_messaging.js]
skip-if =
verify # This was already broken on mozilla-central, skipping for now
# to prevent it from causing issues with people who want to use

View File

@@ -0,0 +1,244 @@
/* 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 { ExperimentFakes } = ChromeUtils.import(
"resource://testing-common/NimbusTestUtils.jsm"
);
const { TelemetryTestUtils } = ChromeUtils.import(
"resource://testing-common/TelemetryTestUtils.jsm"
);
const { ASRouter } = ChromeUtils.import(
"resource://activity-stream/lib/ASRouter.jsm"
);
async function openTabAndWaitForRender() {
let { win, tab } = await openAboutPrivateBrowsing();
await SpecialPowers.spawn(tab, [], async function() {
// Wait for render to complete
await ContentTaskUtils.waitForCondition(() =>
content.document.documentElement.hasAttribute(
"PrivateBrowsingRenderComplete"
)
);
});
return { win, tab };
}
async function setupMSExperimentWithMessage(message) {
let doExperimentCleanup = await ExperimentFakes.enrollWithFeatureConfig({
featureId: "pbNewtab",
enabled: true,
value: message,
});
Services.prefs.setStringPref(
"browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments",
'{"id":"messaging-experiments","enabled":true,"type":"remote-experiments","messageGroups":["pbNewtab"],"updateCycleInMs":0}'
);
// Reload the provider
await ASRouter._updateMessageProviders();
// Wait to load the messages from the messaging-experiments provider
await ASRouter.loadMessagesFromAllProviders();
registerCleanupFunction(async () => {
// Reload the provider again at cleanup to remove the experiment message
await ASRouter._updateMessageProviders();
// Wait to load the messages from the messaging-experiments provider
await ASRouter.loadMessagesFromAllProviders();
Services.prefs.clearUserPref(
"browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments"
);
});
Assert.ok(
ASRouter.state.messages.find(m => m.id.includes(message.id)),
"Experiment message found in ASRouter state"
);
return doExperimentCleanup;
}
add_task(async function setup() {
let { win } = await openAboutPrivateBrowsing();
await BrowserTestUtils.closeWindow(win);
});
add_task(async function test_experiment_messaging_system() {
const LOCALE = Services.locale.appLocaleAsBCP47;
let doExperimentCleanup = await setupMSExperimentWithMessage({
id: "PB_NEWTAB_MESSAGING_SYSTEM",
template: "pb_newtab",
content: {
promoEnabled: true,
infoEnabled: true,
infoBody: "fluent:about-private-browsing-info-title",
promoLinkText: "fluent:about-private-browsing-prominent-cta",
infoLinkUrl: "http://foo.example.com/%LOCALE%",
promoLinkUrl: "http://bar.example.com/%LOCALE%",
},
// Priority ensures this message is picked over the one in
// OnboardingMessageProvider
priority: 5,
targeting: "true",
});
Services.telemetry.clearEvents();
let { win, tab } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab, [LOCALE], async function(locale) {
const infoBody = content.document.getElementById("info-body");
const promoLink = content.document.getElementById(
"private-browsing-vpn-link"
);
// Check experiment values are rendered
is(
infoBody.textContent,
"Youre in a Private Window",
"should render infoBody with fluent"
);
is(
promoLink.textContent,
"Stay private with Mozilla VPN",
"should render promoLinkText with fluent"
);
is(
content.document.querySelector(".info a").getAttribute("href"),
"http://foo.example.com/" + locale,
"should format the infoLinkUrl url"
);
is(
content.document.querySelector(".info a").getAttribute("target"),
"_blank",
"should open info url in new tab"
);
is(
content.document.querySelector(".promo a").getAttribute("target"),
"_blank",
"should open promo url in new tab"
);
});
// There's something buggy here, disabling for now to prevent intermittent failures
// until we fix it in bug 1754536.
//
// TelemetryTestUtils.assertEvents(
// [
// {
//
// method: "expose",
// extra: {
// featureId: "pbNewtab",
// },
// },
// ],
// { category: "normandy" }
// );
await BrowserTestUtils.closeWindow(win);
await doExperimentCleanup();
});
add_task(async function test_experiment_messaging_system_impressions() {
const LOCALE = Services.locale.appLocaleAsBCP47;
let doExperimentCleanup = await setupMSExperimentWithMessage({
id: `PB_NEWTAB_MESSAGING_SYSTEM_${Math.random()}`,
template: "pb_newtab",
content: {
promoEnabled: true,
infoEnabled: true,
infoBody: "fluent:about-private-browsing-info-title",
promoLinkText: "fluent:about-private-browsing-prominent-cta",
infoLinkUrl: "http://foo.example.com/%LOCALE%",
promoLinkUrl: "http://bar.example.com/%LOCALE%",
},
frequency: {
lifetime: 2,
},
// Priority ensures this message is picked over the one in
// OnboardingMessageProvider
priority: 5,
targeting: "true",
});
let { win: win1, tab: tab1 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab1, [LOCALE], async function(locale) {
is(
content.document.querySelector(".promo a").getAttribute("href"),
"http://bar.example.com/" + locale,
"should format the promoLinkUrl url"
);
});
let { win: win2, tab: tab2 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab2, [LOCALE], async function(locale) {
is(
content.document.querySelector(".promo a").getAttribute("href"),
"http://bar.example.com/" + locale,
"should format the promoLinkUrl url"
);
});
let { win: win3, tab: tab3 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab3, [], async function() {
is(
content.document.querySelector(".promo a"),
null,
"should no longer render the experiment message after 2 impressions"
);
});
await BrowserTestUtils.closeWindow(win1);
await BrowserTestUtils.closeWindow(win2);
await BrowserTestUtils.closeWindow(win3);
await doExperimentCleanup();
});
add_task(async function test_experiment_messaging_system_dismiss() {
const LOCALE = Services.locale.appLocaleAsBCP47;
let doExperimentCleanup = await setupMSExperimentWithMessage({
id: `PB_NEWTAB_MESSAGING_SYSTEM_${Math.random()}`,
template: "pb_newtab",
content: {
promoEnabled: true,
infoEnabled: true,
infoBody: "fluent:about-private-browsing-info-title",
promoLinkText: "fluent:about-private-browsing-prominent-cta",
infoLinkUrl: "http://foo.example.com/%LOCALE%",
promoLinkUrl: "http://bar.example.com/%LOCALE%",
},
// Priority ensures this message is picked over the one in
// OnboardingMessageProvider
priority: 5,
targeting: "true",
});
let { win: win1, tab: tab1 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab1, [LOCALE], async function(locale) {
is(
content.document.querySelector(".promo a").getAttribute("href"),
"http://bar.example.com/" + locale,
"should format the promoLinkUrl url"
);
content.document.querySelector("#dismiss-btn").click();
});
let { win: win2, tab: tab2 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab2, [], async function() {
is(
content.document.querySelector(".promo a"),
null,
"should no longer render the experiment message after dismissing"
);
});
await BrowserTestUtils.closeWindow(win1);
await BrowserTestUtils.closeWindow(win2);
await doExperimentCleanup();
});

View File

@@ -8,9 +8,6 @@ const { ExperimentFakes } = ChromeUtils.import(
const { ExperimentAPI } = ChromeUtils.import(
"resource://nimbus/ExperimentAPI.jsm"
);
const { TelemetryTestUtils } = ChromeUtils.import(
"resource://testing-common/TelemetryTestUtils.jsm"
);
const { PanelTestProvider } = ChromeUtils.import(
"resource://activity-stream/lib/PanelTestProvider.jsm"
);
@@ -18,16 +15,6 @@ const { ASRouter } = ChromeUtils.import(
"resource://activity-stream/lib/ASRouter.jsm"
);
/**
* These tests ensure that the experiment and remote default capabilities
* for the "privatebrowsing" feature are working as expected.
*/
add_task(async function setup() {
// XXX I believe this is likely to become unnecessary as part of the fix for bug 1749775.
requestLongerTimeout(5);
});
async function openTabAndWaitForRender() {
let { win, tab } = await openAboutPrivateBrowsing();
await SpecialPowers.spawn(tab, [], async function() {
@@ -59,39 +46,6 @@ function waitForTelemetryEvent(category) {
}, "waiting for telemetry event");
}
async function setupMSExperimentWithMessage(message) {
let doExperimentCleanup = await ExperimentFakes.enrollWithFeatureConfig({
featureId: "pbNewtab",
enabled: true,
value: message,
});
Services.prefs.setStringPref(
"browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments",
'{"id":"messaging-experiments","enabled":true,"type":"remote-experiments","messageGroups":["pbNewtab"],"updateCycleInMs":0}'
);
// Reload the provider
await ASRouter._updateMessageProviders();
// Wait to load the messages from the messaging-experiments provider
await ASRouter.loadMessagesFromAllProviders();
registerCleanupFunction(async () => {
// Reload the provider again at cleanup to remove the experiment message
await ASRouter._updateMessageProviders();
// Wait to load the messages from the messaging-experiments provider
await ASRouter.loadMessagesFromAllProviders();
Services.prefs.clearUserPref(
"browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments"
);
});
Assert.ok(
ASRouter.state.messages.find(m => m.id.includes(message.id)),
"Experiment message found in ASRouter state"
);
return doExperimentCleanup;
}
add_task(async function test_experiment_plain_text() {
const defaultMessageContent = (await PanelTestProvider.getMessages()).find(
m => m.template === "pb_newtab"
@@ -108,6 +62,7 @@ add_task(async function test_experiment_plain_text() {
infoIcon: "chrome://branding/content/about-logo.png",
promoTitle: "Promo title",
promoLinkText: "Promo link",
promoLinkUrl: "https://test.com",
},
});
@@ -155,6 +110,7 @@ add_task(async function test_experiment_fluent() {
...defaultMessageContent,
infoBody: "fluent:about-private-browsing-info-title",
promoLinkText: "fluent:about-private-browsing-prominent-cta",
promoLinkUrl: "https://test.com",
},
});
@@ -331,6 +287,7 @@ add_task(async function test_experiment_bottom_promo() {
...defaultMessageContent,
enabled: true,
promoLinkType: "button",
promoLinkUrl: "http://example.com",
promoSectionStyle: "bottom",
promoHeader: "Need more privacy?",
infoTitleEnabled: true,
@@ -388,10 +345,11 @@ add_task(async function test_experiment_below_search_promo() {
...defaultMessageContent,
enabled: true,
promoLinkType: "button",
promoLinkUrl: "http://example.com",
promoSectionStyle: "below-search",
promoHeader: "Need more privacy?",
promoTitle:
"Mozilla VPN. Security, reliability and speed — on every device, anywhere you go.",
"Mozilla VPN. Security, reliability and speed — on every device, anywhere you go.",
promoImageLarge: "chrome://browser/content/assets/moz-vpn.svg",
promoImageSmall: "chrome://browser/content/assets/vpn-logo.svg",
infoTitleEnabled: false,
@@ -447,10 +405,11 @@ add_task(async function test_experiment_top_promo() {
...defaultMessageContent,
enabled: true,
promoLinkType: "button",
promoLinkUrl: "http://example.com",
promoSectionStyle: "top",
promoHeader: "Need more privacy?",
promoTitle:
"Mozilla VPN. Security, reliability and speed — on every device,anywhere you go.",
"Mozilla VPN. Security, reliability and speed — on every device, anywhere you go.",
promoImageLarge: "chrome://browser/content/assets/moz-vpn.svg",
promoImageSmall: "chrome://browser/content/assets/vpn-logo.svg",
infoTitleEnabled: false,
@@ -492,183 +451,3 @@ add_task(async function test_experiment_top_promo() {
await doExperimentCleanup();
});
add_task(async function test_experiment_messaging_system() {
const LOCALE = Services.locale.appLocaleAsBCP47;
let doExperimentCleanup = await setupMSExperimentWithMessage({
id: "PB_NEWTAB_MESSAGING_SYSTEM",
template: "pb_newtab",
content: {
promoEnabled: true,
infoEnabled: true,
infoBody: "fluent:about-private-browsing-info-title",
promoLinkText: "fluent:about-private-browsing-prominent-cta",
infoLinkUrl: "http://foo.example.com/%LOCALE%",
promoLinkUrl: "http://bar.example.com/%LOCALE%",
},
// Priority ensures this message is picked over the one in
// OnboardingMessageProvider
priority: 5,
targeting: "true",
});
Services.telemetry.clearEvents();
let { win, tab } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab, [LOCALE], async function(locale) {
const infoBody = content.document.getElementById("info-body");
const promoLink = content.document.getElementById(
"private-browsing-vpn-link"
);
// Check experiment values are rendered
is(
infoBody.textContent,
"Youre in a Private Window",
"should render infoBody with fluent"
);
is(
promoLink.textContent,
"Stay private with Mozilla VPN",
"should render promoLinkText with fluent"
);
is(
content.document.querySelector(".info a").getAttribute("href"),
"http://foo.example.com/" + locale,
"should format the infoLinkUrl url"
);
is(
content.document.querySelector(".info a").getAttribute("target"),
"_blank",
"should open info url in new tab"
);
is(
content.document.querySelector(".promo a").getAttribute("target"),
"_blank",
"should open promo url in new tab"
);
});
// There's something buggy here, disabling for now to prevent intermittent failures
// until we fix it in bug 1749775.
//
// TelemetryTestUtils.assertEvents(
// [
// {
//
// method: "expose",
// extra: {
// featureId: "pbNewtab",
// },
// },
// ],
// { category: "normandy" }
// );
await BrowserTestUtils.closeWindow(win);
await doExperimentCleanup();
});
add_task(async function test_experiment_messaging_system_impressions() {
const LOCALE = Services.locale.appLocaleAsBCP47;
let doExperimentCleanup = await setupMSExperimentWithMessage({
id: `PB_NEWTAB_MESSAGING_SYSTEM_${Math.random()}`,
template: "pb_newtab",
content: {
promoEnabled: true,
infoEnabled: true,
infoBody: "fluent:about-private-browsing-info-title",
promoLinkText: "fluent:about-private-browsing-prominent-cta",
infoLinkUrl: "http://foo.example.com/%LOCALE%",
promoLinkUrl: "http://bar.example.com/%LOCALE%",
},
frequency: {
lifetime: 2,
},
// Priority ensures this message is picked over the one in
// OnboardingMessageProvider
priority: 5,
targeting: "true",
});
let { win: win1, tab: tab1 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab1, [LOCALE], async function(locale) {
is(
content.document.querySelector(".promo a").getAttribute("href"),
"http://bar.example.com/" + locale,
"should format the promoLinkUrl url"
);
});
let { win: win2, tab: tab2 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab2, [LOCALE], async function(locale) {
is(
content.document.querySelector(".promo a").getAttribute("href"),
"http://bar.example.com/" + locale,
"should format the promoLinkUrl url"
);
});
let { win: win3, tab: tab3 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab3, [], async function() {
is(
content.document.querySelector(".promo a"),
null,
"should no longer render the experiment message after 2 impressions"
);
});
await BrowserTestUtils.closeWindow(win1);
await BrowserTestUtils.closeWindow(win2);
await BrowserTestUtils.closeWindow(win3);
await doExperimentCleanup();
});
add_task(async function test_experiment_messaging_system_dismiss() {
const LOCALE = Services.locale.appLocaleAsBCP47;
let doExperimentCleanup = await setupMSExperimentWithMessage({
id: `PB_NEWTAB_MESSAGING_SYSTEM_${Math.random()}`,
template: "pb_newtab",
content: {
promoEnabled: true,
infoEnabled: true,
infoBody: "fluent:about-private-browsing-info-title",
promoLinkText: "fluent:about-private-browsing-prominent-cta",
infoLinkUrl: "http://foo.example.com/%LOCALE%",
promoLinkUrl: "http://bar.example.com/%LOCALE%",
},
// Priority ensures this message is picked over the one in
// OnboardingMessageProvider
priority: 5,
targeting: "true",
});
let { win: win1, tab: tab1 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab1, [LOCALE], async function(locale) {
is(
content.document.querySelector(".promo a").getAttribute("href"),
"http://bar.example.com/" + locale,
"should format the promoLinkUrl url"
);
content.document.querySelector("#dismiss-btn").click();
});
let { win: win2, tab: tab2 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab2, [], async function() {
is(
content.document.querySelector(".promo a"),
null,
"should no longer render the experiment message after dismissing"
);
});
await BrowserTestUtils.closeWindow(win1);
await BrowserTestUtils.closeWindow(win2);
await doExperimentCleanup();
});

View File

@@ -28,6 +28,10 @@ about-private-browsing-get-privacy = Get privacy protections everywhere you brow
about-private-browsing-hide-activity-1 = Hide browsing activity and location with { -mozilla-vpn-brand-name }. One click creates a secure connection, even on public Wi-Fi.
about-private-browsing-prominent-cta = Stay private with { -mozilla-vpn-brand-name }
about-private-browsing-focus-promo-cta = Download { -focus-brand-name }
about-private-browsing-focus-promo-header = { -focus-brand-name }: Private browsing on-the-go
about-private-browsing-focus-promo-text = Our dedicated private browsing mobile app clears your history and cookies every time.
# This string is the title for the banner for search engine selection
# in a private window.
# Variables:

View File

@@ -27,6 +27,7 @@
-translations-brand-name = Firefox Translations
-rally-brand-name = Mozilla Rally
-rally-short-name = Rally
-focus-brand-name = Firefox Focus
# “Suggest” can be localized, “Firefox” must be treated as a brand
# and kept in English.

View File

@@ -17,6 +17,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
UITour: "resource:///modules/UITour.jsm",
FxAccounts: "resource://gre/modules/FxAccounts.jsm",
MigrationUtils: "resource:///modules/MigrationUtils.jsm",
Spotlight: "resource://activity-stream/lib/Spotlight.jsm",
});
const SpecialMessageActions = {
@@ -174,7 +175,7 @@ const SpecialMessageActions = {
* Messaging System interactions.
*
* @param {{type: string, data?: any}} action User action defined in message JSON.
* @param browser {Browser} The browser most relvant to the message.
* @param browser {Browser} The browser most relevant to the message.
*/
async handleAction(action, browser) {
const window = browser.ownerGlobal;
@@ -324,6 +325,9 @@ const SpecialMessageActions = {
true
);
break;
case "SHOW_SPOTLIGHT":
Spotlight.showSpotlightDialog(browser, action.data);
break;
default:
throw new Error(
`Special message action with type ${action.type} is unsupported.`

View File

@@ -422,6 +422,29 @@
"required": ["data", "type"],
"additionalProperties": false,
"description": "Resets homepage pref and sections layout"
},
{
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
"content": {
"type": "object",
"description": "Object containing content rendered inside spotlight dialog"
}
},
"required": ["content"],
"additionalProperties": false
},
"type": {
"type": "string",
"enum": ["SHOW_SPOTLIGHT"]
}
},
"required": ["data", "type"],
"additionalProperties": false,
"description": "Opens a spotlight dialog"
}
]
}

View File

@@ -256,3 +256,7 @@ Action for enabling the Total Cookie Protection feature.
Action for disabling the Total Cookie Protection feature and enabling an
additional privacy section in about:preferences.
### `SHOW_SPOTLIGHT`
Action for opening a spotlight tab or window modal using the content passed to the dialog.

View File

@@ -11,6 +11,7 @@ support-files =
[browser_sma_open_protection_panel.js]
[browser_sma_open_protection_report.js]
[browser_sma_open_url.js]
[browser_sma_open_spotlight_dialog.js]
[browser_sma_pin_current_tab.js]
[browser_sma_pin_firefox.js]
[browser_sma_show_firefox_accounts.js]

View File

@@ -0,0 +1,36 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { OnboardingMessageProvider } = ChromeUtils.import(
"resource://activity-stream/lib/OnboardingMessageProvider.jsm"
);
const { Spotlight } = ChromeUtils.import(
"resource://activity-stream/lib/Spotlight.jsm"
);
add_task(async function test_OPEN_SPOTLIGHT_DIALOG() {
let pbNewTabMessage = (
await OnboardingMessageProvider.getUntranslatedMessages()
).filter(m => m.id === "PB_NEWTAB_FOCUS_PROMO");
info(`Testing ${pbNewTabMessage[0].id}`);
let showSpotlightStub = sinon.stub(Spotlight, "showSpotlightDialog");
await SMATestUtils.executeAndValidateAction({
type: "SHOW_SPOTLIGHT",
data: { ...pbNewTabMessage[0].content.promoButton.action.data },
});
Assert.equal(
showSpotlightStub.callCount,
1,
"Should call showSpotlightDialog"
);
Assert.deepEqual(
showSpotlightStub.firstCall.args[1],
pbNewTabMessage[0].content.promoButton.action.data,
"Should be called with action.data"
);
});

View File

@@ -129,6 +129,7 @@ let RemotePageAccessManager = {
"ShouldShowSearch",
"ShouldShowSearchBanner",
"ShouldShowVPNPromo",
"SpecialMessageActionDispatch",
],
RPMAddMessageListener: ["*"],
RPMRemoveMessageListener: ["*"],