Source code
Revision control
Copy as Markdown
Other Tools
/* 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
/* eslint-env mozilla/remote-page */
const MONITOR_URL = RPMGetStringPref(
"browser.contentblocking.report.monitor.url",
""
);
const MONITOR_SIGN_IN_URL = RPMGetStringPref(
"browser.contentblocking.report.monitor.sign_in_url",
""
);
const HOW_IT_WORKS_URL_PREF = RPMGetFormatURLPref(
"browser.contentblocking.report.monitor.how_it_works.url"
);
const MONITOR_PREFERENCES_URL = RPMGetFormatURLPref(
"browser.contentblocking.report.monitor.preferences_url"
);
const MONITOR_HOME_PAGE_URL = RPMGetFormatURLPref(
"browser.contentblocking.report.monitor.home_page_url"
);
export default class MonitorClass {
constructor(doc) {
this.doc = doc;
}
init() {
// Wait for monitor data and display the card.
this.getMonitorData();
let monitorAboutLink = this.doc.getElementById("monitor-link");
monitorAboutLink.addEventListener("click", () => {
this.doc.sendTelemetryEvent("clickMtrAboutLink");
});
const storedEmailLink = this.doc.getElementById(
"monitor-stored-emails-link"
);
storedEmailLink.href = MONITOR_PREFERENCES_URL;
storedEmailLink.addEventListener(
"click",
this.onClickMonitorButton.bind(this)
);
const knownBreachesLink = this.doc.getElementById(
"monitor-known-breaches-link"
);
knownBreachesLink.href = MONITOR_HOME_PAGE_URL;
knownBreachesLink.addEventListener(
"click",
this.onClickMonitorButton.bind(this)
);
const exposedPasswordsLink = this.doc.getElementById(
"monitor-exposed-passwords-link"
);
exposedPasswordsLink.href = MONITOR_HOME_PAGE_URL;
exposedPasswordsLink.addEventListener(
"click",
this.onClickMonitorButton.bind(this)
);
}
onClickMonitorButton(evt) {
RPMSendAsyncMessage("ClearMonitorCache");
switch (evt.currentTarget.id) {
case "monitor-partial-breaches-link":
this.doc.sendTelemetryEvent("clickMtrReportLink", "resolve_breaches");
break;
case "monitor-breaches-link":
if (evt.currentTarget.classList.contains("no-breaches-resolved")) {
this.doc.sendTelemetryEvent("clickMtrReportLink", "manage_breaches");
} else {
this.doc.sendTelemetryEvent("clickMtrReportLink", "view_report");
}
break;
case "monitor-stored-emails-link":
this.doc.sendTelemetryEvent("clickMtrReportLink", "stored_emails");
break;
case "monitor-known-breaches-link":
const knownBreaches = this.doc.querySelector(
"span[data-type='known-breaches']"
);
if (knownBreaches.classList.contains("known-resolved-breaches")) {
this.doc.sendTelemetryEvent(
"clickMtrReportLink",
"known_resolved_breaches"
);
} else if (
knownBreaches.classList.contains("known-unresolved-breaches")
) {
this.doc.sendTelemetryEvent(
"clickMtrReportLink",
"known_unresolved_breaches"
);
}
break;
case "monitor-exposed-passwords-link":
const exposedPasswords = this.doc.querySelector(
"span[data-type='exposed-passwords']"
);
if (
exposedPasswords.classList.contains("passwords-exposed-all-breaches")
) {
this.doc.sendTelemetryEvent(
"clickMtrReportLink",
"exposed_passwords_all_breaches"
);
} else if (
exposedPasswords.classList.contains(
"passwords-exposed-unresolved-breaches"
)
) {
this.doc.sendTelemetryEvent(
"clickMtrReportLink",
"exposed_passwords_unresolved_breaches"
);
}
break;
}
}
/**
* Retrieves the monitor data and displays this data in the card.
*/
getMonitorData() {
RPMSendQuery("FetchMonitorData", {}).then(monitorData => {
// Once data for the user is retrieved, display the monitor card.
this.buildContent(monitorData);
// Show the Monitor card.
const monitorUI = this.doc.querySelector(".card.monitor-card.loading");
monitorUI.classList.remove("loading");
});
}
buildContent(monitorData) {
const headerContent = this.doc.querySelector(
"#monitor-header-content span"
);
const monitorCard = this.doc.querySelector(".card.monitor-card");
if (!monitorData.error) {
monitorCard.classList.add("has-logins");
this.doc.l10n.setAttributes(
headerContent,
"monitor-header-content-signed-in"
);
this.renderContentForUserWithAccount(monitorData);
} else {
monitorCard.classList.add("no-logins");
const signUpForMonitorLink = this.doc.getElementById(
"sign-up-for-monitor-link"
);
signUpForMonitorLink.href = this.buildMonitorUrl(monitorData.userEmail);
this.doc.l10n.setAttributes(signUpForMonitorLink, "monitor-sign-up-link");
this.doc.l10n.setAttributes(
headerContent,
"monitor-header-content-no-account"
);
signUpForMonitorLink.addEventListener("click", () => {
this.doc.sendTelemetryEvent("clickMtrSignupButton");
});
}
}
/**
* Builds the appropriate URL that takes the user to the Monitor website's
* sign-up/sign-in page.
*
* @param {string | null} email
* Optional. The email used to direct the user to the Monitor website's OAuth
* sign-in flow. If null, then direct user to just the Monitor website.
*
* @returns {string} URL to Monitor website.
*/
buildMonitorUrl(email = null) {
return email
? `${MONITOR_SIGN_IN_URL}${encodeURIComponent(email)}`
: MONITOR_URL;
}
renderContentForUserWithAccount(monitorData) {
const {
numBreaches,
numBreachesResolved,
passwords,
passwordsResolved,
monitoredEmails,
} = monitorData;
const monitorCardBody = this.doc.querySelector(
".card.monitor-card .card-body"
);
monitorCardBody.classList.remove("hidden");
const howItWorksLink = this.doc.getElementById("monitor-link");
howItWorksLink.href = HOW_IT_WORKS_URL_PREF;
const storedEmail = this.doc.querySelector(
"span[data-type='stored-emails']"
);
storedEmail.textContent = monitoredEmails;
const infoMonitoredAddresses = this.doc.getElementById(
"info-monitored-addresses"
);
this.doc.l10n.setAttributes(
infoMonitoredAddresses,
"info-monitored-emails",
{ count: monitoredEmails }
);
const knownBreaches = this.doc.querySelector(
"span[data-type='known-breaches']"
);
const exposedPasswords = this.doc.querySelector(
"span[data-type='exposed-passwords']"
);
const infoKnownBreaches = this.doc.getElementById("info-known-breaches");
const infoExposedPasswords = this.doc.getElementById(
"info-exposed-passwords"
);
const breachesWrapper = this.doc.querySelector(".monitor-breaches-wrapper");
const partialBreachesWrapper = this.doc.querySelector(
".monitor-partial-breaches-wrapper"
);
const breachesTitle = this.doc.getElementById("monitor-breaches-title");
const breachesIcon = this.doc.getElementById("monitor-breaches-icon");
const breachesDesc = this.doc.getElementById(
"monitor-breaches-description"
);
const breachesLink = this.doc.getElementById("monitor-breaches-link");
if (numBreaches) {
if (!numBreachesResolved) {
partialBreachesWrapper.classList.add("hidden");
knownBreaches.textContent = numBreaches;
knownBreaches.classList.add("known-unresolved-breaches");
knownBreaches.classList.remove("known-resolved-breaches");
this.doc.l10n.setAttributes(
infoKnownBreaches,
"info-known-breaches-found",
{ count: numBreaches }
);
exposedPasswords.textContent = passwords;
exposedPasswords.classList.add("passwords-exposed-all-breaches");
exposedPasswords.classList.remove(
"passwords-exposed-unresolved-breaches"
);
this.doc.l10n.setAttributes(
infoExposedPasswords,
"info-exposed-passwords-found",
{ count: passwords }
);
breachesIcon.setAttribute(
"src",
"chrome://browser/skin/protections/new-feature.svg"
);
this.doc.l10n.setAttributes(
breachesTitle,
"monitor-breaches-unresolved-title"
);
this.doc.l10n.setAttributes(
breachesDesc,
"monitor-breaches-unresolved-description"
);
this.doc.l10n.setAttributes(
breachesLink,
"monitor-manage-breaches-link"
);
breachesLink.classList.add("no-breaches-resolved");
} else if (numBreaches == numBreachesResolved) {
partialBreachesWrapper.classList.add("hidden");
knownBreaches.textContent = numBreachesResolved;
knownBreaches.classList.remove("known-unresolved-breaches");
knownBreaches.classList.add("known-resolved-breaches");
this.doc.l10n.setAttributes(
infoKnownBreaches,
"info-known-breaches-resolved",
{ count: numBreachesResolved }
);
let unresolvedPasswords = passwords - passwordsResolved;
exposedPasswords.textContent = unresolvedPasswords;
exposedPasswords.classList.remove("passwords-exposed-all-breaches");
exposedPasswords.classList.add("passwords-exposed-unresolved-breaches");
this.doc.l10n.setAttributes(
infoExposedPasswords,
"info-exposed-passwords-resolved",
{ count: unresolvedPasswords }
);
breachesIcon.setAttribute(
"src",
"chrome://browser/skin/protections/resolved-breach.svg"
);
this.doc.l10n.setAttributes(
breachesTitle,
"monitor-breaches-resolved-title"
);
this.doc.l10n.setAttributes(
breachesDesc,
"monitor-breaches-resolved-description"
);
this.doc.l10n.setAttributes(breachesLink, "monitor-view-report-link");
} else {
breachesWrapper.classList.add("hidden");
knownBreaches.textContent = numBreachesResolved;
knownBreaches.classList.remove("known-unresolved-breaches");
knownBreaches.classList.add("known-resolved-breaches");
this.doc.l10n.setAttributes(
infoKnownBreaches,
"info-known-breaches-resolved",
{ count: numBreachesResolved }
);
let unresolvedPasswords = passwords - passwordsResolved;
exposedPasswords.textContent = unresolvedPasswords;
exposedPasswords.classList.remove("passwords-exposed-all-breaches");
exposedPasswords.classList.add("passwords-exposed-unresolved-breaches");
this.doc.l10n.setAttributes(
infoExposedPasswords,
"info-exposed-passwords-resolved",
{ count: unresolvedPasswords }
);
const partialBreachesTitle = document.getElementById(
"monitor-partial-breaches-title"
);
this.doc.l10n.setAttributes(
partialBreachesTitle,
"monitor-partial-breaches-title",
{
numBreaches,
numBreachesResolved,
}
);
const progressBar = this.doc.querySelector(".progress-bar");
const partialBreachesMotivationTitle = document.getElementById(
"monitor-partial-breaches-motivation-title"
);
let percentageResolved = Math.floor(
(numBreachesResolved / numBreaches) * 100
);
progressBar.setAttribute("value", 100 - percentageResolved);
switch (true) {
case percentageResolved > 0 && percentageResolved < 25:
this.doc.l10n.setAttributes(
partialBreachesMotivationTitle,
"monitor-partial-breaches-motivation-title-start"
);
break;
case percentageResolved >= 25 && percentageResolved < 75:
this.doc.l10n.setAttributes(
partialBreachesMotivationTitle,
"monitor-partial-breaches-motivation-title-middle"
);
break;
case percentageResolved >= 75 && percentageResolved < 100:
this.doc.l10n.setAttributes(
partialBreachesMotivationTitle,
"monitor-partial-breaches-motivation-title-end"
);
break;
}
const partialBreachesPercentage = document.getElementById(
"monitor-partial-breaches-percentage"
);
this.doc.l10n.setAttributes(
partialBreachesPercentage,
"monitor-partial-breaches-percentage",
{ percentageResolved }
);
const partialBreachesLink = document.getElementById(
"monitor-partial-breaches-link"
);
partialBreachesLink.setAttribute("href", MONITOR_HOME_PAGE_URL);
partialBreachesLink.addEventListener(
"click",
this.onClickMonitorButton.bind(this)
);
}
} else {
partialBreachesWrapper.classList.add("hidden");
knownBreaches.textContent = numBreaches;
knownBreaches.classList.add("known-unresolved-breaches");
knownBreaches.classList.remove("known-resolved-breaches");
this.doc.l10n.setAttributes(
infoKnownBreaches,
"info-known-breaches-found",
{ count: numBreaches }
);
exposedPasswords.textContent = passwords;
exposedPasswords.classList.add("passwords-exposed-all-breaches");
exposedPasswords.classList.remove(
"passwords-exposed-unresolved-breaches"
);
this.doc.l10n.setAttributes(
infoExposedPasswords,
"info-exposed-passwords-found",
{ count: passwords }
);
breachesIcon.setAttribute(
"src",
"chrome://browser/skin/protections/resolved-breach.svg"
);
this.doc.l10n.setAttributes(breachesTitle, "monitor-no-breaches-title");
this.doc.l10n.setAttributes(
breachesDesc,
"monitor-no-breaches-description"
);
this.doc.l10n.setAttributes(breachesLink, "monitor-view-report-link");
}
breachesLink.setAttribute("href", MONITOR_HOME_PAGE_URL);
breachesLink.addEventListener(
"click",
this.onClickMonitorButton.bind(this)
);
}
}