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:
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
"You’re 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();
|
||||
});
|
||||
@@ -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,
|
||||
"You’re 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();
|
||||
});
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.`
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
});
|
||||
@@ -129,6 +129,7 @@ let RemotePageAccessManager = {
|
||||
"ShouldShowSearch",
|
||||
"ShouldShowSearchBanner",
|
||||
"ShouldShowVPNPromo",
|
||||
"SpecialMessageActionDispatch",
|
||||
],
|
||||
RPMAddMessageListener: ["*"],
|
||||
RPMRemoveMessageListener: ["*"],
|
||||
|
||||
Reference in New Issue
Block a user