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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const lazy = {};
ChromeUtils.defineLazyGetter(lazy, "ipProtectionLocalization", () => {
return new Localization(["browser/ipProtection.ftl"], true);
});
ChromeUtils.defineESModuleGetters(lazy, {
EveryWindow: "resource:///modules/EveryWindow.sys.mjs",
IPPProxyManager:
"moz-src:///browser/components/ipprotection/IPPProxyManager.sys.mjs",
IPPProxyStates:
"moz-src:///browser/components/ipprotection/IPPProxyManager.sys.mjs",
});
/**
* Manages showing alerts for different VPN states
*/
class IPProtectionAlertManagerClass {
#localizationMessages = null;
#promptsOpen = false;
#initialized = false;
get initialized() {
return this.#initialized;
}
init() {
if (this.#initialized) {
return;
}
lazy.IPPProxyManager.addEventListener("IPPProxyManager:StateChanged", this);
this.#initialized = true;
}
uninit() {
if (!this.#initialized) {
return;
}
lazy.IPPProxyManager.removeEventListener(
"IPPProxyManager:StateChanged",
this
);
this.#closeAllPrompts();
this.#initialized = false;
}
get localizationMessages() {
if (!this.#localizationMessages) {
const [
pausedTitle,
pausedBody,
closeTabsButton,
continueButton,
errorTitle,
errorBody,
] = lazy.ipProtectionLocalization.formatMessagesSync([
{ id: "vpn-paused-alert-title" },
{ id: "vpn-paused-alert-body", args: { maxUsage: 150 } },
{ id: "vpn-paused-alert-close-tabs-button" },
{ id: "vpn-paused-alert-continue-wo-vpn-button" },
{ id: "vpn-error-alert-title" },
{ id: "vpn-error-alert-body" },
]);
this.#localizationMessages = {
pausedTitle: pausedTitle.value,
pausedBody: pausedBody.value,
closeTabsButton: closeTabsButton.value,
continueButton: continueButton.value,
errorTitle: errorTitle.value,
errorBody: errorBody.value,
};
}
return this.#localizationMessages;
}
handleEvent(event) {
if (event.type !== "IPPProxyManager:StateChanged") {
return;
}
switch (event.detail.state) {
case lazy.IPPProxyStates.ACTIVE:
case lazy.IPPProxyStates.NOT_READY:
case lazy.IPPProxyStates.READY: {
this.#closeAllPrompts();
break;
}
case lazy.IPPProxyStates.PAUSED: {
this.showPausedPrompts();
break;
}
case lazy.IPPProxyStates.ERROR: {
this.showErrorPrompts();
break;
}
}
}
/**
* A helper to create a blocking prompt in all existing windows.
*
* @param {string} title The title of the prompt
* @param {string} body The body text of the prompt
* @param {string} button0 The text of the primary button
* @param {string} button1 The text of the secondary button
* @returns {Array} An array of promies. A promise will resole once one of
* buttons in the prompt are clicked.
*/
#createAllPrompts(title, body, button0, button1) {
if (this.#promptsOpen) {
return [];
}
const promises = [];
for (let window of lazy.EveryWindow.readyWindows) {
promises.push(
Services.prompt.asyncConfirmEx(
window.browsingContext,
Services.prompt.MODAL_TYPE_INTERNAL_WINDOW,
title,
body,
Ci.nsIPromptService.BUTTON_POS_0_DEFAULT |
(Ci.nsIPromptService.BUTTON_TITLE_IS_STRING *
Ci.nsIPromptService.BUTTON_POS_0) |
(Ci.nsIPromptService.BUTTON_TITLE_IS_STRING *
Ci.nsIPromptService.BUTTON_POS_1),
button0,
button1,
null,
null,
false,
{ useTitle: true }
)
);
}
this.#promptsOpen = true;
return promises;
}
/**
* Close all of the open prompts
*/
#closeAllPrompts() {
if (!this.#promptsOpen) {
return;
}
for (let window of lazy.EveryWindow.readyWindows) {
window.gDialogBox.dialog?.close();
}
this.#promptsOpen = false;
}
/**
* Show the VPN paused alert. This notifies the user that they hit the max
* bandwidth.
*
* @returns {number} The button clicked. 0 meaning continue without vpn and
* 1 meaning close all tabs.
*/
async showPausedPrompts() {
let { pausedTitle, pausedBody, continueButton, closeTabsButton } =
this.localizationMessages;
const promises = this.#createAllPrompts(
pausedTitle,
pausedBody,
continueButton,
closeTabsButton
);
if (promises.length === 0) {
return;
}
let result = await Promise.any(promises);
let buttonClicked = result.getProperty("buttonNumClicked");
this.#handlePromptAction(buttonClicked);
}
/**
* Show the VPN error alert. This notifies the user that the VPN isn't
* working.
*
* @returns {number} The button clicked. 0 meaning continue without vpn and
* 1 meaning close all tabs.
*/
async showErrorPrompts() {
let { errorTitle, errorBody, continueButton, closeTabsButton } =
this.localizationMessages;
const promises = this.#createAllPrompts(
errorTitle,
errorBody,
continueButton,
closeTabsButton
);
if (promises.length === 0) {
return;
}
let result = await Promise.any(promises);
let buttonClicked = result.getProperty("buttonNumClicked");
this.#handlePromptAction(buttonClicked);
}
/**
* Handles the action taken by the user from the prompt.
*
* @param {number} buttonClicked Either 0 or 1.
* 0 means continue without vpn
* 1 means close all tabs
*/
#handlePromptAction(buttonClicked) {
this.#closeAllPrompts();
if (buttonClicked === 0) {
lazy.IPPProxyManager.stop(true);
} else if (buttonClicked === 1) {
this.#closeAllTabs();
}
}
async #closeAllTabs() {
const mostRecentWindow = Services.wm.getMostRecentBrowserWindow();
const tabs = mostRecentWindow.gBrowser.tabs;
mostRecentWindow.openTrustedLinkIn("about:home", "tab");
mostRecentWindow.gBrowser.removeTabs(tabs);
for (let window of lazy.EveryWindow.readyWindows) {
if (window === mostRecentWindow) {
continue;
}
window.close();
}
// Stop the VPN after closing everything to prevent any requests from
// completing.
lazy.IPPProxyManager.stop(true);
}
}
const IPProtectionAlertManager = new IPProtectionAlertManagerClass();
export { IPProtectionAlertManager };