mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Bug 1661756 - Add VPN Card and Banner r=prathiksha,flod,nhnt11
Differential Revision: https://phabricator.services.mozilla.com/D88633
This commit is contained in:
parent
98201431da
commit
39db0a2518
@ -49,6 +49,16 @@ const SCOPE_MONITOR = [
|
||||
"https://identity.mozilla.com/apps/monitor",
|
||||
];
|
||||
|
||||
const SCOPE_VPN = "profile https://identity.mozilla.com/account/subscriptions";
|
||||
const VPN_ENDPOINT = `${Services.prefs.getStringPref(
|
||||
"identity.fxaccounts.auth.uri"
|
||||
)}oauth/subscriptions/active`;
|
||||
|
||||
// The ID of the vpn subscription, if we see this ID attached to a user's account then they have subscribed to vpn.
|
||||
const VPN_SUB_ID = Services.prefs.getStringPref(
|
||||
"browser.contentblocking.report.vpn_sub_id"
|
||||
);
|
||||
|
||||
// Error messages
|
||||
const INVALID_OAUTH_TOKEN = "Invalid OAuth token";
|
||||
const USER_UNSUBSCRIBED_TO_MONITOR = "User is not subscribed to Monitor";
|
||||
@ -301,6 +311,57 @@ class AboutProtectionsParent extends JSWindowActorParent {
|
||||
);
|
||||
}
|
||||
|
||||
async VPNSubStatus() {
|
||||
// For testing, set vpn sub status manually
|
||||
if (gTestOverride && "vpnOverrides" in gTestOverride) {
|
||||
return gTestOverride.vpnOverrides().hasSubscription;
|
||||
}
|
||||
|
||||
const vpnToken = await fxAccounts.getOAuthToken({ scope: SCOPE_VPN });
|
||||
let headers = new Headers();
|
||||
headers.append("Authorization", `Bearer ${vpnToken}`);
|
||||
const request = new Request(VPN_ENDPOINT, { headers });
|
||||
const res = await fetch(request);
|
||||
if (res.ok) {
|
||||
const result = await res.json();
|
||||
for (let sub of result) {
|
||||
if (sub.subscriptionId == VPN_SUB_ID) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// there was an error, assume user is not subscribed to VPN
|
||||
return false;
|
||||
}
|
||||
|
||||
// VPN shows if we are in a supported region and supported languages
|
||||
// VPN does not show in China - VPNs are illegal there, this is a requirement to hardcode, and not use in a pref.
|
||||
VPNShouldShow() {
|
||||
let currentRegion = "";
|
||||
if (gTestOverride && "vpnOverrides" in gTestOverride) {
|
||||
currentRegion = gTestOverride.vpnOverrides().location;
|
||||
} else {
|
||||
// The region we have detected the user to be in
|
||||
// We cannot run this in tests due to it using a request
|
||||
currentRegion = Region.current ? Region.current.toLowerCase() : "";
|
||||
}
|
||||
|
||||
// The region that the user has set as their home region
|
||||
const homeRegion = Region.home.toLowerCase() || "";
|
||||
const regionsWithVPN = Services.prefs.getStringPref(
|
||||
"browser.contentblocking.report.vpn_regions"
|
||||
);
|
||||
const language = Services.locale.appLocaleAsBCP47;
|
||||
|
||||
return (
|
||||
currentRegion != "cn" &&
|
||||
homeRegion != "cn" &&
|
||||
regionsWithVPN.includes(currentRegion) &&
|
||||
language.includes("en-")
|
||||
);
|
||||
}
|
||||
|
||||
async receiveMessage(aMessage) {
|
||||
let win = this.browsingContext.top.embedderElement.ownerGlobal;
|
||||
switch (aMessage.name) {
|
||||
@ -386,6 +447,12 @@ class AboutProtectionsParent extends JSWindowActorParent {
|
||||
|
||||
case "FetchEntryPoint":
|
||||
return entrypoint;
|
||||
|
||||
case "FetchVPNSubStatus":
|
||||
return this.VPNSubStatus();
|
||||
|
||||
case "FetchShowVPNCard":
|
||||
return this.VPNShouldShow();
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
@ -1699,6 +1699,15 @@ pref("browser.contentblocking.report.proxy.enabled", false);
|
||||
// Disable the mobile promotion by default.
|
||||
pref("browser.contentblocking.report.show_mobile_app", true);
|
||||
|
||||
// Enable the vpn card by default.
|
||||
pref("browser.contentblocking.report.vpn.enabled", true);
|
||||
// Only show vpn card to certain regions. Comma separated string of two letter ISO 3166-1 country codes.
|
||||
pref("browser.contentblocking.report.vpn_regions", "us,ca,nz,sg,my,gb");
|
||||
// Comma separated string of mozilla vpn supported platforms.
|
||||
pref("browser.contentblocking.report.vpn_platforms", "win");
|
||||
pref("browser.contentblocking.report.hide_vpn_banner", false);
|
||||
pref("browser.contentblocking.report.vpn_sub_id", "sub_HrfCZF7VPHzZkA");
|
||||
|
||||
pref("browser.contentblocking.report.monitor.url", "https://monitor.firefox.com/?entrypoint=protection_report_monitor&utm_source=about-protections");
|
||||
pref("browser.contentblocking.report.monitor.how_it_works.url", "https://monitor.firefox.com/about");
|
||||
pref("browser.contentblocking.report.monitor.sign_in_url", "https://monitor.firefox.com/oauth/init?entrypoint=protection_report_monitor&utm_source=about-protections&email=");
|
||||
@ -1711,6 +1720,10 @@ pref("browser.contentblocking.report.lockwise.mobile-ios.url", "https://apps.app
|
||||
pref("browser.contentblocking.report.lockwise.mobile-android.url", "https://play.google.com/store/apps/details?id=mozilla.lockbox&referrer=utm_source%3Dprotection_report%26utm_content%3Dmobile_promotion");
|
||||
pref("browser.contentblocking.report.mobile-ios.url", "https://apps.apple.com/app/firefox-private-safe-browser/id989804926");
|
||||
pref("browser.contentblocking.report.mobile-android.url", "https://play.google.com/store/apps/details?id=org.mozilla.firefox&referrer=utm_source%3Dprotection_report%26utm_content%3Dmobile_promotion");
|
||||
pref("browser.contentblocking.report.vpn.url", "https://vpn.mozilla.org/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=about-protections-card");
|
||||
pref("browser.contentblocking.report.vpn-promo.url", "https://vpn.mozilla.org/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=about-protections-top-promo");
|
||||
pref("browser.contentblocking.report.vpn-android.url", "https://play.google.com/store/apps/details?id=org.mozilla.firefox.vpn&referrer=utm_source%3Dfirefox-browser%26utm_medium%3Dfirefox-browser%26utm_campaign%3Dabout-protections-mobile-vpn%26anid%3D--");
|
||||
pref("browser.contentblocking.report.vpn-ios.url", "https://apps.apple.com/us/app/firefox-private-network-vpn/id1489407738");
|
||||
|
||||
// Protection Report's SUMO urls
|
||||
pref("browser.contentblocking.report.lockwise.how_it_works.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/password-manager-report");
|
||||
|
6
browser/base/content/logos/vpn-dark.svg
Normal file
6
browser/base/content/logos/vpn-dark.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<!-- 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/. -->
|
||||
<svg width="192" height="192" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M96 21.6c-7.953 0-14.4 6.447-14.4 14.4S88.047 50.4 96 50.4s14.4-6.447 14.4-14.4-6.447-14.4-14.4-14.4zM62.4 36C62.4 17.443 77.443 2.4 96 2.4c18.557 0 33.6 15.043 33.6 33.6 0 18.557-15.043 33.6-33.6 33.6a33.45 33.45 0 01-15.985-4.039L65.561 80.015A33.397 33.397 0 0168.21 86.4h55.582c4.131-13.88 16.988-24 32.209-24 18.557 0 33.6 15.043 33.6 33.6 0 18.557-15.043 33.6-33.6 33.6a33.452 33.452 0 01-15.985-4.039l-14.454 14.454A33.452 33.452 0 01129.6 156c0 18.557-15.043 33.6-33.6 33.6-18.557 0-33.6-15.043-33.6-33.6 0-18.557 15.043-33.6 33.6-33.6a33.452 33.452 0 0115.985 4.039l14.454-14.454a33.37 33.37 0 01-2.648-6.385H68.209c-4.131 13.879-16.988 24-32.209 24-18.557 0-33.6-15.043-33.6-33.6 0-18.557 15.043-33.6 33.6-33.6a33.45 33.45 0 0115.985 4.039l14.454-14.454A33.45 33.45 0 0162.4 36zm19.2 120c0-7.953 6.447-14.4 14.4-14.4s14.4 6.447 14.4 14.4-6.447 14.4-14.4 14.4-14.4-6.447-14.4-14.4zM36 81.6c-7.953 0-14.4 6.447-14.4 14.4s6.447 14.4 14.4 14.4 14.4-6.447 14.4-14.4S43.953 81.6 36 81.6zM141.6 96c0-7.953 6.447-14.4 14.4-14.4s14.4 6.447 14.4 14.4-6.447 14.4-14.4 14.4-14.4-6.447-14.4-14.4z" fill="#fff"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
6
browser/base/content/logos/vpn-light.svg
Normal file
6
browser/base/content/logos/vpn-light.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<!-- 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/. -->
|
||||
<svg width="188" height="188" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M94 19.6c-7.953 0-14.4 6.447-14.4 14.4S86.047 48.4 94 48.4s14.4-6.447 14.4-14.4-6.447-14.4-14.4-14.4zM60.4 34C60.4 15.443 75.443.4 94 .4c18.557 0 33.6 15.043 33.6 33.6 0 18.557-15.043 33.6-33.6 33.6a33.45 33.45 0 01-15.985-4.039L63.561 78.015A33.397 33.397 0 0166.21 84.4h55.582c4.131-13.88 16.988-24 32.209-24 18.557 0 33.6 15.043 33.6 33.6 0 18.557-15.043 33.6-33.6 33.6a33.452 33.452 0 01-15.985-4.039l-14.454 14.454A33.452 33.452 0 01127.6 154c0 18.557-15.043 33.6-33.6 33.6-18.557 0-33.6-15.043-33.6-33.6 0-18.557 15.043-33.6 33.6-33.6a33.452 33.452 0 0115.985 4.039l14.454-14.454a33.37 33.37 0 01-2.648-6.385H66.209c-4.131 13.879-16.988 24-32.209 24C15.443 127.6.4 112.557.4 94 .4 75.443 15.443 60.4 34 60.4a33.45 33.45 0 0115.985 4.039l14.454-14.454A33.45 33.45 0 0160.4 34zm19.2 120c0-7.953 6.447-14.4 14.4-14.4s14.4 6.447 14.4 14.4-6.447 14.4-14.4 14.4-14.4-6.447-14.4-14.4zM34 79.6c-7.953 0-14.4 6.447-14.4 14.4s6.447 14.4 14.4 14.4 14.4-6.447 14.4-14.4S41.953 79.6 34 79.6zM139.6 94c0-7.953 6.447-14.4 14.4-14.4s14.4 6.447 14.4 14.4-6.447 14.4-14.4 14.4-14.4-6.447-14.4-14.4z" fill="#000" fill-rule="evenodd"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
@ -22,6 +22,7 @@ const kESModuleList = new Set([
|
||||
/browser\/lockwise-card.js$/,
|
||||
/browser\/monitor-card.js$/,
|
||||
/browser\/proxy-card.js$/,
|
||||
/browser\/vpn-card.js$/,
|
||||
/toolkit\/content\/global\/certviewer\/components\/.*\.js$/,
|
||||
/toolkit\/content\/global\/certviewer\/.*\.js$/,
|
||||
]);
|
||||
|
@ -22,6 +22,8 @@ browser.jar:
|
||||
content/browser/logos/send.svg (content/logos/send.svg)
|
||||
content/browser/logos/tracking-protection.svg (content/logos/tracking-protection.svg)
|
||||
content/browser/logos/tracking-protection-dark-theme.svg (content/logos/tracking-protection-dark-theme.svg)
|
||||
content/browser/logos/vpn-dark.svg (content/logos/vpn-dark.svg)
|
||||
content/browser/logos/vpn-light.svg (content/logos/vpn-light.svg)
|
||||
content/browser/aboutNetErrorCodes.js (content/aboutNetErrorCodes.js)
|
||||
content/browser/aboutNetError.xhtml (content/aboutNetError.xhtml)
|
||||
content/browser/aboutNetError.js (content/aboutNetError.js)
|
||||
|
@ -33,10 +33,10 @@
|
||||
|
||||
--gear-icon-fill: var(--grey-90-a60);
|
||||
--hover-grey-link: var(--grey-70);
|
||||
--feature-banner-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
body {
|
||||
margin-block: 40px 80px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@ -85,9 +85,11 @@ h2 {
|
||||
#report-content {
|
||||
width: 763px;
|
||||
margin: 0 auto;
|
||||
margin-block: 40px 80px;
|
||||
}
|
||||
|
||||
.card-header .wrapper {
|
||||
.card-header .wrapper,
|
||||
.new-banner .wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
align-items: center;
|
||||
@ -97,6 +99,8 @@ h2 {
|
||||
.card-header > button,
|
||||
#save-passwords-button,
|
||||
#get-proxy-extension-link,
|
||||
#get-vpn-link,
|
||||
#vpn-banner-link,
|
||||
#manage-passwords-button,
|
||||
#sign-up-for-monitor-link {
|
||||
grid-area: 1 / 5 / 1 / -1;
|
||||
@ -108,6 +112,15 @@ h2 {
|
||||
line-height: initial;
|
||||
}
|
||||
|
||||
#vpn-banner-link {
|
||||
grid-area: 1 / 6 / 1 / -1;
|
||||
}
|
||||
|
||||
.new-banner .wrapper div:nth-child(1) {
|
||||
grid-area: 1 / 1 / 1 / 6;
|
||||
padding-inline-end: 15px;
|
||||
}
|
||||
|
||||
.lockwise-card.has-logins .wrapper div:nth-child(1) {
|
||||
grid-area: 1 / 1 / 1 / 6;
|
||||
}
|
||||
@ -126,6 +139,11 @@ h2 {
|
||||
grid-area: 1 / 1 / 1 / -1;
|
||||
}
|
||||
|
||||
.vpn-card.subscribed .wrapper div:nth-child(1) {
|
||||
padding-inline-end: 29px;
|
||||
grid-area: 1 / 1 / 1 / 7;
|
||||
}
|
||||
|
||||
/* We want to hide certain components depending on its state. */
|
||||
.no-logins .monitor-scanned-wrapper,
|
||||
.etp-card.custom-not-blocking .card-body,
|
||||
@ -133,6 +151,8 @@ h2 {
|
||||
#manage-protections,
|
||||
.etp-card .icon.dark,
|
||||
.proxy-card .icon.dark,
|
||||
.vpn-card .icon.dark,
|
||||
.vpn-banner .icon.dark,
|
||||
a.hidden,
|
||||
.loading .card-body,
|
||||
.lockwise-card.hidden,
|
||||
@ -156,6 +176,7 @@ a.hidden,
|
||||
.loading button,
|
||||
.loading .wrapper,
|
||||
.proxy-card.hidden,
|
||||
.vpn-card.hidden,
|
||||
.card-body.hidden,
|
||||
.hidden {
|
||||
display: none;
|
||||
@ -169,6 +190,15 @@ a.hidden,
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.vpn-card .icon {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
.new-banner .icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
@ -179,16 +209,21 @@ a.hidden,
|
||||
--cryptominer-highlight-color: #BEBECA;
|
||||
|
||||
--gear-icon-fill: rgba(249, 249, 250, 0.60);
|
||||
--hover-grey-link: var(--grey-30)
|
||||
--hover-grey-link: var(--grey-30);
|
||||
--feature-banner-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.etp-card .icon.dark,
|
||||
.proxy-card .icon.dark {
|
||||
.proxy-card .icon.dark,
|
||||
.vpn-card .icon.dark,
|
||||
.vpn-banner .icon.dark {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.etp-card .icon.light,
|
||||
.proxy-card .icon.light {
|
||||
.proxy-card .icon.light,
|
||||
.vpn-card .icon.light,
|
||||
.vpn-banner .icon.light {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@ -658,7 +693,7 @@ label[for="tab-cryptominer"]:hover ~ #highlight-hover {
|
||||
|
||||
.lockwise-scanned-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 7% auto;
|
||||
grid-template-columns: 24px auto;
|
||||
margin-block-start: 24px;
|
||||
grid-area: 2 / 1 / 2 / 5;
|
||||
padding-bottom: 1.7em;
|
||||
@ -677,6 +712,18 @@ label[for="tab-cryptominer"]:hover ~ #highlight-hover {
|
||||
margin-inline-end: 15px;
|
||||
}
|
||||
|
||||
.vpn-card.subscribed #get-vpn-link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vpn-card:not(.subscribed) .content.subscribed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vpn-card.subscribed .content:not(.subscribed) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Monitor card */
|
||||
.monitor-info-wrapper {
|
||||
display: grid;
|
||||
@ -947,6 +994,8 @@ label[for="tab-cryptominer"]:hover ~ #highlight-hover {
|
||||
#manage-protections,
|
||||
#sign-up-for-monitor-link,
|
||||
#get-proxy-extension-link,
|
||||
#get-vpn-link,
|
||||
#vpn-banner-link,
|
||||
.monitor-partial-breaches-link-wrapper,
|
||||
.monitor-breaches-link-wrapper {
|
||||
background-color: var(--in-content-primary-button-background);
|
||||
@ -959,6 +1008,8 @@ label[for="tab-cryptominer"]:hover ~ #highlight-hover {
|
||||
#manage-protections:active,
|
||||
#sign-up-for-monitor-link:active,
|
||||
#get-proxy-extension-link:active,
|
||||
#get-vpn-link:active,
|
||||
#vpn-banner-link:active,
|
||||
#monitor-partial-breaches-link:active,
|
||||
#monitor-breaches-link:active {
|
||||
background-color: var(--in-content-primary-button-background-active);
|
||||
@ -967,6 +1018,8 @@ label[for="tab-cryptominer"]:hover ~ #highlight-hover {
|
||||
#manage-protections:hover,
|
||||
#sign-up-for-monitor-link:hover,
|
||||
#get-proxy-extension-link:hover,
|
||||
#get-vpn-link:hover,
|
||||
#vpn-banner-link:hover,
|
||||
#monitor-partial-breaches-link:hover,
|
||||
#monitor-breaches-link:hover {
|
||||
background-color: var(--in-content-primary-button-background-hover);
|
||||
@ -975,6 +1028,8 @@ label[for="tab-cryptominer"]:hover ~ #highlight-hover {
|
||||
#manage-protections:focus,
|
||||
#sign-up-for-monitor-link:focus,
|
||||
#get-proxy-extension-link:focus,
|
||||
#get-vpn-link:focus,
|
||||
#vpn-banner-link:focus,
|
||||
#monitor-partial-breaches-link:focus,
|
||||
.monitor-block > a:focus,
|
||||
#monitor-breaches-link:focus {
|
||||
@ -1025,3 +1080,54 @@ label[for="tab-cryptominer"]:hover ~ #highlight-hover {
|
||||
opacity: 0.02;
|
||||
}
|
||||
}
|
||||
|
||||
.new-banner {
|
||||
width: 100%;
|
||||
background: var(--feature-banner-color);
|
||||
}
|
||||
|
||||
.banner-wrapper {
|
||||
width: 763px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 7fr;
|
||||
grid-gap: var(--card-padding);
|
||||
line-height: 1.3em;
|
||||
margin: 0 auto;
|
||||
padding: 12px var(--card-padding);
|
||||
}
|
||||
|
||||
.new-banner .banner-title {
|
||||
margin: 0;
|
||||
line-height: 1.25;
|
||||
cursor: default;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.new-banner .content {
|
||||
margin-block: 5px 0;
|
||||
font-size: 0.88em;
|
||||
cursor: default;
|
||||
color: var(--in-content-deemphasized-text);
|
||||
}
|
||||
|
||||
.new-banner .exit-icon {
|
||||
top: auto;
|
||||
inset-inline-end: 30px;
|
||||
}
|
||||
|
||||
.vpn-card .title-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 24px auto;
|
||||
}
|
||||
|
||||
.vpn-card:not(.subscribed) .card-title {
|
||||
grid-area: 1 / 1 / 1 / -1;
|
||||
}
|
||||
|
||||
.vpn-card.subscribed .card-title {
|
||||
margin-inline-start: 3px;
|
||||
}
|
||||
|
||||
.vpn-card:not(.subscribed) #check-icon {
|
||||
display: none;
|
||||
}
|
||||
|
@ -11,3 +11,16 @@
|
||||
proxy-title = Stay safe on public Wi-Fi
|
||||
proxy-header-content = { -secure-proxy-brand-name } makes wireless hotspots more secure to protect you from hackers.
|
||||
get-proxy-extension-link = Get the Extension
|
||||
|
||||
vpn-title = Take privacy protections beyond the browser
|
||||
vpn-header-content = Protect your entire device with { -mozilla-vpn-brand-name }. One tap encrypts all traffic and hides your location.
|
||||
get-vpn-link = Get { -mozilla-vpn-brand-name }
|
||||
|
||||
vpn-title-subscribed = VPN: Subscribed
|
||||
# Note This text is not being translated, and the <br> will need to be removed if or when it does get translated
|
||||
vpn-header-content-subscribed = Using the { -mozilla-vpn-brand-name } encrypts all your traffic and hides your location — on up to 5 devices. Get the most from your subscription — add it from <br> the <a data-l10n-name="vpn-google-playstore-link">Google Play Store</a> or <a data-l10n-name="vpn-app-store-link">Apple App Store</a>.
|
||||
|
||||
vpn-banner-header = Protection that extends beyond the browser
|
||||
# Note This text is not being translated, and the <br> will need to be removed if or when it does get translated
|
||||
vpn-banner-content = Try { -mozilla-vpn-brand-name } risk-free and see why TechRadar says <br> “its speed, simplicity and low monthly price make it worth a look.”
|
||||
vpn-banner-link = Get { -mozilla-vpn-brand-name }
|
||||
|
@ -20,10 +20,25 @@
|
||||
<script type="module" src="chrome://browser/content/lockwise-card.js"></script>
|
||||
<script type="module" src="chrome://browser/content/monitor-card.js"></script>
|
||||
<script type="module" src="chrome://browser/content/proxy-card.js"></script>
|
||||
<script type="module" src="chrome://browser/content/vpn-card.js"></script>
|
||||
<title data-l10n-id="protection-report-webpage-title"></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="new-banner vpn-banner hidden">
|
||||
<div class=banner-wrapper>
|
||||
<img class="icon light" src="chrome://browser/content/logos/vpn-light.svg"/>
|
||||
<img class="icon dark" src="chrome://browser/content/logos/vpn-dark.svg"/>
|
||||
<div class="wrapper">
|
||||
<div>
|
||||
<h3 class="banner-title" data-l10n-id="vpn-banner-header"></h3>
|
||||
<span class="content" data-l10n-id="vpn-banner-content"></span>
|
||||
</div>
|
||||
<a target="_blank" id="vpn-banner-link" data-l10n-id="get-vpn-link"></a>
|
||||
<button class="exit-icon" data-l10n-id="protections-close-button2"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="report-content">
|
||||
<h1 id="report-title" data-l10n-id="protection-report-page-content-title"></h1>
|
||||
<p id="report-summary" data-l10n-id="protection-report-page-summary-default"></p>
|
||||
@ -287,6 +302,27 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card card-no-hover vpn-card hidden">
|
||||
<div class="card-header">
|
||||
<img class="icon light" src="chrome://browser/content/logos/vpn-light.svg"/>
|
||||
<img class="icon dark" src="chrome://browser/content/logos/vpn-dark.svg"/>
|
||||
<div class="wrapper">
|
||||
<div>
|
||||
<div class="title-wrapper">
|
||||
<img id="check-icon" src="chrome://browser/skin/protections/resolved-breach.svg">
|
||||
<h3 class="card-title" data-l10n-id="vpn-title"></h3>
|
||||
</div>
|
||||
<p class="content" data-l10n-id="vpn-header-content"></p>
|
||||
<p class="content subscribed" data-l10n-id="vpn-header-content-subscribed">
|
||||
<a target="_blank" id="vpn-google-playstore-link" data-l10n-name="vpn-google-playstore-link"></a>
|
||||
<a target="_blank" id="vpn-app-store-link" data-l10n-name="vpn-app-store-link"></a>
|
||||
</p>
|
||||
</div>
|
||||
<a target="_blank" id="get-vpn-link" data-l10n-id="get-vpn-link"></a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -7,6 +7,7 @@
|
||||
import LockwiseCard from "./lockwise-card.js";
|
||||
import MonitorCard from "./monitor-card.js";
|
||||
import ProxyCard from "./proxy-card.js";
|
||||
import VPNCard from "./vpn-card.js";
|
||||
|
||||
let cbCategory = RPMGetStringPref("browser.contentblocking.category");
|
||||
document.sendTelemetryEvent = (action, object, value = "") => {
|
||||
@ -480,4 +481,16 @@ document.addEventListener("DOMContentLoaded", e => {
|
||||
// For tests
|
||||
const proxyUI = document.querySelector(".proxy-card");
|
||||
proxyUI.dataset.enabled = proxyEnabled;
|
||||
|
||||
const VPNEnabled = RPMGetBoolPref(
|
||||
"browser.contentblocking.report.vpn.enabled",
|
||||
true
|
||||
);
|
||||
if (VPNEnabled) {
|
||||
const vpnCard = new VPNCard(document);
|
||||
vpnCard.init();
|
||||
}
|
||||
// For tests
|
||||
const vpnUI = document.querySelector(".vpn-card");
|
||||
vpnUI.dataset.enabled = VPNEnabled;
|
||||
});
|
||||
|
116
browser/components/protections/content/vpn-card.js
Normal file
116
browser/components/protections/content/vpn-card.js
Normal file
@ -0,0 +1,116 @@
|
||||
/* 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-env mozilla/frame-script */
|
||||
export default class VPNCard {
|
||||
constructor(document) {
|
||||
this.doc = document;
|
||||
}
|
||||
|
||||
init() {
|
||||
const vpnLink = this.doc.getElementById("get-vpn-link");
|
||||
const vpnBannerLink = this.doc.getElementById("vpn-banner-link");
|
||||
vpnLink.href = RPMGetStringPref(
|
||||
"browser.contentblocking.report.vpn.url",
|
||||
""
|
||||
);
|
||||
vpnBannerLink.href = RPMGetStringPref(
|
||||
"browser.contentblocking.report.vpn-promo.url",
|
||||
""
|
||||
);
|
||||
|
||||
vpnLink.addEventListener("click", () => {
|
||||
this.doc.sendTelemetryEvent("click", "vpn_card_link");
|
||||
});
|
||||
let androidVPNAppLink = document.getElementById(
|
||||
"vpn-google-playstore-link"
|
||||
);
|
||||
androidVPNAppLink.href = RPMGetStringPref(
|
||||
"browser.contentblocking.report.vpn-android.url"
|
||||
);
|
||||
androidVPNAppLink.addEventListener("click", () => {
|
||||
document.sendTelemetryEvent("click", "vpn_app_link_android");
|
||||
});
|
||||
let iosVPNAppLink = document.getElementById("vpn-app-store-link");
|
||||
iosVPNAppLink.href = RPMGetStringPref(
|
||||
"browser.contentblocking.report.vpn-ios.url"
|
||||
);
|
||||
iosVPNAppLink.addEventListener("click", () => {
|
||||
document.sendTelemetryEvent("click", "vpn_app_link_ios");
|
||||
});
|
||||
|
||||
const vpnBanner = this.doc.querySelector(".vpn-banner");
|
||||
const exitIcon = vpnBanner.querySelector(".exit-icon");
|
||||
vpnBannerLink.addEventListener("click", () => {
|
||||
this.doc.sendTelemetryEvent("click", "vpn_banner_link");
|
||||
});
|
||||
// User has closed the vpn banner, hide it.
|
||||
exitIcon.addEventListener("click", () => {
|
||||
vpnBanner.classList.add("hidden");
|
||||
this.doc.sendTelemetryEvent("click", "vpn_banner_close");
|
||||
});
|
||||
|
||||
this.showVPNCard();
|
||||
}
|
||||
|
||||
// Show the VPN card if user is located in areas, and on platforms, it serves
|
||||
async showVPNCard() {
|
||||
const showVPNBanner = this.showVPNBanner.bind(this);
|
||||
RPMSendQuery("FetchShowVPNCard", {}).then(shouldShow => {
|
||||
if (!shouldShow) {
|
||||
return;
|
||||
}
|
||||
const vpnCard = this.doc.querySelector(".vpn-card");
|
||||
let availablePlatforms = RPMGetStringPref(
|
||||
"browser.contentblocking.report.vpn_platforms"
|
||||
);
|
||||
|
||||
let hasSupportedPlatform = false;
|
||||
for (let platform of availablePlatforms.split(",")) {
|
||||
if (navigator.platform.toLowerCase().includes(platform)) {
|
||||
hasSupportedPlatform = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasSupportedPlatform) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add 'subscribed' class if user is subscribed to vpn
|
||||
RPMSendQuery("FetchVPNSubStatus", {}).then(async hasVPN => {
|
||||
if (hasVPN) {
|
||||
vpnCard.classList.add("subscribed");
|
||||
vpnCard
|
||||
.querySelector(".card-title")
|
||||
.setAttribute("data-l10n-id", "vpn-title-subscribed");
|
||||
|
||||
// hide the promo banner if the user is already subscribed to vpn
|
||||
await RPMSetBoolPref(
|
||||
"browser.contentblocking.report.hide_vpn_banner",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
vpnCard.classList.remove("hidden");
|
||||
showVPNBanner();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
showVPNBanner() {
|
||||
if (
|
||||
RPMGetBoolPref("browser.contentblocking.report.hide_vpn_banner", false) ||
|
||||
!RPMGetBoolPref("browser.contentblocking.report.vpn.enabled", false)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const vpnBanner = this.doc.querySelector(".vpn-banner");
|
||||
vpnBanner.classList.remove("hidden");
|
||||
this.doc.sendTelemetryEvent("show", "vpn_banner");
|
||||
// VPN banner only shows on the first visit, flip a pref so it does not show again.
|
||||
RPMSetBoolPref("browser.contentblocking.report.hide_vpn_banner", true);
|
||||
}
|
||||
}
|
@ -9,3 +9,4 @@ browser.jar:
|
||||
content/browser/protections.html (content/protections.html)
|
||||
content/browser/protections.js (content/protections.js)
|
||||
content/browser/proxy-card.js (content/proxy-card.js)
|
||||
content/browser/vpn-card.js (content/vpn-card.js)
|
||||
|
@ -10,3 +10,4 @@ support-files =
|
||||
[browser_protections_proxy.js]
|
||||
[browser_protections_report_ui.js]
|
||||
[browser_protections_telemetry.js]
|
||||
[browser_protections_vpn.js]
|
||||
|
@ -13,6 +13,7 @@ add_task(async function setup() {
|
||||
set: [
|
||||
["browser.contentblocking.report.monitor.enabled", false],
|
||||
["browser.contentblocking.report.lockwise.enabled", false],
|
||||
["browser.contentblocking.report.vpn.enabled", false],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
@ -31,7 +31,10 @@ const SQL = {
|
||||
|
||||
add_task(async function setup() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.contentblocking.database.enabled", true]],
|
||||
set: [
|
||||
["browser.contentblocking.database.enabled", true],
|
||||
["browser.contentblocking.report.vpn.enabled", false],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -10,6 +10,10 @@ XPCOMUtils.defineLazyServiceGetter(
|
||||
"nsITrackingDBService"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
Region: "resource://gre/modules/Region.jsm",
|
||||
});
|
||||
|
||||
const { AboutProtectionsParent } = ChromeUtils.import(
|
||||
"resource:///actors/AboutProtectionsParent.jsm"
|
||||
);
|
||||
@ -46,10 +50,9 @@ requestLongerTimeout(2);
|
||||
add_task(async function setup() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.contentblocking.database.enabled", true],
|
||||
["browser.contentblocking.report.monitor.enabled", true],
|
||||
["browser.contentblocking.report.lockwise.enabled", true],
|
||||
["browser.contentblocking.report.proxy.enabled", true],
|
||||
["browser.contentblocking.report.vpn_regions", "us,ca,nz,sg,my,gb"],
|
||||
["browser.contentblocking.report.vpn_platforms", "win"],
|
||||
|
||||
// Change the endpoints to prevent non-local network connections when landing on the page.
|
||||
["browser.contentblocking.report.monitor.url", ""],
|
||||
["browser.contentblocking.report.monitor.sign_in_url", ""],
|
||||
@ -64,6 +67,10 @@ add_task(async function setup() {
|
||||
["browser.contentblocking.report.mobile-android.url", ""],
|
||||
["browser.contentblocking.report.monitor.home_page_url", ""],
|
||||
["browser.contentblocking.report.monitor.preferences_url", ""],
|
||||
["browser.contentblocking.report.vpn.url", ""],
|
||||
["browser.contentblocking.report.vpn-promo.url", ""],
|
||||
["browser.contentblocking.report.vpn-android.url", ""],
|
||||
["browser.contentblocking.report.vpn-ios.url", ""],
|
||||
],
|
||||
});
|
||||
|
||||
@ -71,16 +78,21 @@ add_task(async function setup() {
|
||||
Services.telemetry.canRecordExtended = true;
|
||||
registerCleanupFunction(() => {
|
||||
Services.telemetry.canRecordExtended = oldCanRecord;
|
||||
// AboutProtectionsParent.setTestOverride(null);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function checkTelemetryLoadEvents() {
|
||||
// There's an arbitrary interval of 2 seconds in which the content
|
||||
// processes sync their event data with the parent process, we wait
|
||||
// this out to ensure that we clear everything that is left over from
|
||||
// previous tests and don't receive random events in the middle of our tests.
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
await new Promise(c => setTimeout(c, 2000));
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.contentblocking.database.enabled", false],
|
||||
["browser.contentblocking.report.monitor.enabled", false],
|
||||
["browser.contentblocking.report.lockwise.enabled", false],
|
||||
["browser.contentblocking.report.proxy.enabled", false],
|
||||
["browser.contentblocking.report.vpn.enabled", false],
|
||||
],
|
||||
});
|
||||
await addArbitraryTimeout();
|
||||
|
||||
// Clear everything.
|
||||
Services.telemetry.clearEvents();
|
||||
@ -158,13 +170,26 @@ function waitForTelemetryEventCount(count) {
|
||||
}, "waiting for telemetry event count of: " + count);
|
||||
}
|
||||
|
||||
add_task(async function checkTelemetryClickEvents() {
|
||||
let addArbitraryTimeout = async () => {
|
||||
// There's an arbitrary interval of 2 seconds in which the content
|
||||
// processes sync their event data with the parent process, we wait
|
||||
// this out to ensure that we clear everything that is left over from
|
||||
// previous tests and don't receive random events in the middle of our tests.
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
await new Promise(c => setTimeout(c, 2000));
|
||||
};
|
||||
|
||||
add_task(async function checkTelemetryClickEvents() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.contentblocking.database.enabled", true],
|
||||
["browser.contentblocking.report.monitor.enabled", true],
|
||||
["browser.contentblocking.report.lockwise.enabled", true],
|
||||
["browser.contentblocking.report.proxy.enabled", true],
|
||||
["browser.contentblocking.report.vpn.enabled", false],
|
||||
],
|
||||
});
|
||||
await addArbitraryTimeout();
|
||||
|
||||
// Clear everything.
|
||||
Services.telemetry.clearEvents();
|
||||
@ -820,12 +845,16 @@ add_task(async function test_save_telemetry() {
|
||||
// Test that telemetry is sent if entrypoint param is included,
|
||||
// and test that it is recorded as default if entrypoint param is not properly included
|
||||
add_task(async function checkTelemetryLoadEventForEntrypoint() {
|
||||
// There's an arbitrary interval of 2 seconds in which the content
|
||||
// processes sync their event data with the parent process, we wait
|
||||
// this out to ensure that we clear everything that is left over from
|
||||
// previous tests and don't receive random events in the middle of our tests.
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
await new Promise(c => setTimeout(c, 2000));
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.contentblocking.database.enabled", false],
|
||||
["browser.contentblocking.report.monitor.enabled", false],
|
||||
["browser.contentblocking.report.lockwise.enabled", false],
|
||||
["browser.contentblocking.report.proxy.enabled", false],
|
||||
["browser.contentblocking.report.vpn.enabled", false],
|
||||
],
|
||||
});
|
||||
await addArbitraryTimeout();
|
||||
|
||||
// Clear everything.
|
||||
Services.telemetry.clearEvents();
|
||||
@ -903,3 +932,248 @@ add_task(async function checkTelemetryLoadEventForEntrypoint() {
|
||||
// Clean up.
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
// This test is skipping due to failures on try, it passes locally.
|
||||
// Test that telemetry is sent from the vpn card
|
||||
add_task(async function checkTelemetryClickEventsVPN() {
|
||||
if (Services.sysinfo.getProperty("name") != "Windows_NT") {
|
||||
ok(true, "User is on an unsupported platform, the vpn card will not show");
|
||||
return;
|
||||
}
|
||||
await addArbitraryTimeout();
|
||||
// Clear everything.
|
||||
Services.telemetry.clearEvents();
|
||||
await TestUtils.waitForCondition(() => {
|
||||
let events = Services.telemetry.snapshotEvents(
|
||||
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
|
||||
true
|
||||
).content;
|
||||
return !events || !events.length;
|
||||
});
|
||||
Services.telemetry.setEventRecordingEnabled("security.ui.protections", true);
|
||||
|
||||
// user is not subscribed to VPN, and is in the us
|
||||
AboutProtectionsParent.setTestOverride(getVPNOverrides(false, "us"));
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.contentblocking.database.enabled", false],
|
||||
["browser.contentblocking.report.monitor.enabled", false],
|
||||
["browser.contentblocking.report.lockwise.enabled", false],
|
||||
["browser.contentblocking.report.proxy.enabled", false],
|
||||
["browser.contentblocking.report.vpn.enabled", true],
|
||||
["browser.contentblocking.report.vpn_regions", "us,ca,nz,sg,my,gb,cn"],
|
||||
["browser.contentblocking.report.vpn_platforms", "win"],
|
||||
["browser.contentblocking.report.hide_vpn_banner", true],
|
||||
["browser.contentblocking.report.vpn-android.url", ""],
|
||||
["browser.contentblocking.report.vpn-ios.url", ""],
|
||||
["browser.contentblocking.report.vpn.url", ""],
|
||||
],
|
||||
});
|
||||
Services.locale.availableLocales = ["en-US"];
|
||||
Services.locale.requestedLocales = ["en-US"];
|
||||
Region._setHomeRegion("US", false);
|
||||
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab({
|
||||
url: "about:protections",
|
||||
gBrowser,
|
||||
});
|
||||
|
||||
info("checking for vpn link");
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
|
||||
const getVPNLink = await ContentTaskUtils.waitForCondition(() => {
|
||||
return content.document.getElementById("get-vpn-link");
|
||||
}, "get vpn link exists");
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() => ContentTaskUtils.is_visible(getVPNLink),
|
||||
"get vpn link is visible"
|
||||
);
|
||||
await EventUtils.sendMouseEvent(
|
||||
{ type: "click", button: 1 },
|
||||
getVPNLink,
|
||||
content
|
||||
);
|
||||
});
|
||||
|
||||
let events = await waitForTelemetryEventCount(2);
|
||||
events = events.filter(
|
||||
e =>
|
||||
e[1] == "security.ui.protections" &&
|
||||
e[2] == "click" &&
|
||||
e[3] == "vpn_card_link"
|
||||
);
|
||||
is(
|
||||
events.length,
|
||||
1,
|
||||
`recorded telemetry for vpn_card_link when user is not subscribed`
|
||||
);
|
||||
|
||||
// User is subscribed to VPN
|
||||
AboutProtectionsParent.setTestOverride(getVPNOverrides(true, "us"));
|
||||
await reloadTab(tab);
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
|
||||
const androidVPNLink = await ContentTaskUtils.waitForCondition(() => {
|
||||
return content.document.getElementById("vpn-google-playstore-link");
|
||||
}, "android vpn link exists");
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() => ContentTaskUtils.is_visible(androidVPNLink),
|
||||
"android vpn link is visible"
|
||||
);
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
return content.document
|
||||
.querySelector(".vpn-card")
|
||||
.classList.contains("subscribed");
|
||||
}, "subscribed class is added to the vpn card");
|
||||
|
||||
await EventUtils.sendMouseEvent(
|
||||
{ type: "click", button: 1 },
|
||||
androidVPNLink,
|
||||
content
|
||||
);
|
||||
});
|
||||
|
||||
events = await waitForTelemetryEventCount(5);
|
||||
events = events.filter(
|
||||
e =>
|
||||
e[1] == "security.ui.protections" &&
|
||||
e[2] == "click" &&
|
||||
e[3] == "vpn_app_link_android"
|
||||
);
|
||||
is(events.length, 1, `recorded telemetry for vpn_app_link_android link`);
|
||||
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
|
||||
const iosVPNLink = await ContentTaskUtils.waitForCondition(() => {
|
||||
return content.document.getElementById("vpn-app-store-link");
|
||||
}, "ios vpn link exists");
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() => ContentTaskUtils.is_visible(iosVPNLink),
|
||||
"ios vpn link is visible"
|
||||
);
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
return content.document
|
||||
.querySelector(".vpn-card")
|
||||
.classList.contains("subscribed");
|
||||
}, "subscribed class is added to the vpn card");
|
||||
|
||||
await EventUtils.sendMouseEvent(
|
||||
{ type: "click", button: 1 },
|
||||
iosVPNLink,
|
||||
content
|
||||
);
|
||||
});
|
||||
|
||||
events = await waitForTelemetryEventCount(6);
|
||||
events = events.filter(
|
||||
e =>
|
||||
e[1] == "security.ui.protections" &&
|
||||
e[2] == "click" &&
|
||||
e[3] == "vpn_app_link_ios"
|
||||
);
|
||||
is(events.length, 1, `recorded telemetry for vpn_app_link_ios link`);
|
||||
|
||||
// Clean up.
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
}).skip();
|
||||
|
||||
// This test is skipping due to failures on try, it passes locally.
|
||||
// Test that telemetry is sent from the vpn banner
|
||||
add_task(async function checkTelemetryEventsVPNBanner() {
|
||||
if (Services.sysinfo.getProperty("name") != "Windows_NT") {
|
||||
ok(true, "User is on an unsupported platform, the vpn card will not show");
|
||||
return;
|
||||
}
|
||||
AboutProtectionsParent.setTestOverride(getVPNOverrides(false, "us"));
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.contentblocking.database.enabled", false],
|
||||
["browser.contentblocking.report.monitor.enabled", false],
|
||||
["browser.contentblocking.report.lockwise.enabled", false],
|
||||
["browser.contentblocking.report.proxy.enabled", false],
|
||||
["browser.contentblocking.report.vpn.enabled", true],
|
||||
["browser.contentblocking.report.vpn_regions", "us,ca,nz,sg,my,gb"],
|
||||
["browser.contentblocking.report.vpn_platforms", "win"],
|
||||
["browser.contentblocking.report.hide_vpn_banner", false],
|
||||
["browser.contentblocking.report.vpn-promo.url", ""],
|
||||
],
|
||||
});
|
||||
await addArbitraryTimeout();
|
||||
|
||||
// The VPN banner only shows if the user is in en*
|
||||
Services.locale.availableLocales = ["en-US"];
|
||||
Services.locale.requestedLocales = ["en-US"];
|
||||
|
||||
// Clear everything.
|
||||
Services.telemetry.clearEvents();
|
||||
await TestUtils.waitForCondition(() => {
|
||||
let events = Services.telemetry.snapshotEvents(
|
||||
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
|
||||
true
|
||||
).content;
|
||||
return !events || !events.length;
|
||||
});
|
||||
|
||||
Services.telemetry.setEventRecordingEnabled("security.ui.protections", true);
|
||||
// User is not subscribed to VPN
|
||||
AboutProtectionsParent.setTestOverride(getVPNOverrides(false, "us"));
|
||||
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab({
|
||||
url: "about:protections",
|
||||
gBrowser,
|
||||
});
|
||||
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
|
||||
const bannerVPNLink = await ContentTaskUtils.waitForCondition(() => {
|
||||
return content.document.getElementById("vpn-banner-link");
|
||||
}, "vpn banner link exists");
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() => ContentTaskUtils.is_visible(bannerVPNLink),
|
||||
"vpn banner link is visible"
|
||||
);
|
||||
await EventUtils.sendMouseEvent(
|
||||
{ type: "click", button: 1 },
|
||||
bannerVPNLink,
|
||||
content
|
||||
);
|
||||
});
|
||||
|
||||
let events = await waitForTelemetryEventCount(3);
|
||||
events = events.filter(
|
||||
e =>
|
||||
e[1] == "security.ui.protections" &&
|
||||
e[2] == "click" &&
|
||||
e[3] == "vpn_banner_link"
|
||||
);
|
||||
is(events.length, 1, `recorded telemetry for vpn_banner_link`);
|
||||
|
||||
// VPN Banner flips this pref each time it shows, flip back between each instruction.
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.contentblocking.report.hide_vpn_banner", false]],
|
||||
});
|
||||
|
||||
await reloadTab(tab);
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
|
||||
const bannerExitLink = await ContentTaskUtils.waitForCondition(() => {
|
||||
return content.document.querySelector(".vpn-banner .exit-icon");
|
||||
}, "vpn banner exit link exists");
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() => ContentTaskUtils.is_visible(bannerExitLink),
|
||||
"vpn banner exit link is visible"
|
||||
);
|
||||
await EventUtils.sendMouseEvent(
|
||||
{ type: "click", button: 1 },
|
||||
bannerExitLink,
|
||||
content
|
||||
);
|
||||
});
|
||||
|
||||
events = await waitForTelemetryEventCount(7);
|
||||
events = events.filter(
|
||||
e =>
|
||||
e[1] == "security.ui.protections" &&
|
||||
e[2] == "click" &&
|
||||
e[3] == "vpn_banner_close"
|
||||
);
|
||||
is(events.length, 1, `recorded telemetry for vpn_banner_close`);
|
||||
|
||||
// Clean up.
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
}).skip();
|
||||
|
@ -0,0 +1,277 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
Region: "resource://gre/modules/Region.jsm",
|
||||
});
|
||||
|
||||
const { AboutProtectionsParent } = ChromeUtils.import(
|
||||
"resource:///actors/AboutProtectionsParent.jsm"
|
||||
);
|
||||
|
||||
add_task(async function setup() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.contentblocking.report.monitor.enabled", false],
|
||||
["browser.contentblocking.report.lockwise.enabled", false],
|
||||
["browser.contentblocking.report.vpn.enabled", true],
|
||||
],
|
||||
});
|
||||
AboutProtectionsParent.setTestOverride(getVPNOverrides(false, "us"));
|
||||
const avLocales = Services.locale.availableLocales;
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
Services.locale.availableLocales = avLocales;
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function testVPNCardVisibility() {
|
||||
if (Services.sysinfo.getProperty("name") != "Windows_NT") {
|
||||
ok(true, "User is on an unsupported platform, the vpn card will not show");
|
||||
return;
|
||||
}
|
||||
AboutProtectionsParent.setTestOverride(getVPNOverrides(false, "my"));
|
||||
Region._setHomeRegion("my", false);
|
||||
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab({
|
||||
url: "about:protections",
|
||||
gBrowser,
|
||||
});
|
||||
|
||||
info("Enable showing the VPN card");
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.contentblocking.report.vpn.enabled", true],
|
||||
["browser.contentblocking.report.vpn_regions", "us,ca,nz,sg,my,gb"],
|
||||
["browser.contentblocking.report.vpn_platforms", "win"],
|
||||
],
|
||||
});
|
||||
|
||||
info("Check that vpn card is hidden if user's language is not en*");
|
||||
Services.locale.availableLocales = ["ko-KR", "ar"];
|
||||
Services.locale.requestedLocales = ["ko-KR"];
|
||||
await reloadTab(tab);
|
||||
await checkVPNCardVisibility(tab, true);
|
||||
|
||||
info("Check that vpn card is shown if user's language is en*");
|
||||
// Set language back to en-US
|
||||
Services.locale.availableLocales = ["en-US"];
|
||||
Services.locale.requestedLocales = ["en-US"];
|
||||
await reloadTab(tab);
|
||||
await checkVPNCardVisibility(tab, false);
|
||||
|
||||
info(
|
||||
"Check that vpn card is hidden if user's location is not on the regions list."
|
||||
);
|
||||
AboutProtectionsParent.setTestOverride(getVPNOverrides(false, "ls"));
|
||||
await reloadTab(tab);
|
||||
await checkVPNCardVisibility(tab, true);
|
||||
|
||||
info(
|
||||
"Check that vpn card shows a different version if user has subscribed to Mozilla vpn."
|
||||
);
|
||||
AboutProtectionsParent.setTestOverride(getVPNOverrides(true, "us"));
|
||||
await reloadTab(tab);
|
||||
await checkVPNCardVisibility(tab, false, true);
|
||||
|
||||
info(
|
||||
"VPN card should be hidden when vpn not enabled, though all other conditions are true"
|
||||
);
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.contentblocking.report.vpn.enabled", false]],
|
||||
});
|
||||
await reloadTab(tab);
|
||||
await checkVPNCardVisibility(tab, true);
|
||||
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
async function checkVPNCardVisibility(tab, shouldBeHidden, subscribed = false) {
|
||||
await SpecialPowers.spawn(
|
||||
tab.linkedBrowser,
|
||||
[{ _shouldBeHidden: shouldBeHidden, _subscribed: subscribed }],
|
||||
async function({ _shouldBeHidden, _subscribed }) {
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
const vpnCard = content.document.querySelector(".vpn-card");
|
||||
const subscribedStateCorrect =
|
||||
vpnCard.classList.contains("subscribed") == _subscribed;
|
||||
return (
|
||||
ContentTaskUtils.is_hidden(vpnCard) === _shouldBeHidden &&
|
||||
subscribedStateCorrect
|
||||
);
|
||||
});
|
||||
|
||||
const visibilityState = _shouldBeHidden ? "hidden" : "shown";
|
||||
ok(true, `VPN card is ${visibilityState}.`);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
add_task(async function testVPNPromoBanner() {
|
||||
if (Services.sysinfo.getProperty("name") != "Windows_NT") {
|
||||
ok(true, "User is on an unsupported platform, the vpn card will not show");
|
||||
return;
|
||||
}
|
||||
AboutProtectionsParent.setTestOverride(getVPNOverrides(false, "us"));
|
||||
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab({
|
||||
url: "about:protections",
|
||||
gBrowser,
|
||||
});
|
||||
|
||||
info("Enable showing the VPN card and banner");
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.contentblocking.report.vpn.enabled", true],
|
||||
["browser.contentblocking.report.vpn_regions", "us,ca,nz,sg,my,gb"],
|
||||
["browser.contentblocking.report.vpn_platforms", "win"],
|
||||
["browser.contentblocking.report.hide_vpn_banner", false],
|
||||
],
|
||||
});
|
||||
|
||||
info("Check that vpn banner is hidden if user's language is not en*");
|
||||
Services.locale.availableLocales = ["de"];
|
||||
Services.locale.requestedLocales = ["de"];
|
||||
await reloadTab(tab);
|
||||
await checkVPNPromoBannerVisibility(tab, true);
|
||||
|
||||
// VPN Banner flips this pref each time it shows, flip back between each instruction.
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.contentblocking.report.hide_vpn_banner", false]],
|
||||
});
|
||||
|
||||
info("Check that vpn banner is shown if user's language is en*");
|
||||
// Set language back to en-US
|
||||
Services.locale.availableLocales = ["en-US"];
|
||||
Services.locale.requestedLocales = ["en-US"];
|
||||
await reloadTab(tab);
|
||||
await checkVPNPromoBannerVisibility(tab, false);
|
||||
|
||||
is(
|
||||
Services.prefs.getBoolPref(
|
||||
"browser.contentblocking.report.hide_vpn_banner",
|
||||
false
|
||||
),
|
||||
true,
|
||||
"After showing the banner once, the pref to hide the VPN banner is flipped"
|
||||
);
|
||||
info("The banner does not show when the pref to hide it is flipped");
|
||||
await reloadTab(tab);
|
||||
await checkVPNPromoBannerVisibility(tab, true);
|
||||
|
||||
// VPN Banner flips this pref each time it shows, flip back between each instruction.
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.contentblocking.report.hide_vpn_banner", false]],
|
||||
});
|
||||
|
||||
info(
|
||||
"Check that VPN banner is hidden if user's location is not on the regions list."
|
||||
);
|
||||
AboutProtectionsParent.setTestOverride(getVPNOverrides(false, "ls"));
|
||||
await reloadTab(tab);
|
||||
await checkVPNPromoBannerVisibility(tab, true);
|
||||
|
||||
info(
|
||||
"VPN banner should be hidden when vpn not enabled, though all other conditions are true"
|
||||
);
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.contentblocking.report.vpn.enabled", false],
|
||||
["browser.contentblocking.report.hide_vpn_banner", false],
|
||||
],
|
||||
});
|
||||
await reloadTab(tab);
|
||||
await checkVPNPromoBannerVisibility(tab, true);
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.contentblocking.report.vpn.enabled", true],
|
||||
["browser.contentblocking.report.hide_vpn_banner", false],
|
||||
],
|
||||
});
|
||||
|
||||
info("If user is subscribed to VPN already the promo banner should not show");
|
||||
AboutProtectionsParent.setTestOverride(getVPNOverrides(true, "us"));
|
||||
|
||||
await reloadTab(tab);
|
||||
await checkVPNPromoBannerVisibility(tab, true);
|
||||
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
async function checkVPNPromoBannerVisibility(tab, shouldBeHidden) {
|
||||
await SpecialPowers.spawn(
|
||||
tab.linkedBrowser,
|
||||
[{ _shouldBeHidden: shouldBeHidden }],
|
||||
async function({ _shouldBeHidden }) {
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
const vpnBanner = content.document.querySelector(".vpn-banner");
|
||||
return ContentTaskUtils.is_hidden(vpnBanner) === _shouldBeHidden;
|
||||
});
|
||||
|
||||
const visibilityState = _shouldBeHidden ? "hidden" : "shown";
|
||||
ok(true, `VPN banner is ${visibilityState}.`);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Expect the vpn card and banner to not show as we are expressly excluding China. Even when cn is in the supported region pref.
|
||||
add_task(async function testVPNDoesNotShowChina() {
|
||||
if (Services.sysinfo.getProperty("name") != "Windows_NT") {
|
||||
ok(true, "User is on an unsupported platform, the vpn card will not show");
|
||||
return;
|
||||
}
|
||||
AboutProtectionsParent.setTestOverride(getVPNOverrides(false, "us"));
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab({
|
||||
url: "about:protections",
|
||||
gBrowser,
|
||||
});
|
||||
|
||||
info("Enable showing the VPN card");
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.contentblocking.report.vpn.enabled", true],
|
||||
["browser.contentblocking.report.vpn_regions", "us,ca,nz,sg,my,gb,cn"],
|
||||
["browser.contentblocking.report.vpn_platforms", "win,mac"],
|
||||
["browser.contentblocking.report.hide_vpn_banner", false],
|
||||
],
|
||||
});
|
||||
|
||||
info("Check that vpn banner and card are able to show when conditions allow");
|
||||
Region._setHomeRegion("US", false);
|
||||
Services.locale.availableLocales = ["en-US"];
|
||||
Services.locale.requestedLocales = ["en-US"];
|
||||
await reloadTab(tab);
|
||||
await checkVPNCardVisibility(tab, false);
|
||||
await checkVPNPromoBannerVisibility(tab, false);
|
||||
|
||||
// VPN Banner flips this pref each time it shows, flip back between each instruction.
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.contentblocking.report.hide_vpn_banner", false]],
|
||||
});
|
||||
|
||||
info(
|
||||
"set home location to China, even though user is currently in the US, expect vpn card to be hidden"
|
||||
);
|
||||
Region._setHomeRegion("CN", false);
|
||||
await reloadTab(tab);
|
||||
await checkVPNCardVisibility(tab, true);
|
||||
await checkVPNPromoBannerVisibility(tab, true);
|
||||
|
||||
// VPN Banner flips this pref each time it shows, flip back between each instruction.
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.contentblocking.report.hide_vpn_banner", false]],
|
||||
});
|
||||
|
||||
info("home region is US, but current location is China");
|
||||
Region._setHomeRegion("US", false);
|
||||
AboutProtectionsParent.setTestOverride(getVPNOverrides(false, "cn"));
|
||||
await reloadTab(tab);
|
||||
await checkVPNCardVisibility(tab, true);
|
||||
await checkVPNPromoBannerVisibility(tab, true);
|
||||
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
@ -77,3 +77,15 @@ const mockGetMonitorData = data => {
|
||||
registerCleanupFunction(function head_cleanup() {
|
||||
Services.logins.removeAllLogins();
|
||||
});
|
||||
|
||||
// Used to replace AboutProtectionsParent.VPNSubStatus and Region.current
|
||||
const getVPNOverrides = (hasSubscription = false, location = "us") => {
|
||||
return {
|
||||
vpnOverrides: () => {
|
||||
return {
|
||||
hasSubscription,
|
||||
location,
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -1835,14 +1835,16 @@ security.ui.protections:
|
||||
show:
|
||||
objects: [
|
||||
"protection_report",
|
||||
"vpn_banner",
|
||||
]
|
||||
bug_numbers:
|
||||
- 1557050
|
||||
- 1610897
|
||||
- 1643428
|
||||
- 1650468
|
||||
- 1661756
|
||||
description: >
|
||||
User arrived on the protection report. This also includes a 'value' attribute which defaults to 'direct' or will be the value that a referring website addds to the url.
|
||||
User arrived on the protection report. This also includes a 'value' attribute which defaults to 'direct' or will be the value that a referring website addds to the url. This also indicates if the vpn banner has been seen.
|
||||
expiry_version: "86"
|
||||
record_in_processes: ["content"]
|
||||
release_channel_collection: opt-out
|
||||
@ -1885,6 +1887,7 @@ security.ui.protections:
|
||||
- 1612091
|
||||
- 1637615
|
||||
- 1643428
|
||||
- 1661756
|
||||
description: >
|
||||
User interaction by click events on the protection report.
|
||||
objects: [
|
||||
@ -1897,6 +1900,11 @@ security.ui.protections:
|
||||
"trackers_about_link",
|
||||
"mobile_app_link",
|
||||
"settings_link",
|
||||
"vpn_banner_link",
|
||||
"vpn_banner_close",
|
||||
"vpn_card_link",
|
||||
"vpn_app_link_android",
|
||||
"vpn_app_link_ios",
|
||||
]
|
||||
expiry_version: "86"
|
||||
record_in_processes: ["content"]
|
||||
|
@ -18,6 +18,10 @@ const kAllowedPrefs = new Set([
|
||||
"testing.allowed-prefs.some-char-pref",
|
||||
"testing.allowed-prefs.some-int-pref",
|
||||
|
||||
"browser.contentblocking.report.hide_lockwise_app",
|
||||
"browser.contentblocking.report.hide_vpn_banner",
|
||||
"browser.contentblocking.report.show_mobile_app",
|
||||
|
||||
"narrate.rate",
|
||||
"narrate.voice",
|
||||
|
||||
@ -58,8 +62,6 @@ const kAllowedPrefs = new Set([
|
||||
"security.ssl.errorReporting.automatic",
|
||||
"security.tls.version.enable-deprecated",
|
||||
"security.xfocsp.errorReporting.automatic",
|
||||
"browser.contentblocking.report.hide_lockwise_app",
|
||||
"browser.contentblocking.report.show_mobile_app",
|
||||
]);
|
||||
|
||||
const kPrefTypeMap = new Map([
|
||||
|
@ -143,12 +143,15 @@ let RemotePageAccessManager = {
|
||||
"FetchMobileDeviceConnected",
|
||||
"GetShowProxyCard",
|
||||
"FetchEntryPoint",
|
||||
"FetchVPNSubStatus",
|
||||
"FetchShowVPNCard",
|
||||
],
|
||||
RPMAddMessageListener: ["*"],
|
||||
RPMRemoveMessageListener: ["*"],
|
||||
RPMSetBoolPref: [
|
||||
"browser.contentblocking.report.hide_lockwise_app",
|
||||
"browser.contentblocking.report.show_mobile_app",
|
||||
"browser.contentblocking.report.hide_vpn_banner",
|
||||
],
|
||||
RPMGetBoolPref: [
|
||||
"browser.contentblocking.report.lockwise.enabled",
|
||||
@ -161,6 +164,8 @@ let RemotePageAccessManager = {
|
||||
"privacy.trackingprotection.socialtracking.enabled",
|
||||
"browser.contentblocking.report.hide_lockwise_app",
|
||||
"browser.contentblocking.report.show_mobile_app",
|
||||
"browser.contentblocking.report.hide_vpn_banner",
|
||||
"browser.contentblocking.report.vpn.enabled",
|
||||
],
|
||||
RPMGetStringPref: [
|
||||
"browser.contentblocking.category",
|
||||
@ -172,6 +177,11 @@ let RemotePageAccessManager = {
|
||||
"browser.contentblocking.report.lockwise.mobile-ios.url",
|
||||
"browser.contentblocking.report.mobile-ios.url",
|
||||
"browser.contentblocking.report.mobile-android.url",
|
||||
"browser.contentblocking.report.vpn.url",
|
||||
"browser.contentblocking.report.vpn-promo.url",
|
||||
"browser.contentblocking.report.vpn-android.url",
|
||||
"browser.contentblocking.report.vpn-ios.url",
|
||||
"browser.contentblocking.report.vpn_platforms",
|
||||
],
|
||||
RPMGetIntPref: ["network.cookie.cookieBehavior"],
|
||||
RPMGetFormatURLPref: [
|
||||
|
Loading…
Reference in New Issue
Block a user