/* 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/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(document) { this.doc = document; } init() { // Wait for monitor data and display the card. this.getMonitorData(); let monitorAboutLink = this.doc.getElementById("monitor-link"); monitorAboutLink.addEventListener("click", () => { this.doc.sendTelemetryEvent("click", "mtr_about_link"); }); 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( "click", "mtr_report_link", "resolve_breaches" ); break; case "monitor-breaches-link": if (evt.currentTarget.classList.contains("no-breaches-resolved")) { this.doc.sendTelemetryEvent( "click", "mtr_report_link", "manage_breaches" ); } else { this.doc.sendTelemetryEvent( "click", "mtr_report_link", "view_report" ); } break; case "monitor-stored-emails-link": this.doc.sendTelemetryEvent( "click", "mtr_report_link", "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( "click", "mtr_report_link", "known_resolved_breaches" ); } else if ( knownBreaches.classList.contains("known-unresolved-breaches") ) { this.doc.sendTelemetryEvent( "click", "mtr_report_link", "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( "click", "mtr_report_link", "exposed_passwords_all_breaches" ); } else if ( exposedPasswords.classList.contains( "passwords-exposed-unresolved-breaches" ) ) { this.doc.sendTelemetryEvent( "click", "mtr_report_link", "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"); headerContent.setAttribute( "data-l10n-id", "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); signUpForMonitorLink.setAttribute("data-l10n-id", "monitor-sign-up-link"); headerContent.setAttribute( "data-l10n-id", "monitor-header-content-no-account" ); signUpForMonitorLink.addEventListener("click", () => { this.doc.sendTelemetryEvent("click", "mtr_signup_button"); }); } } /** * 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. * * @return 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" ); breachesTitle.setAttribute( "data-l10n-id", "monitor-breaches-unresolved-title" ); breachesDesc.setAttribute( "data-l10n-id", "monitor-breaches-unresolved-description" ); breachesLink.setAttribute( "data-l10n-id", "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" ); breachesTitle.setAttribute( "data-l10n-id", "monitor-breaches-resolved-title" ); breachesDesc.setAttribute( "data-l10n-id", "monitor-breaches-resolved-description" ); breachesLink.setAttribute("data-l10n-id", "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" ); partialBreachesTitle.setAttribute( "data-l10n-args", JSON.stringify({ numBreaches, numBreachesResolved, }) ); partialBreachesTitle.setAttribute( "data-l10n-id", "monitor-partial-breaches-title" ); 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: partialBreachesMotivationTitle.setAttribute( "data-l10n-id", "monitor-partial-breaches-motivation-title-start" ); break; case percentageResolved >= 25 && percentageResolved < 75: partialBreachesMotivationTitle.setAttribute( "data-l10n-id", "monitor-partial-breaches-motivation-title-middle" ); break; case percentageResolved >= 75 && percentageResolved < 100: partialBreachesMotivationTitle.setAttribute( "data-l10n-id", "monitor-partial-breaches-motivation-title-end" ); break; } const partialBreachesPercentage = document.getElementById( "monitor-partial-breaches-percentage" ); partialBreachesPercentage.setAttribute( "data-l10n-args", JSON.stringify({ percentageResolved, }) ); partialBreachesPercentage.setAttribute( "data-l10n-id", "monitor-partial-breaches-percentage" ); 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" ); breachesTitle.setAttribute("data-l10n-id", "monitor-no-breaches-title"); breachesDesc.setAttribute( "data-l10n-id", "monitor-no-breaches-description" ); breachesLink.setAttribute("data-l10n-id", "monitor-view-report-link"); } breachesLink.setAttribute("href", MONITOR_HOME_PAGE_URL); breachesLink.addEventListener( "click", this.onClickMonitorButton.bind(this) ); } }