/* 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 https://mozilla.org/MPL/2.0/. */
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
import { html, ifDefined } from "chrome://global/content/vendor/lit.all.mjs";
import {
BANDWIDTH,
LINKS,
} from "chrome://browser/content/ipprotection/ipprotection-constants.mjs";
const { ERRORS } = ChromeUtils.importESModule(
"moz-src:///toolkit/components/ipprotection/IPPProxyManager.sys.mjs"
);
// eslint-disable-next-line import/no-unassigned-import
import "chrome://browser/content/ipprotection/ipprotection-message-bar.mjs";
// eslint-disable-next-line import/no-unassigned-import
import "chrome://browser/content/ipprotection/ipprotection-unauthenticated.mjs";
// eslint-disable-next-line import/no-unassigned-import
import "chrome://browser/content/ipprotection/ipprotection-status-card.mjs";
// eslint-disable-next-line import/no-unassigned-import
import "chrome://browser/content/ipprotection/ipprotection-status-box.mjs";
// eslint-disable-next-line import/no-unassigned-import
import "chrome://global/content/elements/moz-toggle.mjs";
/**
* Custom element that implements a message bar and status card for IP protection.
*/
export default class IPProtectionContentElement extends MozLitElement {
static queries = {
unauthenticatedEl: "ipprotection-unauthenticated",
messagebarEl: "ipprotection-message-bar",
statusCardEl: "ipprotection-status-card",
upgradeEl: "#upgrade-vpn-content",
activeSubscriptionEl: "#active-subscription-vpn-content",
supportLinkEl: "#vpn-support-link",
statusBoxEl: "ipprotection-status-box",
siteExclusionControlEl: "#site-exclusion-control",
siteExclusionToggleEl: "#site-exclusion-toggle",
settingsButtonEl: "#vpn-settings-button",
};
static properties = {
state: { type: Object, attribute: false },
_showMessageBar: { type: Boolean, state: true },
_messageDismissed: { type: Boolean, state: true },
};
constructor() {
super();
this.state = {};
this.messageBarListener = this.#messageBarListener.bind(this);
this.statusCardListener = this.#statusCardListener.bind(this);
this._showMessageBar = false;
this._messageDismissed = false;
}
connectedCallback() {
super.connectedCallback();
this.dispatchEvent(new CustomEvent("IPProtection:Init", { bubbles: true }));
this.addEventListener(
"ipprotection-status-card:user-toggled-on",
this.#statusCardListener
);
this.addEventListener(
"ipprotection-status-card:user-toggled-off",
this.#statusCardListener
);
this.addEventListener(
"ipprotection-message-bar:user-dismissed",
this.#messageBarListener
);
}
disconnectedCallback() {
super.disconnectedCallback();
this.removeEventListener(
"ipprotection-status-card:user-toggled-on",
this.#statusCardListener
);
this.removeEventListener(
"ipprotection-status-card:user-toggled-off",
this.#statusCardListener
);
this.removeEventListener(
"ipprotection-message-bar:user-dismissed",
this.#messageBarListener
);
}
get canEnableConnection() {
return this.state && this.state.isProtectionEnabled && !this.state.error;
}
get hasSiteExclusion() {
return this.state?.siteData?.isExclusion ?? false;
}
get #hasErrors() {
return !this.state || !!this.state.error;
}
handleClickSupportLink(event) {
const win = event.target.ownerGlobal;
if (event.target === this.supportLinkEl) {
event.preventDefault();
win.openWebLinkIn(LINKS.PRODUCT_URL, "tab");
this.dispatchEvent(
new CustomEvent("IPProtection:Close", { bubbles: true })
);
}
}
handleUpgrade(event) {
const win = event.target.ownerGlobal;
win.openWebLinkIn(LINKS.PRODUCT_URL + "#pricing", "tab");
// Close the panel
this.dispatchEvent(
new CustomEvent("IPProtection:ClickUpgrade", { bubbles: true })
);
Glean.ipprotection.clickUpgradeButton.record();
}
focus() {
if (this.state.unauthenticated) {
this.unauthenticatedEl?.focus();
} else {
this.statusCardEl?.focus();
}
}
#statusCardListener(event) {
if (event.type === "ipprotection-status-card:user-toggled-on") {
this.dispatchEvent(
new CustomEvent("IPProtection:UserEnable", { bubbles: true })
);
} else if (event.type === "ipprotection-status-card:user-toggled-off") {
this.dispatchEvent(
new CustomEvent("IPProtection:UserDisable", { bubbles: true })
);
}
}
#messageBarListener(event) {
if (event.type === "ipprotection-message-bar:user-dismissed") {
this._showMessageBar = false;
this._messageDismissed = true;
this.state.error = "";
if (this.state.bandwidthWarning) {
const threshold = Services.prefs.getIntPref(
"browser.ipProtection.bandwidthThreshold",
0
);
this.dispatchEvent(
new CustomEvent("IPProtection:DismissBandwidthWarning", {
bubbles: true,
composed: true,
detail: { threshold },
})
);
}
}
}
handleToggleUseVPN(event) {
let isEnabled = event.target.pressed;
if (isEnabled) {
this.dispatchEvent(
new CustomEvent("IPProtection:UserEnableVPNForSite", {
bubbles: true,
})
);
} else {
this.dispatchEvent(
new CustomEvent("IPProtection:UserDisableVPNForSite", {
bubbles: true,
composed: true,
})
);
}
}
handleClickSettingsButton(event) {
event.preventDefault();
const win = event.target.ownerGlobal;
win.openPreferences("privacy-vpn");
this.dispatchEvent(
new CustomEvent("IPProtection:Close", { bubbles: true, composed: true })
);
}
updated(changedProperties) {
super.updated(changedProperties);
// Clear messages when there is an error.
if (this.state.error) {
this._messageDismissed = false;
}
}
messageBarTemplate() {
let messageId;
let messageLink;
let messageLinkl10nId;
let messageLinkL10nArgs;
let messageType = "info";
if (this.state.bandwidthWarning && this.state.bandwidthUsage) {
messageId = "ipprotection-message-bandwidth-warning";
messageType = "warning";
const bandwidthRemaining =
this.state.bandwidthUsage.remaining / BANDWIDTH.BYTES_IN_GB;
const maxUsage = this.state.bandwidthUsage.max / BANDWIDTH.BYTES_IN_GB;
const pctUsed = (100 * (maxUsage - bandwidthRemaining)) / maxUsage;
let usageLeft = Math.round(bandwidthRemaining);
if (pctUsed >= 75 && pctUsed < 90) {
usageLeft = bandwidthRemaining.toFixed(1);
} else if (bandwidthRemaining < 1) {
messageId = "ipprotection-message-bandwidth-warning-mb";
usageLeft = Math.floor(
this.state.bandwidthUsage.remaining / BANDWIDTH.BYTES_IN_MB
);
}
messageLinkL10nArgs = JSON.stringify({
usageLeft,
maxUsage,
});
} else if (this.state.onboardingMessage) {
messageId = this.state.onboardingMessage;
messageType = "info";
switch (this.state.onboardingMessage) {
case "ipprotection-message-continuous-onboarding-intro":
break;
case "ipprotection-message-continuous-onboarding-autostart":
messageLink = "about:settings#privacy";
messageLinkl10nId = "setting-link";
break;
case "ipprotection-message-continuous-onboarding-site-settings":
messageLink = "about:settings#privacy";
messageLinkl10nId = "setting-link";
break;
}
}
return html`