From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../protections/content/monitor-card.mjs | 457 +++++++++++++++++++++ 1 file changed, 457 insertions(+) create mode 100644 browser/components/protections/content/monitor-card.mjs (limited to 'browser/components/protections/content/monitor-card.mjs') diff --git a/browser/components/protections/content/monitor-card.mjs b/browser/components/protections/content/monitor-card.mjs new file mode 100644 index 0000000000..5fd76dfd8b --- /dev/null +++ b/browser/components/protections/content/monitor-card.mjs @@ -0,0 +1,457 @@ +/* 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(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("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) + ); + } +} -- cgit v1.2.3