diff --git a/src/_locales b/src/_locales
deleted file mode 160000
index f3da295..0000000
--- a/src/_locales
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit f3da295d004b7d6314c5baa321d9a5418ec937d9
diff --git a/src/confirm-page.html b/src/confirm-page.html
deleted file mode 100644
index 259af1a..0000000
--- a/src/confirm-page.html
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/css/confirm-page.css b/src/css/confirm-page.css
deleted file mode 100644
index 3ff1e14..0000000
--- a/src/css/confirm-page.css
+++ /dev/null
@@ -1,91 +0,0 @@
-/* General Rules and Resets */
-.title {
- background-image: none;
-}
-
-main {
- background: url(/img/onboarding-4.png) no-repeat;
- background-position: 200px 0;
- background-size: 120px;
- margin-inline-start: -350px;
- padding-inline-start: 350px;
-}
-
-.container-name {
- font-weight: bold;
-}
-
-button .container-name,
-#current-container-name {
- font-weight: bold;
-}
-
-@media only screen and (max-width: 900px) {
- main {
- background: none;
- }
-
- /* for a mid sized window we have enough for this but not our image */
- .title {
- background-image: url('chrome://global/skin/icons/info.svg');
- }
-}
-
-html {
- box-sizing: border-box;
- font: message-box;
-}
-
-#redirect-url,
-#redirect-origin {
- font-weight: bold;
-
- /* max-inline-size is needed to force this text smaller than the layout at a mid-sized window */
- max-inline-size: 40rem;
- word-break: break-all;
-}
-
-#redirect-url {
- background: #efedf0; /* Grey 20 */
- border-radius: 2px;
- line-height: 1.5;
- padding-block-end: 0.5rem;
- padding-block-start: 0.5rem;
- padding-inline-end: 0.5rem;
- padding-inline-start: 0.5rem;
-}
-
-/* stylelint-disable media-feature-name-no-unknown */
-@media (prefers-color-scheme: dark) {
- #redirect-url {
- background: #38383d; /* Grey 70 */
- color: #eee; /* White 20 */
- }
-}
-/* stylelint-enable */
-
-#redirect-url img {
- block-size: 16px;
- inline-size: 16px;
- margin-inline-end: 6px;
- offset-block-start: 3px;
- position: relative;
-}
-
-dfn {
- font-style: normal;
-}
-
-#deny,
-#confirm {
- flex-grow: 1;
-}
-
-.button-container > button {
- min-inline-size: 240px;
-}
-
-.check-label {
- align-items: center;
- display: flex;
-}
diff --git a/src/css/content.css b/src/css/content.css
deleted file mode 100644
index 5681887..0000000
--- a/src/css/content.css
+++ /dev/null
@@ -1,27 +0,0 @@
-.container-notification {
- align-items: center;
- background: #efefef;
- color: #003f07;
- display: flex;
- font: 12px sans-serif;
- inline-size: 100vw;
- justify-content: start;
- offset-block-start: 0;
- offset-inline-start: 0;
- padding-block-end: 8px;
- padding-block-start: 8px;
- padding-inline-end: 8px;
- padding-inline-start: 8px;
- position: fixed;
- text-align: start;
- transform: translateY(-100%);
- transition: transform 0.3s cubic-bezier(0.07, 0.95, 0, 1) 0.3s;
- z-index: 999999999999;
-}
-
-.container-notification img {
- block-size: 16px;
- display: inline-block;
- inline-size: 16px;
- margin-inline-end: 3px;
-}
diff --git a/src/css/options.css b/src/css/options.css
deleted file mode 100644
index 5a6baf0..0000000
--- a/src/css/options.css
+++ /dev/null
@@ -1,122 +0,0 @@
-body {
- --grey10: #e7e7e7;
-
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
- background: #fff;
- color: rgb(74, 74, 79);
- font-size: 13px;
- overflow: hidden;
-}
-
-h3:first-of-type {
- margin-block-start: 2.5rem;
-}
-
-label {
- display: flex;
- align-items: center;
- font-size: 14px;
-}
-
-label > span {
- padding-inline-end: 4px;
-}
-
-.settings-group {
- margin-block-end: 16px;
-}
-
-form {
- display: flex;
- flex-direction: column;
- padding-block-end: 1rem;
-}
-
-.settings-group p {
- margin-inline-start: 24px;
- margin-block: 4px 8px;
-}
-
-input[type="checkbox"] {
- margin-inline: 0 8px;
- margin-block: 1px auto;
- inline-size: 16px;
- block-size: 16px;
-}
-
-button {
- margin-inline: 0 auto;
-}
-
-.keyboard-shortcut {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- max-inline-size: 70%;
- align-items: center;
-}
-
-.bold {
- font-weight: 600;
-}
-
-.moz-vpn-proxy-permissions {
- margin-block: 0 2rem;
- padding-block-end: 1rem;
- border-block-end: 1px solid var(--grey10);
- display: flex;
- flex-direction: column;
-}
-
-h3.moz-vpn-proxy-permissions-title {
- margin-block-start: 0;
- position: relative;
- display: flex;
- align-items: center;
-}
-
-.warning-icon {
- display: flex;
- align-items: center;
-}
-
-.warning-icon.show-warning::before {
- background-image: url("/img/warning.svg");
- background-size: 24px;
- background-repeat: no-repeat;
- background-position: center;
- content: "";
- display: block;
- block-size: 24px;
- inline-size: 24px;
- margin-inline-end: 0.5rem;
-}
-
-.moz-vpn-proxy-permissions-title::before,
-.moz-vpn-proxy-permissions-title::after {
- background-color: var(--grey10);
- content: "";
- height: 1px;
- flex: 1 1 0%;
-}
-
-h3.moz-vpn-proxy-permissions-title::before {
- margin-inline-end: 2rem;
- margin-inline-start: -50%;
-}
-
-h3.moz-vpn-proxy-permissions-title::after {
- margin-inline-start: 2rem;
- margin-inline-end: -50%;
-}
-
-@media (prefers-color-scheme: dark) {
- body {
- background: #23212a;
- color: #fff;
- }
-
- p {
- color: rgb(177, 177, 179);
- }
-}
diff --git a/src/css/popup.css b/src/css/popup.css
deleted file mode 100644
index 92fcb21..0000000
--- a/src/css/popup.css
+++ /dev/null
@@ -1,2279 +0,0 @@
-@font-face {
- font-family: "Metropolis";
- font-style: normal;
- font-weight: 800;
- src: url("/fonts/Metropolis-Medium.woff2") format("woff2");
-}
-
-@font-face {
- font-family: "Metropolis-Light";
- font-style: normal;
- font-weight: 300;
- src: url("/fonts/Metropolis-Light.woff2") format("woff2");
-}
-
-@font-face {
- font-family: "Inter";
- font-style: normal;
- font-weight: 400;
- src: url("/fonts/Inter-Regular.woff2") format("woff2");
-}
-
-@font-face {
- font-family: "Inter-Medium";
- font-style: normal;
- font-weight: 500;
- src: url("/fonts/Inter-Medium.woff2") format("woff2");
-}
-
-/* General Rules and Resets */
-
-* {
- font-size: inherit;
- margin-block-end: 0;
- margin-block-start: 0;
- margin-inline-end: 0;
- margin-inline-start: 0;
- padding-block-end: 0;
- padding-block-start: 0;
- padding-inline-end: 0;
- padding-inline-start: 0;
-}
-
-html {
- background-color: #fefefe;
- box-sizing: border-box;
- font-size: 12px;
- overscroll-behavior: none;
-}
-
-body {
- font-family: var(--fontInter);
- font-size: 13px;
- inline-size: 352px;
- letter-spacing: -0.125px;
- min-inline-size: 352px;
- background-color: var(--bgColor);
-
- --highlight-blue: #1296f8;
- --hr-grey: #cececf91;
- --text-grey: #262726eb;
-
- color: var(--text-grey);
-}
-
-html,
-body {
- block-size: 100%; /* Bugfix: issue 948 */
- max-block-size: 650px;
- min-block-size: 300px;
-
- /* stylelint-disable */
- scrollbar-width: none;
- /* stylelint-enable */
- transition: height 0.1s ease-in-out;
-}
-
-:root {
- --fontInter: "Inter", sans-serif;
- --fontInterMedium: "Inter-Medium", sans-serif;
- --fontMetropolis: "Metropolis", sans-serif;
- --fontMetropolisLight: "Metropolis-Light", sans-serif;
- --primary-action-color: #248aeb;
- --title-text-color: #000;
- --text-normal-color: #262726;
- --text-heading-color: #3d3d3d;
- --iconArrowLeft: url("/img/arrow-icon-left.svg");
- --iconArrowRight: url("/img/arrow-icon-right.svg");
- --iconCloseX: url("/img/close.svg");
- --iconGear: url("/img/gear-icon.svg");
- --iconProxyWarning: url("/img/proxy-warning.svg");
- --logoMozillaVpn: url("/img/moz-vpn-logo.svg");
- --menuItemHeight: 28px;
- --marginInline: 16px;
- --footerHeight: 48px;
- --bgColor: #fefffe;
- --blue20: #0df;
- --blue30: #00b3f4;
- --blue40: #0090ed;
- --blue50: #0060df;
- --blue60: #0250bb;
- --blue70: #054096;
- --red30: #ff848b;
- --red40: #ff6a75;
- --red50: #ff4f5e;
- --red60: #e22850;
- --red70: #c50042;
- --alertColor: var(--red50);
- --primaryCtaDefault: var(--blue50);
- --primaryCtaHover: var(--blue60);
- --primaryCtaActive: var(--blue70);
- --primaryCtafocus: rgba(0, 97, 223, 0.4);
- --controllerDefault: var(--bgColor);
- --controllerHover: var(--grey10);
- --controllerActive: var(--grey20);
- --green50: #3fe1b0;
- --green60: #3ad4b3;
- --green70: #1cc4a0;
- --green80: #00a49a;
- --grey10: #e7e7e7;
- --grey20: #cececf;
- --grey30: #9e9e9e;
- --grey40: #6d6e6e;
- --grey50: #3d3d3d;
- --panelSize: 560px;
- --rowHeight: 48px;
-
- /* calculated from 12px */
- --font-size-heading: 1.33rem; /* 16px */
- --block-line-space-size: 0.5rem; /* 6px */
- --inline-item-space-size: 0.5rem; /* 6px */
- --block-line-separation-size: 0.33rem; /* 10px */
- --inline-icon-space-size: 0.833rem; /* 10px */
-
- /* Use for url and icon size */
- --block-url-label-size: 2rem; /* 24px */
- --inline-start-size: 1.66rem; /* 20px */
- --inline-button-size: 5.833rem; /* 70px */
- --icon-size: 1.166rem; /* 14px */
- --small-text-size: 0.833rem; /* 10px */
- --small-radius: 3px;
- --icon-button-size: calc(calc(var(--block-line-separation-size) * 2) + 1.66rem); /* 20px */
- --inactive-opacity: 0.3;
- --overflow-size: 1px;
- --icon-fit: 8;
-
- background: var(--bgColor);
- margin-block: 0;
- margin-inline: 0;
-}
-
-*,
-*::before,
-*::after {
- box-sizing: inherit;
-}
-
-table {
- border: 0;
- border-spacing: 0;
- inline-size: 100%;
-}
-
-/* Helper Classes */
-.hide {
- display: none !important;
-}
-
-.scrollable {
- flex: 1;
- inline-size: 100%;
- block-size: 100%;
- overscroll-behavior: none;
- overflow-y: auto;
- overflow-x: hidden;
- padding-block-end: 8px;
-}
-
-.offpage {
- opacity: 0;
-}
-
-[hidden] {
- display: none !important;
-}
-
-/* effect borrowed from tabs in firefox, ensure that the element flexes to the full width */
-.truncate-text {
- inline-size: calc(100vw - 80px);
- overflow: hidden;
- position: relative;
- white-space: nowrap;
- text-overflow: ellipsis;
-}
-
-.truncate-text::after {
- background: var(--bgColor);
- content: " ";
- block-size: 100%;
- inline-size: 100px;
- inset-inline-end: 0;
- mask-image: linear-gradient(to right, transparent, var(--bgColor) 70%);
- position: absolute;
-}
-
-.hover-highlight:hover .truncate-text::after,
-.hover-highlight:focus .truncate-text::after {
- background-color: var(--highlight-blue);
- mask-image: linear-gradient(to right, transparent, var(--highlight-blue) 50%);
-}
-
-/* Color and icon helpers */
-[data-identity-color="blue"] {
- --identity-tab-color: #37adff;
- --identity-icon-color: #37adff;
-}
-
-[data-identity-color="turquoise"] {
- --identity-tab-color: #00c79a;
- --identity-icon-color: #00c79a;
-}
-
-[data-identity-color="green"] {
- --identity-tab-color: #51cd00;
- --identity-icon-color: #51cd00;
-}
-
-[data-identity-color="grey"] {
- /* Only used for the edit panel */
- --identity-icon-color: #616161;
-}
-
-[data-identity-color="yellow"] {
- --identity-tab-color: #ffcb00;
- --identity-icon-color: #ffcb00;
-}
-
-[data-identity-color="orange"] {
- --identity-tab-color: #ff9f00;
- --identity-icon-color: #ff9f00;
-}
-
-[data-identity-color="red"] {
- --identity-tab-color: #ff613d;
- --identity-icon-color: #ff613d;
-}
-
-[data-identity-color="pink"] {
- --identity-tab-color: #ff4bda;
- --identity-icon-color: #ff4bda;
-}
-
-[data-identity-color="purple"] {
- --identity-tab-color: #af51f5;
- --identity-icon-color: #af51f5;
-}
-
-[data-identity-icon="fingerprint"] {
- --identity-icon: url("/img/usercontext.svg#fingerprint");
-}
-
-[data-identity-icon="briefcase"] {
- --identity-icon: url("/img/usercontext.svg#briefcase");
-}
-
-[data-identity-icon="dollar"] {
- --identity-icon: url("/img/usercontext.svg#dollar");
-}
-
-[data-identity-icon="cart"] {
- --identity-icon: url("/img/usercontext.svg#cart");
-}
-
-[data-identity-icon="circle"] {
- --identity-icon: url("/img/usercontext.svg#circle");
-}
-
-[data-identity-icon="food"] {
- --identity-icon: url("/img/usercontext.svg#food");
-}
-
-[data-identity-icon="gift"] {
- --identity-icon: url("/img/usercontext.svg#gift");
-}
-
-[data-identity-icon="vacation"] {
- --identity-icon: url("/img/usercontext.svg#vacation");
-}
-
-[data-identity-icon="fruit"] {
- --identity-icon: url("/img/usercontext.svg#fruit");
-}
-
-[data-identity-icon="pet"] {
- --identity-icon: url("/img/usercontext.svg#pet");
-}
-
-[data-identity-icon="tree"] {
- --identity-icon: url("/img/usercontext.svg#tree");
-}
-
-[data-identity-icon="chill"] {
- --identity-icon: url("/img/usercontext.svg#chill");
-}
-
-[data-identity-icon="fence"] {
- --identity-icon: url("/img/usercontext.svg#fence");
-}
-
-#current-tab [data-identity-icon="default-tab"] {
- background: center center no-repeat url("/img/blank-tab.svg");
- fill: currentColor;
-}
-
-/* Buttons */
-
-.button {
- color: var(--text-heading-color);
-}
-
-.button.primary {
- background-color: #0996f8;
- color: white;
-}
-
-.button.primary:hover,
-.button.primary:focus {
- background-color: #0675d3;
-}
-
-.button.secondary:hover,
-.button.secondary:focus {
- background-color: rgba(0, 0, 0, 0.05);
-}
-
-/* Mozilla VPN status icon */
-
-.moz-vpn-status-icon {
- color: var(--text-heading-color);
- background-size: 17px;
- background-position: left center;
- font-size: 13px;
- padding-inline-start: 22px;
- padding-inline-end: 32px;
-}
-
-.moz-vpn-status-icon.connected {
- background-image: url("/img/moz-vpn-status-icons/moz-vpn-connected.svg");
-}
-
-.moz-vpn-status-icon.disconnected {
- background-image: url("/img/moz-vpn-status-icons/moz-vpn-disconnected.svg");
-}
-
-.moz-vpn-logotype.vpn-status-container-list {
- color: var(--text-heading-color);
- background-size: 16px;
- background-position: left center;
- font-size: 12px;
- padding-inline-start: 19px;
- padding-inline-end: 22px;
- margin-inline-end: 20px;
- align-items: center;
-}
-
-.moz-vpn-connection-status-indicator.container-list-status-icon {
- block-size: 16px;
- inline-size: 16px;
-}
-
-/* Toggle Switch */
-
-.switch {
- display: inline-block;
- block-size: 24px;
- position: relative;
- inline-size: 45px;
-}
-
-.switch .switch-input {
- block-size: 0;
- opacity: 0;
- inline-size: 0;
-}
-
-.slider {
- background-color: var(--grey20);
- border-radius: 24px;
- inset-block-end: 0;
- box-shadow: 0 0 0 2px var(--bgColor), 0 0 0 4px var(--bgColor);
- inset-inline-start: 0;
- position: absolute;
- inset-inline-end: 0;
- inset-block-start: 0;
- transition: 0.1s ease-in-out;
-}
-
-.slider::before {
- background-color: #fff;
- border-radius: 50%;
- inset-block-end: 3px;
- content: "";
- block-size: 18px;
- inset-inline-start: 3px;
- position: absolute;
- transition: 0.1s ease-in-out;
- inline-size: 18px;
-}
-
-input:hover + .slider {
- background-color: var(--grey30);
-}
-
-input:focus + .slider {
- box-shadow: 0 0 0 2px var(--bgColor), 0 0 0 4px var(--grey30);
-}
-
-input:active + .slider {
- background-color: var(--grey40);
-}
-
-input:checked + .slider {
- background-color: var(--green50);
-}
-
-input:checked:hover + .slider {
- background-color: var(--green60);
-}
-
-input:checked:focus + .slider {
- box-shadow: 0 0 0 2px var(--bgColor), 0 0 0 4px var(--green70);
-}
-
-input:checked:active + .slider {
- background-color: var(--green70);
-}
-
-input:checked + .slider::before {
- transform: translateX(21px);
-}
-
-.hidden {
- visibility: hidden;
-}
-
-/* Primary CTA Buttons */
-
-.primary-cta {
- block-size: 32px;
- background-color: var(--primaryCtaDefault);
- border: transparent;
- border-radius: 4px;
- color: #fff;
- transition: background-color 0.2s ease-in-out;
-}
-
-.primary-cta:hover {
- background-color: var(--primaryCtaHover);
-}
-
-.primary-cta:focus {
- outline: none;
- box-shadow: 0 0 0 1px var(--blue60), 0 0 0 4px var(--primaryCtafocus);
-}
-
-.primary-cta:active {
- background-color: var(--primaryCtaActive);
-}
-
-/* Mozilla VPN tout */
-
-#moz-vpn-tout {
- opacity: 0;
- background-color: var(--bgColor);
- visibility: visible;
- max-block-size: 500px;
- position: absolute;
- inset-block-end: var(--footerHeight);
- inset-inline-start: 0;
- inset-inline-end: 0;
- box-shadow: 0 0 7px 0 #9498a25e;
- animation: appear 0.2s ease-out 0.5s forwards;
- transition: opacity 0.1s ease-in-out, max-height 0.3s ease-in-out;
-}
-
-@keyframes appear {
- 0% {
- opacity: 0;
- transform: translateY(10%);
- }
-
- 100% {
- opacity: 1;
- transform: translateY(0%);
- }
-}
-
-/* Mozilla VPN Controller UI in Container Management Panel */
-
-.moz-vpn-content,
-.moz-vpn-controller-content {
- display: flex;
- position: relative;
- flex-direction: column;
- padding-block: 16px;
- transition: max-height 0.3s ease-in-out, padding-block-end 0.2s ease-in-out;
- box-shadow: 0 0 0 1px var(--hr-grey);
-}
-
-.moz-vpn-connection-status-indicator {
- position: absolute;
- inset-inline-end: 0;
- background-position: center center;
- background-repeat: no-repeat;
- background-size: contain;
- size: 0;
- color: rgba(0, 0, 0, 0);
- block-size: 24px;
- inline-size: 24px;
-}
-
-.current-country-flag {
- display: inline-block;
- background-repeat: no-repeat;
- background-position: left center;
- background-size: contain;
- block-size: 16px;
- inline-size: 16px;
-}
-
-.moz-vpn-controller-content.show-server-button {
- padding-block-end: 56px;
- transition: 0.2s ease-in-out;
-}
-
-.dismiss-moz-vpn-tout {
- margin-inline-start: auto;
- block-size: 24px;
- inline-size: 24px;
- background: var(--bgColor);
- background-image: var(--iconCloseX);
- border: none;
- border-radius: 4px;
-}
-
-.flag-img {
- block-size: 13px;
- margin-inline-end: 4px;
- opacity: 0.9;
-}
-
-.page-action-flag {
- margin-inline-end: var(--marginInline);
-}
-
-.display-none {
- display: none;
-}
-
-.proxy-disabled {
- opacity: 0.4;
-}
-
-fieldset.proxies {
- position: absolute;
- inset-block-start: 120px;
- inset-inline-start: 0;
- inset-inline-end: 0;
- block-size: 60px;
- display: flex;
- background: #5cabff;
- justify-content: center;
- align-content: center;
- align-items: center;
- flex-direction: row;
- pointer-events: none;
-}
-
-input.proxies {
- font-size: 6px;
- block-size: 20px;
- max-block-size: 20px;
- padding-block: 0 !important;
- padding-inline: 0 !important;
- display: inline-flex;
- inline-size: 40% !important;
- pointer-events: none;
-}
-
-.moz-vpn-cta {
- block-size: 32px;
- margin-block-start: 16px;
- margin-block-end: 4px;
- margin-inline: var(--marginInline);
- text-align: center;
-}
-
-.apply-to-container {
- block-size: 32px;
- inline-size: 100%;
- text-align: center;
- margin-block: 16px;
-}
-
-#moz-vpn-current-server {
- align-items: center;
- border: none;
- display: flex;
- block-size: 48px;
- margin-block-start: 8px;
- background-image: var(--iconArrowRight);
- background-position: calc(100% - 24px) center;
- background-repeat: no-repeat;
- background-size: 9px;
- outline: none;
- padding-inline-start: 20px;
- visibility: visible;
- position: absolute;
- inset-block-end: 0;
- inline-size: 100%;
- opacity: 0;
- transition: opacity 0.2s ease-in-out;
-}
-
-.moz-vpn-controller-content.show-server-button #moz-vpn-current-server {
- opacity: 1;
-}
-
-.moz-vpn-controller-content.show-server-button #moz-vpn-current-server[disabled] {
- opacity: 0.5;
- cursor: not-allowed;
-}
-
-@keyframes serverButtonAppear {
- 0% {
- opacity: 0;
- visibility: hidden;
- z-index: -1;
- }
-
- 90% {
- z-index: -1;
- visibility: hidden;
- }
-
- 100% {
- visibility: visible;
- z-index: 1;
- opacity: 1;
- }
-}
-
-#moz-vpn-current-server.hidden {
- block-size: 0;
- opacity: 0;
- visibility: hidden;
- z-index: -1;
-}
-
-.current-city-name {
- padding-inline-start: 12px;
-}
-
-.collapsible-content {
- max-block-size: 0;
- opacity: 0;
- visibility: hidden;
- display: none;
- background-color: var(--bgColor);
- transition: max-height 0.2s ease-in-out, opacity 0.2s ease-in-out, visibility 0.2s ease-in-out;
-}
-
-.moz-vpn-subtitle {
- font-size: 12px;
- flex: 0 1 80%;
- color: var(--text-normal-color);
-}
-
-.collapsible-content > .flx-row.flx-space-between {
- inline-size: calc(100% - 40px);
- margin-inline: auto;
- padding-block-start: 12px;
-}
-
-[disabled] {
- pointer-events: none;
- opacity: 0.5;
-}
-
-#current-proxy {
- font-size: 12px;
- color: var(--grey30);
- line-height: 13px;
-}
-
-.expanded .collapsible-content {
- display: flex;
- max-block-size: 500px;
- opacity: 1;
- visibility: visible;
-}
-
-.hide-label,
-.show-label {
- line-height: 100%;
- position: absolute;
- inset-inline-end: 0;
- transition: visibility 0.2s ease-in-out, color 0.2s ease-in-out, opacity 0.2s ease-in-out;
-}
-
-.expanded .hide-label,
-.show-label {
- visibility: visible;
- opacity: 1;
-}
-
-/* stylelint-disable */
-.hide-label,
-.expanded .show-label {
- visibility: hidden;
- opacity: 0;
-}
-
-/* stylelint-enable */
-
-.expand-collapse {
- inline-size: 50%;
- margin-inline-start: auto;
- pointer-events: all;
-}
-
-.button-wrapper {
- margin-inline: 20px;
-}
-
-/* Advanced Proxy Settings Button */
-
-#edit-advanced-proxy-input {
- padding-inline-end: 40px;
-}
-
-#edit-advanced-proxy-input.valid:focus {
- box-shadow: 0 0 0 3px #3fe1b030;
- border-color: var(--green80);
-}
-
-.advanced-proxy-settings-btn {
- background-color: var(--bgColor);
- box-shadow: 0 0 0 1px var(--hr-grey);
- background-image: var(--iconGear), var(--iconArrowRight);
- background-position: 16px center, calc(100% - 24px) center;
- background-repeat: no-repeat;
- background-size: 24px 24px, 9px;
- border: none;
- color: var(--text-grey);
- block-size: 56px;
- min-block-size: 56px;
- line-height: 19px;
- display: flex;
- flex-direction: column;
- justify-content: center;
- outline: none;
- padding-inline-start: 44px;
- z-index: 2;
- transition: opacity 0.1s ease-in-out, background-color 0.1s ease-in-out;
-}
-
-.disabled {
- opacity: 0.5;
- cursor: not-allowed;
- pointer-events: none;
-}
-
-.advanced-proxy-settings-btn:hover,
-.advanced-proxy-settings-btn:focus {
- background-color: var(--grey10);
- outline: none;
-}
-
-#clear-advanced-proxy-input {
- position: absolute;
- inset-inline-end: 8px;
- inset-block-start: 7px;
- border: none;
- block-size: 22px;
- inline-size: 22px;
- border-radius: 50%;
- background-image: var(--iconCloseX);
- background-repeat: no-repeat;
- background-position: center center;
- background-size: 16px;
- font-size: 1;
- color: var(--bgColor);
-}
-
-.proxy-title-container-color {
- block-size: 12px;
- inline-size: 12px;
- z-index: 10;
- border-radius: 50%;
-}
-
-.advanced-proxy-panel-content {
- padding-block: 16px;
- padding-inline: 20px;
- margin-block-start: 56px;
- display: flex;
- flex-direction: column;
-}
-
-.advanced-proxy-input-wrapper {
- margin-block-start: 12px;
- position: relative;
- display: flex;
- flex-direction: column;
-}
-
-.proxy-validity {
- position: absolute;
- inset-block-start: 42px;
- inset-inline-start: 16px;
- visibility: hidden;
- opacity: 0;
- background-color: var(--alertColor);
- color: white;
- border-radius: 4px;
- padding-block: 2px;
- padding-inline: 4px;
- transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out;
-}
-
-.proxy-validity::after {
- content: "";
- block-size: 8px;
- inline-size: 8px;
- background-color: var(--alertColor);
- inset-block-start: -4px;
- position: absolute;
- transform: rotate(45deg);
- inset-inline-start: 12px;
-}
-
-.invalid .proxy-validity {
- opacity: 1;
- z-index: 10;
- visibility: visible;
-}
-
-.invalid .proxy-host.primary-input {
- border-color: var(--red50);
- box-shadow: 0 0 0 3px #ff848b70;
-}
-
-.invalid button {
- pointer-events: none;
- opacity: 0.5;
-}
-
-/* Mozilla VPN Server list */
-
-.moz-vpn-logo,
-.moz-vpn-logotype {
- color: var(--text-heading-color);
- background-image: var(--logoMozillaVpn);
- background-repeat: no-repeat;
- background-size: 24px;
- background-position: left center;
- font-family: var(--fontMetropolis);
- font-size: 15px;
- line-height: 24px;
- padding-inline-start: 28px;
- position: relative;
- padding-inline-end: 32px;
-}
-
-#moz-vpn-server-list-panel {
- block-size: var(--panelSize);
- max-block-size: var(--panelSize);
- min-block-size: var(--panelSize);
- overflow-x: hidden;
- overflow-y: hidden;
-}
-
-.proxy-panel-title {
- line-height: var(--rowHeight);
- block-size: var(--rowHeight);
- border-block-end: 1px solid var(--hr-grey);
- position: fixed;
- z-index: 1;
- background-color: var(--bgColor);
- box-shadow: 0 0 13px -2px #b5b5b500;
- transition: box-shadow 0.5s ease;
-}
-
-.drop-shadow {
- box-shadow: 0 0 13px -2px #b5b5b54d;
-}
-
-.moz-vpn-server-list {
- padding-block-start: 4px;
- font-size: 15px;
- color: var(--grey50);
- position: absolute;
- inset-block-start: var(--rowHeight);
- inset-inline-start: 0;
- inset-inline-end: 0;
- overflow: scroll;
- overflow-x: hidden;
- overscroll-behavior: none;
- block-size: calc(var(--panelSize) - var(--rowHeight));
- min-block-size: calc(var(--panelSize) - var(--rowHeight));
-}
-
-#moz-vpn-return {
- z-index: 2;
-}
-
-.server-list-item {
- display: flex;
- flex-direction: column;
- position: relative;
- background-color: var(--bgColor);
-}
-
-.server-country-flag {
- inline-size: 16px;
- margin-inline-start: 16px;
- margin-block: auto;
- pointer-events: none;
-}
-
-.server-country-name {
- padding-block: 0;
- padding-inline-end: 0;
- padding-inline-start: 20px;
- font-family: var(--fontMetropolis);
- pointer-events: none;
- color: var(--text-heading-color);
-}
-
-.server-city-list-item,
-.server-city-list-visibility-btn {
- block-size: 40px;
- margin-block-start: 4px;
- margin-block-end: 4px;
- margin-inline-start: 8px;
- margin-inline-end: 8px;
- inline-size: calc(100% - 16px);
-}
-
-.server-city-list-visibility-btn {
- display: flex;
- background-color: var(--bgColor);
- border-radius: 4px;
- border: none;
- transition: background-color 0.3s ease;
-}
-
-.server-city-list-visibility-btn:hover {
- background-color: var(--grey10);
-}
-
-.server-city-list-visibility-btn:active {
- background-color: var(--grey20);
-}
-
-.toggle {
- background-image: url("/img/arrow-toggle.svg");
- background-position: center center;
- background-repeat: no-repeat;
- block-size: 24px;
- margin-inline-start: 8px;
- pointer-events: none;
- transform: rotate(-90deg);
- transition: transform 0.275s ease-in-out;
- inline-size: 24px;
-}
-
-.expanded .toggle {
- transform: rotate(0deg);
-}
-
-.server-city-list {
- block-size: 0;
- opacity: 0;
- transition: height 0.3s ease-in-out, opacity 0.3s ease, visibility 0.4s ease;
- list-style-type: none;
- visibility: hidden;
-}
-
-.expanded .server-city-list {
- opacity: 1;
- visibility: visible;
-}
-
-.server-city-list-item {
- align-items: center;
- display: flex;
- position: relative;
-}
-
-.server-city-name {
- font-family: var(--fontMetropolisLight);
- font-weight: 300;
- color: var(--text-grey);
- padding-inline-start: 18px;
-}
-
-/* ----- controller buttons ------- */
-
-.controller {
- background-color: var(--bgColor);
- color: var(--text-grey);
- transition: background-color 0.1s ease-in-out;
-}
-
-.controller:hover,
-.controller:focus {
- background-color: var(--controllerHover);
-}
-
-.controller:active {
- background-color: var(--controllerActive);
-}
-
-/* WARNING MODAL ---- */
-
-.modal-warning {
- position: absolute;
- inset-block-start: 0;
- inset-block-end: 0;
- inset-inline-start: 0;
- background-color: #42404c89;
- z-index: 4;
- display: flex;
- justify-content: center;
-}
-
-.modal-content {
- background-color: var(--bgColor);
- inline-size: 80%;
- block-size: 80%;
- margin-inline: auto;
- margin-block: auto;
- border-radius: 16px;
- box-shadow: 1px 2px 10px 10px var(--bgDark);
- padding-block: 20px;
- padding-inline: 20px;
- display: flex;
- justify-content: center;
- align-items: center;
- flex-direction: column;
-}
-
-/* ----- MozillaVPN Proxy Unavailable-Specific -------- */
-
-[data-moz-proxy-warning="proxy-unavailable"] {
- position: relative;
-}
-
-.flag-img.proxy-unavailable {
- opacity: 0.5;
-}
-
-/* ----- MozillaVPN Status Tooltips -------- */
-
-.tooltip {
- opacity: 0;
- position: absolute;
- z-index: 10;
- inset-block-start: 24px;
- inset-inline-end: -3px;
- font-size: 11px;
- font-family: var(--fontInter) !important;
- font-weight: 300;
- color: var(--text-normal-color);
- background-color: var(--bgColor);
- padding-inline: 8px;
- padding-block: 4px;
- border-radius: 4px;
- box-shadow: 0 0 12px 3px #0000001c;
- transform: translateY(-2px);
- transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
- min-inline-size: 170px;
- line-height: 1.3;
- text-align: center;
- pointer-events: none;
-}
-
-.tooltip::before {
- content: "";
- block-size: 7px;
- inline-size: 7px;
- border-radius: 1px;
- transform: rotate(45deg);
- background: inherit;
- position: absolute;
- inset-block-start: -3px;
- inset-inline-end: 9px;
-}
-
-[data-moz-proxy-warning="proxy-unavailable"]:hover .tooltip,
-[data-moz-proxy-warning="proxy-unavailable"]:active .tooltip {
- opacity: 1;
- transform: translateY(0);
- transition: opacity 0.2s ease-in-out 0.5s, transform 0.2s ease-in-out 0.5s;
-}
-
-.moz-vpn-logotype.vpn-status-container-list:hover .tooltip {
- opacity: 1;
- transform: translateY(0);
- transition: opacity 0.2s ease-in-out 1s, transform 0.2s ease-in-out 1s;
-}
-
-.tooltip.proxy-unavailable::after {
- inset-inline-start: 4px;
- inset-inline-end: auto;
-}
-
-.tooltip.proxy-unavailable::before {
- inset-inline-start: 12px;
-}
-
-.tooltip.proxy-unavailable {
- inset-inline-start: 32px;
- inset-block-start: 32px;
- padding-inline-start: 32px;
- text-align: left;
- background-image: var(--iconProxyWarning);
- background-size: 24px 24px;
- background-repeat: no-repeat;
- background-position: 4px 4px;
-}
-
-/* ------------ SERVER LIST RADIO BUTTONS ------------ */
-
-.server-radio-btn {
- block-size: 20px;
- opacity: 0;
- position: fixed;
- inline-size: 20px;
-}
-
-.server-radio-control {
- border-radius: 50%;
- border: 2px solid var(--grey40);
- block-size: 20px;
- margin-inline-start: 46px;
- pointer-events: none;
- position: relative;
- inline-size: 20px;
- transition: border 0.1s ease-in-out;
-}
-
-.server-radio-btn:checked + .server-radio-control {
- border-color: var(--primaryCtaDefault);
- transition: border-color 0.2s ease;
-}
-
-.server-radio-control::after {
- background-color: var(--grey40);
- border-radius: 50%;
- inset-block-end: 0;
- content: "";
- block-size: 12px;
- inset-inline-start: 0;
- margin-inline: auto;
- margin-block: auto;
- opacity: 0;
- position: absolute;
- inset-inline-end: 0;
- inset-block-start: 0;
- transition: opacity 0.1s ease-in-out;
- inline-size: 12px;
-}
-
-/* Unchecked radio button styles */
-
-.server-city-list-item:hover .server-radio-control {
- border: 2px solid var(--grey50);
-}
-
-.server-city-list-item:hover .server-radio-control::after {
- opacity: 0.3;
-}
-
-.server-city-list-item:active .server-radio-control::after {
- opacity: 0.5;
-}
-
-/* Checked radio button rules */
-
-.server-city-list-item:hover .server-radio-btn:checked + .server-radio-control {
- border: 2px solid var(--primaryCtaDefault);
-}
-
-.server-radio-btn:checked + .server-radio-control::after {
- background-color: var(--primaryCtaDefault);
- opacity: 1;
-}
-
-/* Helpers */
-
-.add-bg-color {
- background-color: var(--bgColor);
- z-index: 2;
-}
-
-.flx-space-between {
- justify-content: space-between;
-}
-
-.flx-row {
- align-items: center;
- display: flex;
- flex-direction: row;
-}
-
-/* stylelint-disable */
-
-v-padding-hack16 {
- block-size: 16px;
-}
-
-v-padding-hack-4 {
- block-size: 4px;
- inline-size: 100%;
-}
-
-v-padding-hack-footer {
- block-size: var(--footerHeight);
- inline-size: 100%;
-}
-
-/* stylelint-enable */
-
-.flx-col {
- display: flex;
- flex-direction: column;
-}
-
-fieldset,
-.options-header {
- padding-block-end: 16px;
-}
-
-.options-header {
- display: none;
-}
-
-/* ------ Input ----- */
-
-input[type=text] {
- block-size: 36px;
- border-radius: 4px;
- background-color: var(--bgColor);
- color: var(--text-grey);
- padding-block: 8px;
- padding-inline: 8px;
-}
-
-/* Blue links */
-
-.blue-link {
- box-sizing: content-box;
- text-decoration: none;
- align-items: center;
- background-color: transparent;
- border: none;
- color: var(--primaryCtaDefault);
- display: flex;
- block-size: 24px;
- line-height: 24px;
- position: relative;
- margin-inline: auto;
- transition: color 0.1s ease-in-out;
-}
-
-.blue-link,
-.hide-show-label {
- block-size: 24px;
- line-height: 24px;
-}
-
-.blue-link:hover {
- color: var(--primaryCtaHover);
-}
-
-.blue-link:focus,
-.blue-link:focus .hide-show-label {
- text-decoration: underline;
- outline: none;
-}
-
-/* ------------ ------------ ------------ ------------ */
-
-/* Panels keep everything together */
-.panel {
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- position: relative;
- max-block-size: 601px;
- background-color: var(--bgColor);
- transition: height 0.1s ease-in-out;
-}
-
-.container-panel {
- min-block-size: 500px;
-}
-
-.delete-container-panel {
- min-block-size: 300px;
-}
-
-.panel.onboarding,
-.achievement-panel {
- align-items: center;
- margin-block: var(--marginInline);
- margin-inline: var(--marginInline);
- min-block-size: 360px;
-}
-
-.panel.onboarding-panel-8.optional-permissions-disabled {
- min-block-size: 420px;
- margin-block-end: 0;
- margin-inline: 0;
-}
-
-.optional-permissions-disabled #moz-vpn-fw-onboarding-done {
- display: none !important;
-}
-
-.moz-vpn-permissions {
- padding-block: var(--marginInline);
- padding-inline: var(--marginInline);
- background-color: #cececf1c;
- border-block-start: 1px solid var(--hr-grey);
- display: none;
-}
-
-.optional-permissions-disabled .moz-vpn-permissions {
- display: block;
- inline-size: 100%;
-}
-
-.moz-vpn-onboarding-content {
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- align-items: center;
- padding-inline: var(--marginInline);
- padding-block-end: var(--marginInline);
-}
-
-.moz-vpn-permissions-copy {
- padding-inline: 20px;
- font-size: 12px;
- margin-block-end: 16px;
-}
-
-.panel-content {
- flex: 1;
- padding-block-start: 16px;
-}
-
-.panel-footer .button {
- align-items: center;
- block-size: 100%;
- display: flex;
- flex: 1;
- justify-content: center;
-}
-
-/* Onboarding styles */
-.onboarding * {
- text-align: center;
-}
-
-.onboarding-img {
- block-size: 132px;
- inline-size: 180px;
-}
-
-.onboarding-title {
- color: #43484e;
- font-size: var(--font-size-heading);
- margin-block: 12px;
- margin-inline-end: 0;
- margin-inline-start: 0;
- max-inline-size: 80%;
- font-family: var(--fontMetropolis);
-}
-
-.onboarding p {
- color: var(--text-normal-color);
- font-size: 14px;
- margin-block-end: 16px;
- max-inline-size: 84%;
-}
-
-.onboarding-button {
- align-items: center;
- background-color: #0996f8;
- border-radius: 3px;
- color: white;
- display: flex;
- flex: 0 0 44px;
- font-size: 14px;
- inline-size: 100%;
- justify-content: center;
- text-decoration: none;
- transition: background-color 75ms;
-}
-
-.half-button-wrapper {
- align-items: center;
- display: flex;
- flex-direction: row;
- block-size: 44px;
- inline-size: 100%;
- font-family: var(--fontMetropolis);
-}
-
-.half-onboarding-button {
- align-items: center;
- background-color: #0996f8;
- border-radius: 3px;
- color: white;
- display: flex;
- flex: 1 0 auto;
- font-size: 14px;
- block-size: 44px;
- inline-size: 50%;
- justify-content: center;
- margin-inline-end: 4px;
- text-decoration: none;
- transition: background-color 75ms;
-}
-
-.grey-button {
- background-color: #e3e3e3;
- color: var(--grey50);
-}
-
-.onboarding-button:hover,
-.onboarding-button:active {
- background-color: #0675d3;
-}
-
-.onboarding-button:focus,
-.half-onboarding-button:focus {
- box-shadow: 0 0 0 1px #0a84ff inset, 0 0 0 1px #0a84ff, 0 0 0 4px rgba(10, 132, 255, 0.3);
-}
-
-/* Pop buttons are the square shaped buttons used to
-manage things like container crud */
-.pop-button {
- align-items: center;
- block-size: var(--icon-button-size);
- cursor: pointer;
- display: flex;
- flex: 0 0 var(--icon-button-size);
- justify-content: center;
-}
-
-.panel-footer a {
- text-decoration: none;
-}
-
-.userContext-wrapper {
- align-items: center;
- display: flex;
- flex: 1 1;
- transition: background-color 75ms;
-}
-
-.edit-containers-panel .userContext-wrapper {
- max-inline-size: calc(var(--overflow-size) + 203px);
-}
-
-.disable-edit-containers {
- opacity: var(--inactive-opacity);
- pointer-events: none;
-}
-
-.userContext-icon-wrapper {
- block-size: var(--icon-button-size);
- flex: 0 0 var(--icon-button-size);
- margin-inline-start: var(--inline-icon-space-size);
-}
-
-/* .userContext-icon is used natively, Bug 1333811 was raised to fix */
-.usercontext-icon {
- background-image: var(--identity-icon);
- background-position: center center;
- background-repeat: no-repeat;
- background-size: 16px;
- block-size: 100%;
- fill: var(--identity-icon-color);
- filter: url('/img/filters.svg#fill');
-}
-
-.usercontext-icon::before {
- transform: scale(1);
- transform-origin: center;
- transition: fill 0.1s ease-in-out, transform 0.1s ease-in-out;
-}
-
-.radio-container:active .usercontext-icon::before {
- transform: scale(0.95);
-}
-
-#edit-container-panel-choose-icon .radio-container:hover .usercontext-icon::before {
- fill: var(--grey50) !important;
-}
-
-.mac-icon {
- background-image: url('/img/multiaccountcontainer-16.svg');
- background-position: center center;
- background-repeat: no-repeat;
- background-size: 16px;
- block-size: 100%;
-}
-
-.container-panel-row:hover .clickable .usercontext-icon,
-.container-panel-row:focus .clickable .usercontext-icon,
-.container-panel-row .clickable:focus .usercontext-icon {
- background-image: url('/img/container-newtab.svg');
- fill: #979797;
- filter: url('/img/filters.svg#fill');
-}
-
-.container-panel-row .clickable:hover .usercontext-icon,
-.container-panel-row .clickable:focus .usercontext-icon {
- fill: #0094fb;
-}
-
-/* Panel footer */
-.panel-footer {
- align-items: center;
- block-size: var(--footerHeight);
- color: #000;
- display: flex;
- font-size: 13px;
- inline-size: 100%;
- justify-content: space-between;
- box-shadow: 0 0 0 1px var(--hr-grey);
-}
-
-#container-info-panel {
- block-size: 100vh;
-}
-
-.container-info-has-tabs,
-.container-info-tab-row {
- align-items: center;
- display: flex;
- flex: 0 0 28px;
- font-size: 14px;
- justify-content: flex-start;
- margin-block-end: 0;
- margin-block-start: 0;
- margin-inline-end: 0;
- margin-inline-start: 0;
- padding-inline-end: 16px;
- padding-inline-start: 16px;
-}
-
-.container-info-has-tabs img,
-.container-info-tab-row img {
- block-size: 16px;
- flex: 0 0 16px;
- margin-inline-end: 4px;
-}
-
-.container-info-tab-row img[src=""] {
- margin-inline-end: 0;
-}
-
-.delete-container-confirm {
- padding-inline-end: 20px;
- padding-inline-start: 20px;
-}
-
-.delete-container-confirm-title {
- color: #000;
- font-size: var(--font-size-heading);
-}
-
-#edit-sites-assigned h3 {
- font-size: 14px;
- font-weight: normal;
- padding-block-end: 6px;
- padding-block-start: 6px;
- padding-inline-end: 16px;
- padding-inline-start: 16px;
-}
-
-.assigned-sites-list > div {
- display: flex;
- padding-block-end: 6px;
- padding-block-start: 6px;
-}
-
-.assigned-sites-list > div > .icon {
- margin-inline-end: 10px;
-}
-
-.assigned-sites-list > div > .hostname {
- flex: 1;
-}
-
-.radio-choice > .radio-container {
- align-items: center;
- block-size: 32px;
- display: flex;
- justify-content: center;
- flex: 0 0 calc(100% / var(--icon-fit));
-}
-
-.radio-choice > .radio-container > label {
- background: none;
- block-size: 23px;
- border: 0;
- filter: none;
- inline-size: 23px;
- margin-block-end: 0;
- margin-block-start: 0;
- margin-inline-end: 0;
- margin-inline-start: 0;
- padding-block-end: 0;
- padding-block-start: 0;
- padding-inline-end: 0;
- padding-inline-start: 0;
-}
-
-.radio-choice > .radio-container > label::before {
- background-color: unset;
- background-image: var(--identity-icon);
- background-position: center;
- background-repeat: no-repeat;
- background-size: 16px;
- block-size: 23px;
- border: none;
- content: "";
- display: block;
- fill: var(--identity-icon-color);
- filter: url('/img/filters.svg#fill');
- inline-size: 23px;
- position: relative;
-}
-
-.radio-choice > .radio-container > [type="radio"] {
- -moz-appearance: none;
- display: inline;
- opacity: 0;
- position: absolute;
- margin-block: auto;
- margin-inline: auto;
-}
-
-.radio-choice > .radio-container > [type="radio"]:checked + label {
- background: #d3d3d3;
- border-radius: 100%;
-}
-
-/* When focusing the element add a thin blue highlight to match input fields. This gives a distinction to other selected radio items */
-.radio-choice > .radio-container > [type="radio"]:focus + label {
- outline: 1px solid #1f9ffc;
- -moz-outline-radius: 100%;
-}
-
-.edit-container-panel fieldset {
- background: none;
- border: none;
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
-}
-
-#edit-container-choose-color {
- justify-content: space-between;
-}
-
-.edit-container-panel input[type="text"] {
- inline-size: 100%;
- margin-inline: 4px;
-}
-
-input[type="text"]:focus {
- box-shadow: 0 0 0 3px var(--primaryCtafocus);
- outline: none;
- border-color: var(--blue70);
-}
-
-.edit-container-panel legend,
-.options-header {
- margin-inline: 4px;
- flex: 1 0;
-}
-
-/* Achievement panel elements */
-.share-ctas {
- padding-block-end: 0.5em;
- padding-block-start: 0.5em;
- padding-inline-end: 0.5em;
- padding-inline-start: 0.5em;
- text-align: center;
-}
-
-.cta-link {
- text-decoration: none;
-}
-
-.cta {
- color: #fff;
- font-size: 0.7em;
- font-weight: bold;
- margin-block-end: 0.4em;
- margin-block-start: 0.4em;
- margin-inline-end: 0.4em;
- margin-inline-start: 0.4em;
- padding-block-end: 0.5em;
- padding-block-start: 0.5em;
- padding-inline-end: 0.5em;
- padding-inline-start: 0.5em;
- text-transform: uppercase;
-}
-
-.cta-icon {
- block-size: 18px;
- padding-inline-end: 0.5em;
- vertical-align: middle;
-}
-
-.fb-share-cta {
- background: #375496;
-}
-
-.fb-share-cta .cta-icon {
- margin-block-start: -5px;
-}
-
-.tweet-cta {
- background: #37bae7;
-}
-
-.amo-rate-cta {
- background: #0f1126;
-}
-
-h3.title {
- block-size: 48px;
- color: #000;
- font-family: var(--fontMetropolis);
- font-size: 14px;
- font-weight: bold;
- inline-size: 100%;
- letter-spacing: -0.1px;
- line-height: 48px;
- text-align: center;
-}
-
-.menu {
- border-style: none;
- inline-size: 100%;
- padding-block: 8px;
-}
-
-.menu-item {
- cursor: pointer;
- block-size: var(--menuItemHeight);
- inline-size: 100%;
- line-height: var(--menuItemHeight);
- display: flex;
- align-items: center;
-}
-
-.menu-text {
- display: flex;
- flex: 1;
-}
-
-.menu-item td {
- align-items: center;
- display: flex;
- inline-size: 100%;
-}
-
-.menu-item.drag-over td {
- border-block-start: 2px solid var(--text-normal-color);
-}
-
-.disabled-menu-item {
- color: grey;
- cursor: default;
- font-style: italic;
-}
-
-.hover-highlight {
- transition: background-color 0.1s ease-in-out, color 0.1s ease-in-out;
-}
-
-.hover-highlight:hover,
-.hover-highlight:focus {
- background-color: var(--highlight-blue);
- color: #fff;
-}
-
-.menu-item-name {
- display: flex;
- inline-size: calc(100% - 40px);
- max-inline-size: 260px;
- cursor: default;
-}
-
-.menu-icon {
- display: block;
- block-size: 16px;
- inline-size: 23px;
- margin-block-end: auto;
- margin-block-start: auto;
- margin-inline-end: 8px;
- margin-inline-start: var(--marginInline);
- text-align: center;
-}
-
-/* Maintain 1:1 square ratio for favicons of websites added to a specific container */
-#edit-sites-assigned .menu-icon,
-#container-info-table .menu-icon {
- inline-size: 16px;
-}
-
-.menu-right-float {
- text-align: right;
- margin-inline-start: auto;
- margin-inline-end: 0;
- display: flex;
- justify-content: flex-end;
- align-items: center;
- padding-inline-start: 16px;
-}
-
-.container-count {
- opacity: 0.7;
- text-align: center;
- min-inline-size: 24px;
- margin-inline-end: 4px;
-}
-
-.menu-arrow {
- align-items: center;
- display: flex;
- justify-content: flex-end;
- block-size: 24px;
- margin-inline-end: 20px;
- text-align: center;
-}
-
-.menu-arrow img {
- block-size: 24px;
- inline-size: 12px;
- padding-block-end: 2px;
- padding-block-start: 2px;
- padding-inline-end: 2px;
- padding-inline-start: 2px;
- opacity: 0.9;
-}
-
-hr {
- border: 0;
- border-block-start: 1px solid var(--hr-grey);
- display: block;
-}
-
-.sub-header-wrapper {
- margin-block-start: 12px;
-}
-
-.sub-header {
- color: var(--text-heading-color);
- block-size: 24px;
- line-height: 24px;
- padding-block-end: 0;
- padding-block-start: 0;
- padding-inline-start: 20px;
- font-family: var(--fontInterMedium);
-}
-
-.edit-form {
- color: var(--text-grey);
- flex: 1;
- padding-block-end: 16px;
- padding-block-start: 16px;
- padding-inline-end: 16px;
- padding-inline-start: 16px;
-}
-
-.bottom-btn {
- inset-block-end: 0;
- box-shadow: 0 0 0 1px var(--hr-grey);
- cursor: pointer;
- block-size: var(--footerHeight);
- inline-size: 100%;
- line-height: var(--footerHeight);
- padding-inline-end: 16px;
- padding-inline-start: 16px;
- position: absolute;
- text-align: center;
- font-size: 14px;
- font-family: var(--fontMetropolis);
- color: var(--text-heading-color);
- pointer-events: all;
-}
-
-.delete-btn {
- background-color: var(--bgColor);
- border: none;
- border-left: none;
- border-right: none;
- border-block-end: none;
- box-shadow: 0 0 0 1px var(--hr-grey);
- color: var(--alertColor);
- cursor: default;
- display: flex;
- block-size: var(--rowHeight);
- justify-content: center;
- line-height: var(--rowHeight);
- pointer-events: all;
- transition: background-color 0.1s ease-in-out, border-color 0.1s ease-in-out, box-shadow 0.1s ease-in-out;
-}
-
-.alert-text {
- font-family: var(--fontMetropolis);
- background-color: var(--bgColor);
- color: var(--alertColor);
- cursor: pointer;
- text-align: center;
-}
-
-.alert-text:hover,
-.alert-text:focus {
- background-color: rgba(255, 79, 94, 0.05);
- box-shadow: 0 0 0 1px rgba(255, 79, 94, 0.05);
-}
-
-.delete-btn:active {
- background-color: rgba(255, 79, 94, 0.1);
- box-shadow: 0 0 0 1px var(--alertColor);
-}
-
-.delete-btn:focus {
- box-shadow: 0 0 0 1px var(--alertColor);
- outline: none;
-}
-
-.btn-return.arrow-left {
- background-image: var(--iconArrowLeft);
- border: 0;
- cursor: pointer;
- inset-block-start: 8px;
- inset-inline-start: 8px;
- position: absolute;
- z-index: 2;
- block-size: 32px;
- inline-size: 32px;
- background-repeat: no-repeat;
- border-radius: 4px;
- background-position: center center;
-}
-
-input {
- border: solid 1px #bebebe;
- border-radius: 2px;
-}
-
-.form-header {
- padding-block-end: 0;
- padding-block-start: 0;
- padding-inline-end: 0;
- padding-inline-start: 0;
-}
-
-.edit-container-panel-name-input {
- color: var(--text-grey);
- block-size: 32px;
-}
-
-.container-options {
- block-size: 24px;
- margin-inline: 4px;
- display: flex;
- justify-content: space-between;
-}
-
-.site-isolation {
- inset-block-end: auto;
-}
-
-.options-label {
- cursor: pointer;
- pointer-events: none;
-}
-
-.info-icon {
- cursor: pointer;
- block-size: 16px;
- inline-size: 16px;
- inset-block-start: 16px;
- position: absolute;
- inset-inline-end: 20px;
- text-align: center;
- text-decoration: none;
-}
-
-.info-icon-alert::after {
- block-size: 12px;
- inline-size: 12px;
- background-color: var(--alertColor);
- content: "1";
- border-radius: 50%;
- position: absolute;
- inset-block: -5px;
- inset-inline-end: -6px;
- box-shadow: 0 0 1px #00000075;
- font-size: 8px;
- color: white;
- display: flex;
- align-items: center;
- justify-content: center;
- font-weight: bolder;
-}
-
-.delete-warning {
- padding-block-end: 8px;
- padding-block-start: 8px;
- padding-inline-end: 0;
- padding-inline-start: 0;
-}
-
-.trash-button {
- display: inline-block;
- block-size: 20px;
- inline-size: 20px;
- margin-block-end: 4px;
- margin-block-start: 4px;
- margin-inline-end: 10px;
- margin-inline-start: 0;
- text-align: center;
-}
-
-tr > td > .trash-button {
- display: none;
-}
-
-tr:hover > td > .trash-button {
- display: block;
-}
-
-.move-button {
- cursor: move;
- display: inline-block;
-}
-
-.move-button > img {
- block-size: 16px;
- margin-inline-end: 20px;
- margin-inline-start: 8px;
-}
-
-/* ----- Permissions Overlay ---------- */
-
-#advanced-proxy-settings-panel,
-.advanced-proxy-panel-content {
- position: absolute;
- inset-block: 0;
- inset-inline: 0;
-}
-
-.permissions-overlay {
- position: absolute;
- inset-inline: 0 0;
- inset-block-start: 40px;
- inset-block-end: 0;
- justify-content: center;
- align-content: center;
- flex-direction: column;
- background-color: white;
- padding-block: 2.25rem;
- padding-inline: 2.25rem;
- display: none;
-}
-
-#enable-proxy-permissions {
- text-align: center;
- font-family: var(--fontMetropolis);
- font-size: 14px;
- margin-block-start: 1rem;
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --iconCloseX: url("/img/close-light.svg");
- --iconGear: url("/img/gear-icon-light.svg");
- --iconArrowRight: url("/img/arrow-icon-right-light.svg");
- --iconArrowLeft: url("/img/arrow-icon-left-light.svg");
- --iconProxyWarning: url("/img/proxy-warning-light.svg");
- --logoMozillaVpn: url("/img/moz-vpn-logo-light.svg");
- --bgColor: #42404c;
- --title-text-color: #fff;
- --text-normal-color: #f9f9fa;
- --text-heading-color: #fff;
- --primaryCtaDefault: var(--blue40);
- --primaryCtaHover: var(--blue50);
- --primaryCtaActive: var(--blue60);
- --highlight-blue: #52515d;
- --bottomButtons: var(--highlight-blue);
- --controllerHover: var(--highlight-blue);
- --controllerActive: rgb(90, 89, 102);
- --bgDark: #2b2932;
- }
-
- body {
- color: #ffffffd1;
-
- --highlight-blue: #52515d;
- --hr-grey: #38383d;
- --text-grey: #fefffe;
- }
-
- .permissions-overlay {
- background-color: #494755;
- }
-
- .tooltip {
- background-color: var(--controllerActive);
- }
-
- #moz-vpn-tout {
- box-shadow: 0 0 21px 3px #323139;
- }
-
- .moz-vpn-permissions {
- background-color: #322f3e;
- }
-
- .blue-link {
- color: #36abfc;
- }
-
- .blue-link:hover {
- color: var(--blue20);
- }
-
- .drop-shadow {
- box-shadow: 0 0 13px -2px #323139;
- }
-
- .server-radio-control {
- border-color: var(--grey40);
- }
-
- .server-radio-control::after {
- background-color: var(--grey30);
- }
-
- .server-city-list-item:hover .server-radio-control {
- border-color: var(--grey30);
- }
-
- .server-city-list-item:active .server-radio-control {
- border-color: var(--grey20);
- }
-
- .primary-cta:focus {
- box-shadow: 0 0 0 1px #00ddffd6, 0 0 0 3px var(--primaryCtaHover);
- }
-
- .slider {
- background-color: var(--grey30);
- }
-
- input:hover + .slider {
- background-color: var(--grey40);
- }
-
- input:focus + .slider {
- box-shadow: 0 0 0 2px var(--bgColor), 0 0 0 4px var(--grey20);
- }
-
- h3.title {
- color: #fff;
- }
-
- .delete-btn,
- .bottom-btn {
- background-color: var(--bottomButtons);
- box-shadow: 0 0 0 1px #73737300;
- }
-
- .onboarding-title,
- .delete-container-confirm-title {
- color: #ededf0;
- }
-
- input {
- border: solid 1px #737373;
- }
-
- input[type=text] {
- background-color: rgba(43, 41, 50, 0.79) !important;
- }
-
- .delete-container {
- background-color: #4a4a4a;
- }
-
- .delete-btn,
- .cancel-button,
- .grey-button {
- background-color: var(--bottomButtons);
- color: #f9f9fa;
- }
-
- .button.secondary:hover,
- .button.secondary:focus {
- background-color: #676767;
- }
-
- input[type="text"]:focus {
- box-shadow: 0 0 0 3px var(--blue50);
- border-color: var(--blue30);
- }
-
- .trash-button,
- img.menu-icon,
- .menu-icon > img,
- .menu-arrow > img,
- .info-icon > img {
- filter: invert(1);
- }
-
- #edit-sites-assigned .menu-icon,
- #container-info-table .menu-icon {
- filter: invert(0);
- }
-
- .truncate-text::after {
- background: var(--bgColor);
- mask-image: linear-gradient(to right, transparent, var(--bgColor) 70%);
- }
-
- [data-identity-color="grey"] {
- --identity-icon-color: #ededf0;
- }
-
- .radio-choice > .radio-container > [type="radio"]:checked + label {
- background: var(--bgDark);
- }
-
- #edit-container-panel-choose-icon .radio-container:hover .usercontext-icon::before {
- fill: #fff !important;
- }
-}
-
-/* OVERFLOW MENU */
-
-.overflow body,
-.overflow html {
- inline-size: 100%;
-}
-
-.overflow .container-panel {
- min-block-size: 100%;
-}
-
-.overflow .panel.onboarding {
- margin-block: auto;
-}
diff --git a/src/fonts/Inter-Medium.woff2 b/src/fonts/Inter-Medium.woff2
deleted file mode 100644
index 7d0fbe9..0000000
Binary files a/src/fonts/Inter-Medium.woff2 and /dev/null differ
diff --git a/src/fonts/Inter-Regular.woff2 b/src/fonts/Inter-Regular.woff2
deleted file mode 100644
index 554aed6..0000000
Binary files a/src/fonts/Inter-Regular.woff2 and /dev/null differ
diff --git a/src/fonts/Metropolis-Light.woff2 b/src/fonts/Metropolis-Light.woff2
deleted file mode 100755
index d276865..0000000
Binary files a/src/fonts/Metropolis-Light.woff2 and /dev/null differ
diff --git a/src/fonts/Metropolis-Medium.woff2 b/src/fonts/Metropolis-Medium.woff2
deleted file mode 100755
index d5aabb6..0000000
Binary files a/src/fonts/Metropolis-Medium.woff2 and /dev/null differ
diff --git a/src/img/Account.svg b/src/img/Account.svg
deleted file mode 100644
index 27d6146..0000000
--- a/src/img/Account.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
\ No newline at end of file
diff --git a/src/img/Sync.svg b/src/img/Sync.svg
deleted file mode 100644
index d67f786..0000000
--- a/src/img/Sync.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
\ No newline at end of file
diff --git a/src/img/amo-icon.svg b/src/img/amo-icon.svg
deleted file mode 100644
index bafd00e..0000000
--- a/src/img/amo-icon.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
\ No newline at end of file
diff --git a/src/img/arrow-icon-left-light.svg b/src/img/arrow-icon-left-light.svg
deleted file mode 100644
index 5a35ea8..0000000
--- a/src/img/arrow-icon-left-light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/img/arrow-icon-left.svg b/src/img/arrow-icon-left.svg
deleted file mode 100644
index 2060238..0000000
--- a/src/img/arrow-icon-left.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/img/arrow-icon-right-light.svg b/src/img/arrow-icon-right-light.svg
deleted file mode 100644
index d8549ce..0000000
--- a/src/img/arrow-icon-right-light.svg
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
diff --git a/src/img/arrow-icon-right.svg b/src/img/arrow-icon-right.svg
deleted file mode 100644
index ba8df85..0000000
--- a/src/img/arrow-icon-right.svg
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
diff --git a/src/img/arrow-toggle.svg b/src/img/arrow-toggle.svg
deleted file mode 100644
index a16112c..0000000
--- a/src/img/arrow-toggle.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/img/blank-favicon.svg b/src/img/blank-favicon.svg
deleted file mode 100644
index c2393b6..0000000
--- a/src/img/blank-favicon.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
diff --git a/src/img/blank-tab.svg b/src/img/blank-tab.svg
deleted file mode 100644
index bb3d5eb..0000000
--- a/src/img/blank-tab.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
\ No newline at end of file
diff --git a/src/img/close-light.svg b/src/img/close-light.svg
deleted file mode 100644
index a2a88f1..0000000
--- a/src/img/close-light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/img/close.svg b/src/img/close.svg
deleted file mode 100644
index 1a004fc..0000000
--- a/src/img/close.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/img/container-close-tab.svg b/src/img/container-close-tab.svg
deleted file mode 100644
index 4c8aebc..0000000
--- a/src/img/container-close-tab.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
\ No newline at end of file
diff --git a/src/img/container-delete.svg b/src/img/container-delete.svg
deleted file mode 100644
index 1e67c8e..0000000
--- a/src/img/container-delete.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
diff --git a/src/img/container-move.svg b/src/img/container-move.svg
deleted file mode 100644
index ee4fd70..0000000
--- a/src/img/container-move.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
\ No newline at end of file
diff --git a/src/img/container-newtab.svg b/src/img/container-newtab.svg
deleted file mode 100644
index f41e140..0000000
--- a/src/img/container-newtab.svg
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
diff --git a/src/img/container-openin-16.svg b/src/img/container-openin-16.svg
deleted file mode 100644
index 6786b5e..0000000
--- a/src/img/container-openin-16.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
diff --git a/src/img/container-site-d-24.png b/src/img/container-site-d-24.png
deleted file mode 100644
index 3ef2901..0000000
Binary files a/src/img/container-site-d-24.png and /dev/null differ
diff --git a/src/img/container-site-d-48.png b/src/img/container-site-d-48.png
deleted file mode 100644
index beb0bfa..0000000
Binary files a/src/img/container-site-d-48.png and /dev/null differ
diff --git a/src/img/container-site-d-96.png b/src/img/container-site-d-96.png
deleted file mode 100644
index 3c3e032..0000000
Binary files a/src/img/container-site-d-96.png and /dev/null differ
diff --git a/src/img/filters.svg b/src/img/filters.svg
deleted file mode 100644
index 1f377b9..0000000
--- a/src/img/filters.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/src/img/flags/AC.png b/src/img/flags/AC.png
deleted file mode 100644
index 8e7a630..0000000
Binary files a/src/img/flags/AC.png and /dev/null differ
diff --git a/src/img/flags/AD.png b/src/img/flags/AD.png
deleted file mode 100644
index 69972df..0000000
Binary files a/src/img/flags/AD.png and /dev/null differ
diff --git a/src/img/flags/AE.png b/src/img/flags/AE.png
deleted file mode 100644
index 11d2bb6..0000000
Binary files a/src/img/flags/AE.png and /dev/null differ
diff --git a/src/img/flags/AF.png b/src/img/flags/AF.png
deleted file mode 100644
index 7a37ec2..0000000
Binary files a/src/img/flags/AF.png and /dev/null differ
diff --git a/src/img/flags/AG.png b/src/img/flags/AG.png
deleted file mode 100644
index 8602fff..0000000
Binary files a/src/img/flags/AG.png and /dev/null differ
diff --git a/src/img/flags/AI.png b/src/img/flags/AI.png
deleted file mode 100644
index 73f0c20..0000000
Binary files a/src/img/flags/AI.png and /dev/null differ
diff --git a/src/img/flags/AL.png b/src/img/flags/AL.png
deleted file mode 100644
index a6d4573..0000000
Binary files a/src/img/flags/AL.png and /dev/null differ
diff --git a/src/img/flags/AM.png b/src/img/flags/AM.png
deleted file mode 100644
index 16ef38a..0000000
Binary files a/src/img/flags/AM.png and /dev/null differ
diff --git a/src/img/flags/AO.png b/src/img/flags/AO.png
deleted file mode 100644
index 9349525..0000000
Binary files a/src/img/flags/AO.png and /dev/null differ
diff --git a/src/img/flags/AQ.png b/src/img/flags/AQ.png
deleted file mode 100644
index d21e4ba..0000000
Binary files a/src/img/flags/AQ.png and /dev/null differ
diff --git a/src/img/flags/AR.png b/src/img/flags/AR.png
deleted file mode 100644
index a7d8a31..0000000
Binary files a/src/img/flags/AR.png and /dev/null differ
diff --git a/src/img/flags/AS.png b/src/img/flags/AS.png
deleted file mode 100644
index a5cdfcc..0000000
Binary files a/src/img/flags/AS.png and /dev/null differ
diff --git a/src/img/flags/AT.png b/src/img/flags/AT.png
deleted file mode 100644
index 3937efe..0000000
Binary files a/src/img/flags/AT.png and /dev/null differ
diff --git a/src/img/flags/AU.png b/src/img/flags/AU.png
deleted file mode 100644
index 84a5180..0000000
Binary files a/src/img/flags/AU.png and /dev/null differ
diff --git a/src/img/flags/AW.png b/src/img/flags/AW.png
deleted file mode 100644
index e250be9..0000000
Binary files a/src/img/flags/AW.png and /dev/null differ
diff --git a/src/img/flags/AX.png b/src/img/flags/AX.png
deleted file mode 100644
index a5444fc..0000000
Binary files a/src/img/flags/AX.png and /dev/null differ
diff --git a/src/img/flags/AZ.png b/src/img/flags/AZ.png
deleted file mode 100644
index 4f0c1c8..0000000
Binary files a/src/img/flags/AZ.png and /dev/null differ
diff --git a/src/img/flags/BA.png b/src/img/flags/BA.png
deleted file mode 100644
index 3ffe114..0000000
Binary files a/src/img/flags/BA.png and /dev/null differ
diff --git a/src/img/flags/BB.png b/src/img/flags/BB.png
deleted file mode 100644
index e6fb3ed..0000000
Binary files a/src/img/flags/BB.png and /dev/null differ
diff --git a/src/img/flags/BD.png b/src/img/flags/BD.png
deleted file mode 100644
index c1c933e..0000000
Binary files a/src/img/flags/BD.png and /dev/null differ
diff --git a/src/img/flags/BE.png b/src/img/flags/BE.png
deleted file mode 100644
index d309207..0000000
Binary files a/src/img/flags/BE.png and /dev/null differ
diff --git a/src/img/flags/BF.png b/src/img/flags/BF.png
deleted file mode 100644
index 29064ff..0000000
Binary files a/src/img/flags/BF.png and /dev/null differ
diff --git a/src/img/flags/BG.png b/src/img/flags/BG.png
deleted file mode 100644
index 0519f96..0000000
Binary files a/src/img/flags/BG.png and /dev/null differ
diff --git a/src/img/flags/BH.png b/src/img/flags/BH.png
deleted file mode 100644
index 28c3ec6..0000000
Binary files a/src/img/flags/BH.png and /dev/null differ
diff --git a/src/img/flags/BI.png b/src/img/flags/BI.png
deleted file mode 100644
index 7133116..0000000
Binary files a/src/img/flags/BI.png and /dev/null differ
diff --git a/src/img/flags/BJ.png b/src/img/flags/BJ.png
deleted file mode 100644
index 3ac1d99..0000000
Binary files a/src/img/flags/BJ.png and /dev/null differ
diff --git a/src/img/flags/BL.png b/src/img/flags/BL.png
deleted file mode 100644
index aa9bb06..0000000
Binary files a/src/img/flags/BL.png and /dev/null differ
diff --git a/src/img/flags/BM.png b/src/img/flags/BM.png
deleted file mode 100644
index 1353129..0000000
Binary files a/src/img/flags/BM.png and /dev/null differ
diff --git a/src/img/flags/BN.png b/src/img/flags/BN.png
deleted file mode 100644
index f861221..0000000
Binary files a/src/img/flags/BN.png and /dev/null differ
diff --git a/src/img/flags/BO.png b/src/img/flags/BO.png
deleted file mode 100644
index 21c555b..0000000
Binary files a/src/img/flags/BO.png and /dev/null differ
diff --git a/src/img/flags/BQ.png b/src/img/flags/BQ.png
deleted file mode 100644
index be922b0..0000000
Binary files a/src/img/flags/BQ.png and /dev/null differ
diff --git a/src/img/flags/BR.png b/src/img/flags/BR.png
deleted file mode 100644
index 642f3a5..0000000
Binary files a/src/img/flags/BR.png and /dev/null differ
diff --git a/src/img/flags/BS.png b/src/img/flags/BS.png
deleted file mode 100644
index f959101..0000000
Binary files a/src/img/flags/BS.png and /dev/null differ
diff --git a/src/img/flags/BT.png b/src/img/flags/BT.png
deleted file mode 100644
index ee668fb..0000000
Binary files a/src/img/flags/BT.png and /dev/null differ
diff --git a/src/img/flags/BV.png b/src/img/flags/BV.png
deleted file mode 100644
index d3a19fd..0000000
Binary files a/src/img/flags/BV.png and /dev/null differ
diff --git a/src/img/flags/BW.png b/src/img/flags/BW.png
deleted file mode 100644
index 4aeeb13..0000000
Binary files a/src/img/flags/BW.png and /dev/null differ
diff --git a/src/img/flags/BY.png b/src/img/flags/BY.png
deleted file mode 100644
index fc2051b..0000000
Binary files a/src/img/flags/BY.png and /dev/null differ
diff --git a/src/img/flags/BZ.png b/src/img/flags/BZ.png
deleted file mode 100644
index 0c23f80..0000000
Binary files a/src/img/flags/BZ.png and /dev/null differ
diff --git a/src/img/flags/CA.png b/src/img/flags/CA.png
deleted file mode 100644
index fe777da..0000000
Binary files a/src/img/flags/CA.png and /dev/null differ
diff --git a/src/img/flags/CC.png b/src/img/flags/CC.png
deleted file mode 100644
index f232d71..0000000
Binary files a/src/img/flags/CC.png and /dev/null differ
diff --git a/src/img/flags/CD.png b/src/img/flags/CD.png
deleted file mode 100644
index f0855b2..0000000
Binary files a/src/img/flags/CD.png and /dev/null differ
diff --git a/src/img/flags/CF.png b/src/img/flags/CF.png
deleted file mode 100644
index 18a6891..0000000
Binary files a/src/img/flags/CF.png and /dev/null differ
diff --git a/src/img/flags/CG.png b/src/img/flags/CG.png
deleted file mode 100644
index 113face..0000000
Binary files a/src/img/flags/CG.png and /dev/null differ
diff --git a/src/img/flags/CH.png b/src/img/flags/CH.png
deleted file mode 100644
index bc50b99..0000000
Binary files a/src/img/flags/CH.png and /dev/null differ
diff --git a/src/img/flags/CI.png b/src/img/flags/CI.png
deleted file mode 100644
index e19e84b..0000000
Binary files a/src/img/flags/CI.png and /dev/null differ
diff --git a/src/img/flags/CK.png b/src/img/flags/CK.png
deleted file mode 100644
index e5ca391..0000000
Binary files a/src/img/flags/CK.png and /dev/null differ
diff --git a/src/img/flags/CL.png b/src/img/flags/CL.png
deleted file mode 100644
index d4d91d0..0000000
Binary files a/src/img/flags/CL.png and /dev/null differ
diff --git a/src/img/flags/CM.png b/src/img/flags/CM.png
deleted file mode 100644
index 457a061..0000000
Binary files a/src/img/flags/CM.png and /dev/null differ
diff --git a/src/img/flags/CN.png b/src/img/flags/CN.png
deleted file mode 100644
index dcc0fb1..0000000
Binary files a/src/img/flags/CN.png and /dev/null differ
diff --git a/src/img/flags/CO.png b/src/img/flags/CO.png
deleted file mode 100644
index 0d2cf35..0000000
Binary files a/src/img/flags/CO.png and /dev/null differ
diff --git a/src/img/flags/CP.png b/src/img/flags/CP.png
deleted file mode 100644
index 662adc7..0000000
Binary files a/src/img/flags/CP.png and /dev/null differ
diff --git a/src/img/flags/CR.png b/src/img/flags/CR.png
deleted file mode 100644
index 69b41ac..0000000
Binary files a/src/img/flags/CR.png and /dev/null differ
diff --git a/src/img/flags/CU.png b/src/img/flags/CU.png
deleted file mode 100644
index 00e3299..0000000
Binary files a/src/img/flags/CU.png and /dev/null differ
diff --git a/src/img/flags/CV.png b/src/img/flags/CV.png
deleted file mode 100644
index 88ac363..0000000
Binary files a/src/img/flags/CV.png and /dev/null differ
diff --git a/src/img/flags/CW.png b/src/img/flags/CW.png
deleted file mode 100644
index 83a5a7d..0000000
Binary files a/src/img/flags/CW.png and /dev/null differ
diff --git a/src/img/flags/CX.png b/src/img/flags/CX.png
deleted file mode 100644
index b6f3608..0000000
Binary files a/src/img/flags/CX.png and /dev/null differ
diff --git a/src/img/flags/CY.png b/src/img/flags/CY.png
deleted file mode 100644
index 5c333b0..0000000
Binary files a/src/img/flags/CY.png and /dev/null differ
diff --git a/src/img/flags/CZ.png b/src/img/flags/CZ.png
deleted file mode 100644
index ecad9fd..0000000
Binary files a/src/img/flags/CZ.png and /dev/null differ
diff --git a/src/img/flags/DE.png b/src/img/flags/DE.png
deleted file mode 100644
index 2933ab8..0000000
Binary files a/src/img/flags/DE.png and /dev/null differ
diff --git a/src/img/flags/DG.png b/src/img/flags/DG.png
deleted file mode 100644
index aee82ba..0000000
Binary files a/src/img/flags/DG.png and /dev/null differ
diff --git a/src/img/flags/DJ.png b/src/img/flags/DJ.png
deleted file mode 100644
index 24602c1..0000000
Binary files a/src/img/flags/DJ.png and /dev/null differ
diff --git a/src/img/flags/DK.png b/src/img/flags/DK.png
deleted file mode 100644
index 095ff86..0000000
Binary files a/src/img/flags/DK.png and /dev/null differ
diff --git a/src/img/flags/DM.png b/src/img/flags/DM.png
deleted file mode 100644
index 05f78c9..0000000
Binary files a/src/img/flags/DM.png and /dev/null differ
diff --git a/src/img/flags/DO.png b/src/img/flags/DO.png
deleted file mode 100644
index 4cb2f4a..0000000
Binary files a/src/img/flags/DO.png and /dev/null differ
diff --git a/src/img/flags/DZ.png b/src/img/flags/DZ.png
deleted file mode 100644
index 8bdb882..0000000
Binary files a/src/img/flags/DZ.png and /dev/null differ
diff --git a/src/img/flags/EA.png b/src/img/flags/EA.png
deleted file mode 100644
index fddf21a..0000000
Binary files a/src/img/flags/EA.png and /dev/null differ
diff --git a/src/img/flags/EC.png b/src/img/flags/EC.png
deleted file mode 100644
index 05b0a2a..0000000
Binary files a/src/img/flags/EC.png and /dev/null differ
diff --git a/src/img/flags/EE.png b/src/img/flags/EE.png
deleted file mode 100644
index 94d8d46..0000000
Binary files a/src/img/flags/EE.png and /dev/null differ
diff --git a/src/img/flags/EG.png b/src/img/flags/EG.png
deleted file mode 100644
index 29dfd5b..0000000
Binary files a/src/img/flags/EG.png and /dev/null differ
diff --git a/src/img/flags/EH.png b/src/img/flags/EH.png
deleted file mode 100644
index 29d0791..0000000
Binary files a/src/img/flags/EH.png and /dev/null differ
diff --git a/src/img/flags/ER.png b/src/img/flags/ER.png
deleted file mode 100644
index fd25d0e..0000000
Binary files a/src/img/flags/ER.png and /dev/null differ
diff --git a/src/img/flags/ES.png b/src/img/flags/ES.png
deleted file mode 100644
index 5c449da..0000000
Binary files a/src/img/flags/ES.png and /dev/null differ
diff --git a/src/img/flags/ET.png b/src/img/flags/ET.png
deleted file mode 100644
index 4f4e7fc..0000000
Binary files a/src/img/flags/ET.png and /dev/null differ
diff --git a/src/img/flags/EU.png b/src/img/flags/EU.png
deleted file mode 100644
index 4f0b5d7..0000000
Binary files a/src/img/flags/EU.png and /dev/null differ
diff --git a/src/img/flags/FI.png b/src/img/flags/FI.png
deleted file mode 100644
index 34096f2..0000000
Binary files a/src/img/flags/FI.png and /dev/null differ
diff --git a/src/img/flags/FJ.png b/src/img/flags/FJ.png
deleted file mode 100644
index 3f4c0e0..0000000
Binary files a/src/img/flags/FJ.png and /dev/null differ
diff --git a/src/img/flags/FK.png b/src/img/flags/FK.png
deleted file mode 100644
index 5b2ecff..0000000
Binary files a/src/img/flags/FK.png and /dev/null differ
diff --git a/src/img/flags/FM.png b/src/img/flags/FM.png
deleted file mode 100644
index 0fc25e8..0000000
Binary files a/src/img/flags/FM.png and /dev/null differ
diff --git a/src/img/flags/FO.png b/src/img/flags/FO.png
deleted file mode 100644
index 7f8e443..0000000
Binary files a/src/img/flags/FO.png and /dev/null differ
diff --git a/src/img/flags/FR.png b/src/img/flags/FR.png
deleted file mode 100644
index 662adc7..0000000
Binary files a/src/img/flags/FR.png and /dev/null differ
diff --git a/src/img/flags/GA.png b/src/img/flags/GA.png
deleted file mode 100644
index 6f73a37..0000000
Binary files a/src/img/flags/GA.png and /dev/null differ
diff --git a/src/img/flags/GB.png b/src/img/flags/GB.png
deleted file mode 100644
index af56765..0000000
Binary files a/src/img/flags/GB.png and /dev/null differ
diff --git a/src/img/flags/GD.png b/src/img/flags/GD.png
deleted file mode 100644
index 66f160e..0000000
Binary files a/src/img/flags/GD.png and /dev/null differ
diff --git a/src/img/flags/GE.png b/src/img/flags/GE.png
deleted file mode 100644
index 31af128..0000000
Binary files a/src/img/flags/GE.png and /dev/null differ
diff --git a/src/img/flags/GF.png b/src/img/flags/GF.png
deleted file mode 100644
index 99ff31b..0000000
Binary files a/src/img/flags/GF.png and /dev/null differ
diff --git a/src/img/flags/GG.png b/src/img/flags/GG.png
deleted file mode 100644
index 72ac329..0000000
Binary files a/src/img/flags/GG.png and /dev/null differ
diff --git a/src/img/flags/GH.png b/src/img/flags/GH.png
deleted file mode 100644
index 2b19dbd..0000000
Binary files a/src/img/flags/GH.png and /dev/null differ
diff --git a/src/img/flags/GI.png b/src/img/flags/GI.png
deleted file mode 100644
index 26d4ef7..0000000
Binary files a/src/img/flags/GI.png and /dev/null differ
diff --git a/src/img/flags/GL.png b/src/img/flags/GL.png
deleted file mode 100644
index 5df66b3..0000000
Binary files a/src/img/flags/GL.png and /dev/null differ
diff --git a/src/img/flags/GM.png b/src/img/flags/GM.png
deleted file mode 100644
index 4d1a3b4..0000000
Binary files a/src/img/flags/GM.png and /dev/null differ
diff --git a/src/img/flags/GN.png b/src/img/flags/GN.png
deleted file mode 100644
index 813c3ca..0000000
Binary files a/src/img/flags/GN.png and /dev/null differ
diff --git a/src/img/flags/GP.png b/src/img/flags/GP.png
deleted file mode 100644
index 97a3dd9..0000000
Binary files a/src/img/flags/GP.png and /dev/null differ
diff --git a/src/img/flags/GQ.png b/src/img/flags/GQ.png
deleted file mode 100644
index 02b7aec..0000000
Binary files a/src/img/flags/GQ.png and /dev/null differ
diff --git a/src/img/flags/GR.png b/src/img/flags/GR.png
deleted file mode 100644
index 0478442..0000000
Binary files a/src/img/flags/GR.png and /dev/null differ
diff --git a/src/img/flags/GS.png b/src/img/flags/GS.png
deleted file mode 100644
index da4bbff..0000000
Binary files a/src/img/flags/GS.png and /dev/null differ
diff --git a/src/img/flags/GT.png b/src/img/flags/GT.png
deleted file mode 100644
index 23cec33..0000000
Binary files a/src/img/flags/GT.png and /dev/null differ
diff --git a/src/img/flags/GU.png b/src/img/flags/GU.png
deleted file mode 100644
index 75c0945..0000000
Binary files a/src/img/flags/GU.png and /dev/null differ
diff --git a/src/img/flags/GW.png b/src/img/flags/GW.png
deleted file mode 100644
index cf8c941..0000000
Binary files a/src/img/flags/GW.png and /dev/null differ
diff --git a/src/img/flags/GY.png b/src/img/flags/GY.png
deleted file mode 100644
index 2e102fd..0000000
Binary files a/src/img/flags/GY.png and /dev/null differ
diff --git a/src/img/flags/HK.png b/src/img/flags/HK.png
deleted file mode 100644
index 412b684..0000000
Binary files a/src/img/flags/HK.png and /dev/null differ
diff --git a/src/img/flags/HM.png b/src/img/flags/HM.png
deleted file mode 100644
index 9972434..0000000
Binary files a/src/img/flags/HM.png and /dev/null differ
diff --git a/src/img/flags/HN.png b/src/img/flags/HN.png
deleted file mode 100644
index e161777..0000000
Binary files a/src/img/flags/HN.png and /dev/null differ
diff --git a/src/img/flags/HR.png b/src/img/flags/HR.png
deleted file mode 100644
index 2dafc9a..0000000
Binary files a/src/img/flags/HR.png and /dev/null differ
diff --git a/src/img/flags/HT.png b/src/img/flags/HT.png
deleted file mode 100644
index afbf028..0000000
Binary files a/src/img/flags/HT.png and /dev/null differ
diff --git a/src/img/flags/HU.png b/src/img/flags/HU.png
deleted file mode 100644
index b2f43f7..0000000
Binary files a/src/img/flags/HU.png and /dev/null differ
diff --git a/src/img/flags/IC.png b/src/img/flags/IC.png
deleted file mode 100644
index 44cb511..0000000
Binary files a/src/img/flags/IC.png and /dev/null differ
diff --git a/src/img/flags/ID.png b/src/img/flags/ID.png
deleted file mode 100644
index 4e8df64..0000000
Binary files a/src/img/flags/ID.png and /dev/null differ
diff --git a/src/img/flags/IE.png b/src/img/flags/IE.png
deleted file mode 100644
index bbbeada..0000000
Binary files a/src/img/flags/IE.png and /dev/null differ
diff --git a/src/img/flags/IL.png b/src/img/flags/IL.png
deleted file mode 100644
index 6c390c3..0000000
Binary files a/src/img/flags/IL.png and /dev/null differ
diff --git a/src/img/flags/IM.png b/src/img/flags/IM.png
deleted file mode 100644
index 99304fc..0000000
Binary files a/src/img/flags/IM.png and /dev/null differ
diff --git a/src/img/flags/IN.png b/src/img/flags/IN.png
deleted file mode 100644
index fcacf4c..0000000
Binary files a/src/img/flags/IN.png and /dev/null differ
diff --git a/src/img/flags/IO.png b/src/img/flags/IO.png
deleted file mode 100644
index 7ef54ae..0000000
Binary files a/src/img/flags/IO.png and /dev/null differ
diff --git a/src/img/flags/IQ.png b/src/img/flags/IQ.png
deleted file mode 100644
index 81de697..0000000
Binary files a/src/img/flags/IQ.png and /dev/null differ
diff --git a/src/img/flags/IR.png b/src/img/flags/IR.png
deleted file mode 100644
index 5147905..0000000
Binary files a/src/img/flags/IR.png and /dev/null differ
diff --git a/src/img/flags/IS.png b/src/img/flags/IS.png
deleted file mode 100644
index 349e9ef..0000000
Binary files a/src/img/flags/IS.png and /dev/null differ
diff --git a/src/img/flags/IT.png b/src/img/flags/IT.png
deleted file mode 100644
index 2cd1da7..0000000
Binary files a/src/img/flags/IT.png and /dev/null differ
diff --git a/src/img/flags/JE.png b/src/img/flags/JE.png
deleted file mode 100644
index bc974c1..0000000
Binary files a/src/img/flags/JE.png and /dev/null differ
diff --git a/src/img/flags/JM.png b/src/img/flags/JM.png
deleted file mode 100644
index 2a48a73..0000000
Binary files a/src/img/flags/JM.png and /dev/null differ
diff --git a/src/img/flags/JO.png b/src/img/flags/JO.png
deleted file mode 100644
index 5161701..0000000
Binary files a/src/img/flags/JO.png and /dev/null differ
diff --git a/src/img/flags/JP.png b/src/img/flags/JP.png
deleted file mode 100644
index dd515aa..0000000
Binary files a/src/img/flags/JP.png and /dev/null differ
diff --git a/src/img/flags/KE.png b/src/img/flags/KE.png
deleted file mode 100644
index 468ab95..0000000
Binary files a/src/img/flags/KE.png and /dev/null differ
diff --git a/src/img/flags/KG.png b/src/img/flags/KG.png
deleted file mode 100644
index 58f07a7..0000000
Binary files a/src/img/flags/KG.png and /dev/null differ
diff --git a/src/img/flags/KH.png b/src/img/flags/KH.png
deleted file mode 100644
index cf9c1a6..0000000
Binary files a/src/img/flags/KH.png and /dev/null differ
diff --git a/src/img/flags/KI.png b/src/img/flags/KI.png
deleted file mode 100644
index ee10d01..0000000
Binary files a/src/img/flags/KI.png and /dev/null differ
diff --git a/src/img/flags/KM.png b/src/img/flags/KM.png
deleted file mode 100644
index 1035f14..0000000
Binary files a/src/img/flags/KM.png and /dev/null differ
diff --git a/src/img/flags/KN.png b/src/img/flags/KN.png
deleted file mode 100644
index e021417..0000000
Binary files a/src/img/flags/KN.png and /dev/null differ
diff --git a/src/img/flags/KP.png b/src/img/flags/KP.png
deleted file mode 100644
index 7e05f75..0000000
Binary files a/src/img/flags/KP.png and /dev/null differ
diff --git a/src/img/flags/KR.png b/src/img/flags/KR.png
deleted file mode 100644
index 8ff7ccd..0000000
Binary files a/src/img/flags/KR.png and /dev/null differ
diff --git a/src/img/flags/KW.png b/src/img/flags/KW.png
deleted file mode 100644
index 76f53c2..0000000
Binary files a/src/img/flags/KW.png and /dev/null differ
diff --git a/src/img/flags/KY.png b/src/img/flags/KY.png
deleted file mode 100644
index 158d589..0000000
Binary files a/src/img/flags/KY.png and /dev/null differ
diff --git a/src/img/flags/KZ.png b/src/img/flags/KZ.png
deleted file mode 100644
index d7e3401..0000000
Binary files a/src/img/flags/KZ.png and /dev/null differ
diff --git a/src/img/flags/LA.png b/src/img/flags/LA.png
deleted file mode 100644
index 44f5e8d..0000000
Binary files a/src/img/flags/LA.png and /dev/null differ
diff --git a/src/img/flags/LB.png b/src/img/flags/LB.png
deleted file mode 100644
index f506228..0000000
Binary files a/src/img/flags/LB.png and /dev/null differ
diff --git a/src/img/flags/LC.png b/src/img/flags/LC.png
deleted file mode 100644
index 1b7e53a..0000000
Binary files a/src/img/flags/LC.png and /dev/null differ
diff --git a/src/img/flags/LI.png b/src/img/flags/LI.png
deleted file mode 100644
index 9a69e9a..0000000
Binary files a/src/img/flags/LI.png and /dev/null differ
diff --git a/src/img/flags/LK.png b/src/img/flags/LK.png
deleted file mode 100644
index 8f8d0b3..0000000
Binary files a/src/img/flags/LK.png and /dev/null differ
diff --git a/src/img/flags/LR.png b/src/img/flags/LR.png
deleted file mode 100644
index 8ed6a5a..0000000
Binary files a/src/img/flags/LR.png and /dev/null differ
diff --git a/src/img/flags/LS.png b/src/img/flags/LS.png
deleted file mode 100644
index 051b53a..0000000
Binary files a/src/img/flags/LS.png and /dev/null differ
diff --git a/src/img/flags/LT.png b/src/img/flags/LT.png
deleted file mode 100644
index 69de64e..0000000
Binary files a/src/img/flags/LT.png and /dev/null differ
diff --git a/src/img/flags/LU.png b/src/img/flags/LU.png
deleted file mode 100644
index d7a507d..0000000
Binary files a/src/img/flags/LU.png and /dev/null differ
diff --git a/src/img/flags/LV.png b/src/img/flags/LV.png
deleted file mode 100644
index 5505550..0000000
Binary files a/src/img/flags/LV.png and /dev/null differ
diff --git a/src/img/flags/LY.png b/src/img/flags/LY.png
deleted file mode 100644
index 426e931..0000000
Binary files a/src/img/flags/LY.png and /dev/null differ
diff --git a/src/img/flags/MA.png b/src/img/flags/MA.png
deleted file mode 100644
index 49d5f2f..0000000
Binary files a/src/img/flags/MA.png and /dev/null differ
diff --git a/src/img/flags/MC.png b/src/img/flags/MC.png
deleted file mode 100644
index cc71565..0000000
Binary files a/src/img/flags/MC.png and /dev/null differ
diff --git a/src/img/flags/MD.png b/src/img/flags/MD.png
deleted file mode 100644
index 72ab452..0000000
Binary files a/src/img/flags/MD.png and /dev/null differ
diff --git a/src/img/flags/ME.png b/src/img/flags/ME.png
deleted file mode 100644
index ed5d0c4..0000000
Binary files a/src/img/flags/ME.png and /dev/null differ
diff --git a/src/img/flags/MF.png b/src/img/flags/MF.png
deleted file mode 100644
index 662adc7..0000000
Binary files a/src/img/flags/MF.png and /dev/null differ
diff --git a/src/img/flags/MG.png b/src/img/flags/MG.png
deleted file mode 100644
index 460baba..0000000
Binary files a/src/img/flags/MG.png and /dev/null differ
diff --git a/src/img/flags/MH.png b/src/img/flags/MH.png
deleted file mode 100644
index 6b69a8d..0000000
Binary files a/src/img/flags/MH.png and /dev/null differ
diff --git a/src/img/flags/MK.png b/src/img/flags/MK.png
deleted file mode 100644
index 9db397c..0000000
Binary files a/src/img/flags/MK.png and /dev/null differ
diff --git a/src/img/flags/ML.png b/src/img/flags/ML.png
deleted file mode 100644
index d7db739..0000000
Binary files a/src/img/flags/ML.png and /dev/null differ
diff --git a/src/img/flags/MM.png b/src/img/flags/MM.png
deleted file mode 100644
index d4b642c..0000000
Binary files a/src/img/flags/MM.png and /dev/null differ
diff --git a/src/img/flags/MN.png b/src/img/flags/MN.png
deleted file mode 100644
index 7f2225c..0000000
Binary files a/src/img/flags/MN.png and /dev/null differ
diff --git a/src/img/flags/MO.png b/src/img/flags/MO.png
deleted file mode 100644
index 2fe5c8c..0000000
Binary files a/src/img/flags/MO.png and /dev/null differ
diff --git a/src/img/flags/MP.png b/src/img/flags/MP.png
deleted file mode 100644
index c9d6d57..0000000
Binary files a/src/img/flags/MP.png and /dev/null differ
diff --git a/src/img/flags/MQ.png b/src/img/flags/MQ.png
deleted file mode 100644
index aa46f11..0000000
Binary files a/src/img/flags/MQ.png and /dev/null differ
diff --git a/src/img/flags/MR.png b/src/img/flags/MR.png
deleted file mode 100644
index e976797..0000000
Binary files a/src/img/flags/MR.png and /dev/null differ
diff --git a/src/img/flags/MS.png b/src/img/flags/MS.png
deleted file mode 100644
index fd6b759..0000000
Binary files a/src/img/flags/MS.png and /dev/null differ
diff --git a/src/img/flags/MT.png b/src/img/flags/MT.png
deleted file mode 100644
index 9265b06..0000000
Binary files a/src/img/flags/MT.png and /dev/null differ
diff --git a/src/img/flags/MU.png b/src/img/flags/MU.png
deleted file mode 100644
index b48e01b..0000000
Binary files a/src/img/flags/MU.png and /dev/null differ
diff --git a/src/img/flags/MV.png b/src/img/flags/MV.png
deleted file mode 100644
index c666d7a..0000000
Binary files a/src/img/flags/MV.png and /dev/null differ
diff --git a/src/img/flags/MW.png b/src/img/flags/MW.png
deleted file mode 100644
index e58fe38..0000000
Binary files a/src/img/flags/MW.png and /dev/null differ
diff --git a/src/img/flags/MX.png b/src/img/flags/MX.png
deleted file mode 100644
index 61775bf..0000000
Binary files a/src/img/flags/MX.png and /dev/null differ
diff --git a/src/img/flags/MY.png b/src/img/flags/MY.png
deleted file mode 100644
index 108dd99..0000000
Binary files a/src/img/flags/MY.png and /dev/null differ
diff --git a/src/img/flags/MZ.png b/src/img/flags/MZ.png
deleted file mode 100644
index 1a33e0c..0000000
Binary files a/src/img/flags/MZ.png and /dev/null differ
diff --git a/src/img/flags/NA.png b/src/img/flags/NA.png
deleted file mode 100644
index 860139d..0000000
Binary files a/src/img/flags/NA.png and /dev/null differ
diff --git a/src/img/flags/NC.png b/src/img/flags/NC.png
deleted file mode 100644
index 81fa763..0000000
Binary files a/src/img/flags/NC.png and /dev/null differ
diff --git a/src/img/flags/NE.png b/src/img/flags/NE.png
deleted file mode 100644
index 1f1dc36..0000000
Binary files a/src/img/flags/NE.png and /dev/null differ
diff --git a/src/img/flags/NF.png b/src/img/flags/NF.png
deleted file mode 100644
index 599da68..0000000
Binary files a/src/img/flags/NF.png and /dev/null differ
diff --git a/src/img/flags/NG.png b/src/img/flags/NG.png
deleted file mode 100644
index 6cbb424..0000000
Binary files a/src/img/flags/NG.png and /dev/null differ
diff --git a/src/img/flags/NI.png b/src/img/flags/NI.png
deleted file mode 100644
index 3f14038..0000000
Binary files a/src/img/flags/NI.png and /dev/null differ
diff --git a/src/img/flags/NL.png b/src/img/flags/NL.png
deleted file mode 100644
index 619bf8c..0000000
Binary files a/src/img/flags/NL.png and /dev/null differ
diff --git a/src/img/flags/NO.png b/src/img/flags/NO.png
deleted file mode 100644
index d758735..0000000
Binary files a/src/img/flags/NO.png and /dev/null differ
diff --git a/src/img/flags/NP.png b/src/img/flags/NP.png
deleted file mode 100644
index 48acf5f..0000000
Binary files a/src/img/flags/NP.png and /dev/null differ
diff --git a/src/img/flags/NR.png b/src/img/flags/NR.png
deleted file mode 100644
index 1748da9..0000000
Binary files a/src/img/flags/NR.png and /dev/null differ
diff --git a/src/img/flags/NU.png b/src/img/flags/NU.png
deleted file mode 100644
index 9bd5ec1..0000000
Binary files a/src/img/flags/NU.png and /dev/null differ
diff --git a/src/img/flags/NZ.png b/src/img/flags/NZ.png
deleted file mode 100644
index 0d193b3..0000000
Binary files a/src/img/flags/NZ.png and /dev/null differ
diff --git a/src/img/flags/OM.png b/src/img/flags/OM.png
deleted file mode 100644
index 41176aa..0000000
Binary files a/src/img/flags/OM.png and /dev/null differ
diff --git a/src/img/flags/PA.png b/src/img/flags/PA.png
deleted file mode 100644
index 6174229..0000000
Binary files a/src/img/flags/PA.png and /dev/null differ
diff --git a/src/img/flags/PE.png b/src/img/flags/PE.png
deleted file mode 100644
index f7e5409..0000000
Binary files a/src/img/flags/PE.png and /dev/null differ
diff --git a/src/img/flags/PF.png b/src/img/flags/PF.png
deleted file mode 100644
index f89b755..0000000
Binary files a/src/img/flags/PF.png and /dev/null differ
diff --git a/src/img/flags/PG.png b/src/img/flags/PG.png
deleted file mode 100644
index d75762c..0000000
Binary files a/src/img/flags/PG.png and /dev/null differ
diff --git a/src/img/flags/PH.png b/src/img/flags/PH.png
deleted file mode 100644
index a0adbef..0000000
Binary files a/src/img/flags/PH.png and /dev/null differ
diff --git a/src/img/flags/PK.png b/src/img/flags/PK.png
deleted file mode 100644
index 311ca9a..0000000
Binary files a/src/img/flags/PK.png and /dev/null differ
diff --git a/src/img/flags/PL.png b/src/img/flags/PL.png
deleted file mode 100644
index 8d6921d..0000000
Binary files a/src/img/flags/PL.png and /dev/null differ
diff --git a/src/img/flags/PM.png b/src/img/flags/PM.png
deleted file mode 100644
index 357a863..0000000
Binary files a/src/img/flags/PM.png and /dev/null differ
diff --git a/src/img/flags/PN.png b/src/img/flags/PN.png
deleted file mode 100644
index a7ced0f..0000000
Binary files a/src/img/flags/PN.png and /dev/null differ
diff --git a/src/img/flags/PR.png b/src/img/flags/PR.png
deleted file mode 100644
index 867df29..0000000
Binary files a/src/img/flags/PR.png and /dev/null differ
diff --git a/src/img/flags/PS.png b/src/img/flags/PS.png
deleted file mode 100644
index 9756c04..0000000
Binary files a/src/img/flags/PS.png and /dev/null differ
diff --git a/src/img/flags/PT.png b/src/img/flags/PT.png
deleted file mode 100644
index 246e2ce..0000000
Binary files a/src/img/flags/PT.png and /dev/null differ
diff --git a/src/img/flags/PW.png b/src/img/flags/PW.png
deleted file mode 100644
index 8a7aea6..0000000
Binary files a/src/img/flags/PW.png and /dev/null differ
diff --git a/src/img/flags/PY.png b/src/img/flags/PY.png
deleted file mode 100644
index 8bc9da3..0000000
Binary files a/src/img/flags/PY.png and /dev/null differ
diff --git a/src/img/flags/QA.png b/src/img/flags/QA.png
deleted file mode 100644
index 3630990..0000000
Binary files a/src/img/flags/QA.png and /dev/null differ
diff --git a/src/img/flags/RE.png b/src/img/flags/RE.png
deleted file mode 100644
index 8010d88..0000000
Binary files a/src/img/flags/RE.png and /dev/null differ
diff --git a/src/img/flags/RO.png b/src/img/flags/RO.png
deleted file mode 100644
index ce8f8e8..0000000
Binary files a/src/img/flags/RO.png and /dev/null differ
diff --git a/src/img/flags/RS.png b/src/img/flags/RS.png
deleted file mode 100644
index 0485b86..0000000
Binary files a/src/img/flags/RS.png and /dev/null differ
diff --git a/src/img/flags/RU.png b/src/img/flags/RU.png
deleted file mode 100644
index 9af1cb8..0000000
Binary files a/src/img/flags/RU.png and /dev/null differ
diff --git a/src/img/flags/RW.png b/src/img/flags/RW.png
deleted file mode 100644
index 2802d1a..0000000
Binary files a/src/img/flags/RW.png and /dev/null differ
diff --git a/src/img/flags/SA.png b/src/img/flags/SA.png
deleted file mode 100644
index 5f90f43..0000000
Binary files a/src/img/flags/SA.png and /dev/null differ
diff --git a/src/img/flags/SB.png b/src/img/flags/SB.png
deleted file mode 100644
index 385dbaa..0000000
Binary files a/src/img/flags/SB.png and /dev/null differ
diff --git a/src/img/flags/SC.png b/src/img/flags/SC.png
deleted file mode 100644
index 908f6b8..0000000
Binary files a/src/img/flags/SC.png and /dev/null differ
diff --git a/src/img/flags/SD.png b/src/img/flags/SD.png
deleted file mode 100644
index 5ef2fd9..0000000
Binary files a/src/img/flags/SD.png and /dev/null differ
diff --git a/src/img/flags/SE.png b/src/img/flags/SE.png
deleted file mode 100644
index 2cf2258..0000000
Binary files a/src/img/flags/SE.png and /dev/null differ
diff --git a/src/img/flags/SG.png b/src/img/flags/SG.png
deleted file mode 100644
index 454b82a..0000000
Binary files a/src/img/flags/SG.png and /dev/null differ
diff --git a/src/img/flags/SH.png b/src/img/flags/SH.png
deleted file mode 100644
index 2d0df51..0000000
Binary files a/src/img/flags/SH.png and /dev/null differ
diff --git a/src/img/flags/SI.png b/src/img/flags/SI.png
deleted file mode 100644
index 86bffaf..0000000
Binary files a/src/img/flags/SI.png and /dev/null differ
diff --git a/src/img/flags/SJ.png b/src/img/flags/SJ.png
deleted file mode 100644
index d758735..0000000
Binary files a/src/img/flags/SJ.png and /dev/null differ
diff --git a/src/img/flags/SK.png b/src/img/flags/SK.png
deleted file mode 100644
index 276c387..0000000
Binary files a/src/img/flags/SK.png and /dev/null differ
diff --git a/src/img/flags/SL.png b/src/img/flags/SL.png
deleted file mode 100644
index de88763..0000000
Binary files a/src/img/flags/SL.png and /dev/null differ
diff --git a/src/img/flags/SM.png b/src/img/flags/SM.png
deleted file mode 100644
index ddecba8..0000000
Binary files a/src/img/flags/SM.png and /dev/null differ
diff --git a/src/img/flags/SN.png b/src/img/flags/SN.png
deleted file mode 100644
index 843bda4..0000000
Binary files a/src/img/flags/SN.png and /dev/null differ
diff --git a/src/img/flags/SO.png b/src/img/flags/SO.png
deleted file mode 100644
index a4c573d..0000000
Binary files a/src/img/flags/SO.png and /dev/null differ
diff --git a/src/img/flags/SR.png b/src/img/flags/SR.png
deleted file mode 100644
index 77cd1d3..0000000
Binary files a/src/img/flags/SR.png and /dev/null differ
diff --git a/src/img/flags/SS.png b/src/img/flags/SS.png
deleted file mode 100644
index 9cbf721..0000000
Binary files a/src/img/flags/SS.png and /dev/null differ
diff --git a/src/img/flags/ST.png b/src/img/flags/ST.png
deleted file mode 100644
index f209bcd..0000000
Binary files a/src/img/flags/ST.png and /dev/null differ
diff --git a/src/img/flags/SV.png b/src/img/flags/SV.png
deleted file mode 100644
index 333063f..0000000
Binary files a/src/img/flags/SV.png and /dev/null differ
diff --git a/src/img/flags/SX.png b/src/img/flags/SX.png
deleted file mode 100644
index 72401f5..0000000
Binary files a/src/img/flags/SX.png and /dev/null differ
diff --git a/src/img/flags/SY.png b/src/img/flags/SY.png
deleted file mode 100644
index 3dae2a2..0000000
Binary files a/src/img/flags/SY.png and /dev/null differ
diff --git a/src/img/flags/SZ.png b/src/img/flags/SZ.png
deleted file mode 100644
index 0cf3fb1..0000000
Binary files a/src/img/flags/SZ.png and /dev/null differ
diff --git a/src/img/flags/TA.png b/src/img/flags/TA.png
deleted file mode 100644
index 15e4c3e..0000000
Binary files a/src/img/flags/TA.png and /dev/null differ
diff --git a/src/img/flags/TC.png b/src/img/flags/TC.png
deleted file mode 100644
index d3da08b..0000000
Binary files a/src/img/flags/TC.png and /dev/null differ
diff --git a/src/img/flags/TD.png b/src/img/flags/TD.png
deleted file mode 100644
index 77184f4..0000000
Binary files a/src/img/flags/TD.png and /dev/null differ
diff --git a/src/img/flags/TF.png b/src/img/flags/TF.png
deleted file mode 100644
index 2c17f5e..0000000
Binary files a/src/img/flags/TF.png and /dev/null differ
diff --git a/src/img/flags/TG.png b/src/img/flags/TG.png
deleted file mode 100644
index d9b238a..0000000
Binary files a/src/img/flags/TG.png and /dev/null differ
diff --git a/src/img/flags/TH.png b/src/img/flags/TH.png
deleted file mode 100644
index 218a284..0000000
Binary files a/src/img/flags/TH.png and /dev/null differ
diff --git a/src/img/flags/TJ.png b/src/img/flags/TJ.png
deleted file mode 100644
index 2fc5e4e..0000000
Binary files a/src/img/flags/TJ.png and /dev/null differ
diff --git a/src/img/flags/TK.png b/src/img/flags/TK.png
deleted file mode 100644
index 122903d..0000000
Binary files a/src/img/flags/TK.png and /dev/null differ
diff --git a/src/img/flags/TL.png b/src/img/flags/TL.png
deleted file mode 100644
index 2f69d8d..0000000
Binary files a/src/img/flags/TL.png and /dev/null differ
diff --git a/src/img/flags/TM.png b/src/img/flags/TM.png
deleted file mode 100644
index 8e6fbb1..0000000
Binary files a/src/img/flags/TM.png and /dev/null differ
diff --git a/src/img/flags/TN.png b/src/img/flags/TN.png
deleted file mode 100644
index 0838b93..0000000
Binary files a/src/img/flags/TN.png and /dev/null differ
diff --git a/src/img/flags/TO.png b/src/img/flags/TO.png
deleted file mode 100644
index 6a680b1..0000000
Binary files a/src/img/flags/TO.png and /dev/null differ
diff --git a/src/img/flags/TR.png b/src/img/flags/TR.png
deleted file mode 100644
index a80ec23..0000000
Binary files a/src/img/flags/TR.png and /dev/null differ
diff --git a/src/img/flags/TT.png b/src/img/flags/TT.png
deleted file mode 100644
index 7052a39..0000000
Binary files a/src/img/flags/TT.png and /dev/null differ
diff --git a/src/img/flags/TV.png b/src/img/flags/TV.png
deleted file mode 100644
index 44880af..0000000
Binary files a/src/img/flags/TV.png and /dev/null differ
diff --git a/src/img/flags/TW.png b/src/img/flags/TW.png
deleted file mode 100644
index d5d14ae..0000000
Binary files a/src/img/flags/TW.png and /dev/null differ
diff --git a/src/img/flags/TZ.png b/src/img/flags/TZ.png
deleted file mode 100644
index 87c990d..0000000
Binary files a/src/img/flags/TZ.png and /dev/null differ
diff --git a/src/img/flags/UA.png b/src/img/flags/UA.png
deleted file mode 100644
index 81db6c2..0000000
Binary files a/src/img/flags/UA.png and /dev/null differ
diff --git a/src/img/flags/UG.png b/src/img/flags/UG.png
deleted file mode 100644
index f560449..0000000
Binary files a/src/img/flags/UG.png and /dev/null differ
diff --git a/src/img/flags/UM.png b/src/img/flags/UM.png
deleted file mode 100644
index 6d18cb7..0000000
Binary files a/src/img/flags/UM.png and /dev/null differ
diff --git a/src/img/flags/UN.png b/src/img/flags/UN.png
deleted file mode 100644
index ea7fc4c..0000000
Binary files a/src/img/flags/UN.png and /dev/null differ
diff --git a/src/img/flags/US.png b/src/img/flags/US.png
deleted file mode 100644
index 0ba0642..0000000
Binary files a/src/img/flags/US.png and /dev/null differ
diff --git a/src/img/flags/UY.png b/src/img/flags/UY.png
deleted file mode 100644
index aa981ca..0000000
Binary files a/src/img/flags/UY.png and /dev/null differ
diff --git a/src/img/flags/UZ.png b/src/img/flags/UZ.png
deleted file mode 100644
index ae196dd..0000000
Binary files a/src/img/flags/UZ.png and /dev/null differ
diff --git a/src/img/flags/VA.png b/src/img/flags/VA.png
deleted file mode 100644
index f0fe36f..0000000
Binary files a/src/img/flags/VA.png and /dev/null differ
diff --git a/src/img/flags/VC.png b/src/img/flags/VC.png
deleted file mode 100644
index 234bf65..0000000
Binary files a/src/img/flags/VC.png and /dev/null differ
diff --git a/src/img/flags/VE.png b/src/img/flags/VE.png
deleted file mode 100644
index 70a249f..0000000
Binary files a/src/img/flags/VE.png and /dev/null differ
diff --git a/src/img/flags/VG.png b/src/img/flags/VG.png
deleted file mode 100644
index 7201718..0000000
Binary files a/src/img/flags/VG.png and /dev/null differ
diff --git a/src/img/flags/VI.png b/src/img/flags/VI.png
deleted file mode 100644
index 0feb6ae..0000000
Binary files a/src/img/flags/VI.png and /dev/null differ
diff --git a/src/img/flags/VN.png b/src/img/flags/VN.png
deleted file mode 100644
index 5282bd4..0000000
Binary files a/src/img/flags/VN.png and /dev/null differ
diff --git a/src/img/flags/VU.png b/src/img/flags/VU.png
deleted file mode 100644
index 6047a7b..0000000
Binary files a/src/img/flags/VU.png and /dev/null differ
diff --git a/src/img/flags/WF.png b/src/img/flags/WF.png
deleted file mode 100644
index 15746da..0000000
Binary files a/src/img/flags/WF.png and /dev/null differ
diff --git a/src/img/flags/WS.png b/src/img/flags/WS.png
deleted file mode 100644
index 5356f6d..0000000
Binary files a/src/img/flags/WS.png and /dev/null differ
diff --git a/src/img/flags/XK.png b/src/img/flags/XK.png
deleted file mode 100644
index 043e9b5..0000000
Binary files a/src/img/flags/XK.png and /dev/null differ
diff --git a/src/img/flags/YE.png b/src/img/flags/YE.png
deleted file mode 100644
index e858bb2..0000000
Binary files a/src/img/flags/YE.png and /dev/null differ
diff --git a/src/img/flags/YT.png b/src/img/flags/YT.png
deleted file mode 100644
index 642b133..0000000
Binary files a/src/img/flags/YT.png and /dev/null differ
diff --git a/src/img/flags/ZA.png b/src/img/flags/ZA.png
deleted file mode 100644
index 4535007..0000000
Binary files a/src/img/flags/ZA.png and /dev/null differ
diff --git a/src/img/flags/ZM.png b/src/img/flags/ZM.png
deleted file mode 100644
index 6283325..0000000
Binary files a/src/img/flags/ZM.png and /dev/null differ
diff --git a/src/img/flags/ZW.png b/src/img/flags/ZW.png
deleted file mode 100644
index d87fd6a..0000000
Binary files a/src/img/flags/ZW.png and /dev/null differ
diff --git a/src/img/gear-icon-light.svg b/src/img/gear-icon-light.svg
deleted file mode 100644
index c52fe68..0000000
--- a/src/img/gear-icon-light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/img/gear-icon.svg b/src/img/gear-icon.svg
deleted file mode 100644
index ce8b1cb..0000000
--- a/src/img/gear-icon.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/img/info-thin-16.svg b/src/img/info-thin-16.svg
deleted file mode 100644
index c3edf49..0000000
--- a/src/img/info-thin-16.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
diff --git a/src/img/info.svg b/src/img/info.svg
deleted file mode 100644
index a5f000a..0000000
--- a/src/img/info.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/img/movetowindow-16.svg b/src/img/movetowindow-16.svg
deleted file mode 100644
index 80181a3..0000000
--- a/src/img/movetowindow-16.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
diff --git a/src/img/moz-vpn-connected.svg b/src/img/moz-vpn-connected.svg
deleted file mode 100644
index c28a9e9..0000000
--- a/src/img/moz-vpn-connected.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/src/img/moz-vpn-disconnected.svg b/src/img/moz-vpn-disconnected.svg
deleted file mode 100644
index bd7b5fd..0000000
--- a/src/img/moz-vpn-disconnected.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/src/img/moz-vpn-logo-light.svg b/src/img/moz-vpn-logo-light.svg
deleted file mode 100644
index 305e401..0000000
--- a/src/img/moz-vpn-logo-light.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-
diff --git a/src/img/moz-vpn-logo.svg b/src/img/moz-vpn-logo.svg
deleted file mode 100644
index 79449c4..0000000
--- a/src/img/moz-vpn-logo.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-
diff --git a/src/img/moz-vpn-onboarding.svg b/src/img/moz-vpn-onboarding.svg
deleted file mode 100644
index 488645f..0000000
--- a/src/img/moz-vpn-onboarding.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
diff --git a/src/img/moz-vpn-status-icons/moz-vpn-connected.svg b/src/img/moz-vpn-status-icons/moz-vpn-connected.svg
deleted file mode 100644
index 31e5581..0000000
--- a/src/img/moz-vpn-status-icons/moz-vpn-connected.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/src/img/moz-vpn-status-icons/moz-vpn-disconnected.svg b/src/img/moz-vpn-status-icons/moz-vpn-disconnected.svg
deleted file mode 100644
index c90bf81..0000000
--- a/src/img/moz-vpn-status-icons/moz-vpn-disconnected.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/src/img/multiaccountcontainer-16-dark.svg b/src/img/multiaccountcontainer-16-dark.svg
deleted file mode 100644
index 3c1e24c..0000000
--- a/src/img/multiaccountcontainer-16-dark.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
\ No newline at end of file
diff --git a/src/img/multiaccountcontainer-16.svg b/src/img/multiaccountcontainer-16.svg
deleted file mode 100644
index d6a13d1..0000000
--- a/src/img/multiaccountcontainer-16.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
\ No newline at end of file
diff --git a/src/img/new-16.svg b/src/img/new-16.svg
deleted file mode 100644
index b759168..0000000
--- a/src/img/new-16.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
diff --git a/src/img/no-connection.svg b/src/img/no-connection.svg
deleted file mode 100644
index 02b8663..0000000
--- a/src/img/no-connection.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
diff --git a/src/img/onboarding-1.png b/src/img/onboarding-1.png
deleted file mode 100644
index a75e671..0000000
Binary files a/src/img/onboarding-1.png and /dev/null differ
diff --git a/src/img/onboarding-2.png b/src/img/onboarding-2.png
deleted file mode 100644
index f0c7b1a..0000000
Binary files a/src/img/onboarding-2.png and /dev/null differ
diff --git a/src/img/onboarding-3-security.png b/src/img/onboarding-3-security.png
deleted file mode 100644
index 8bd0205..0000000
Binary files a/src/img/onboarding-3-security.png and /dev/null differ
diff --git a/src/img/onboarding-3.png b/src/img/onboarding-3.png
deleted file mode 100644
index 5498702..0000000
Binary files a/src/img/onboarding-3.png and /dev/null differ
diff --git a/src/img/onboarding-4.png b/src/img/onboarding-4.png
deleted file mode 100644
index 96e5068..0000000
Binary files a/src/img/onboarding-4.png and /dev/null differ
diff --git a/src/img/onboarding-moz-vpn.svg b/src/img/onboarding-moz-vpn.svg
deleted file mode 100644
index cd451b2..0000000
--- a/src/img/onboarding-moz-vpn.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
diff --git a/src/img/password-hide.svg b/src/img/password-hide.svg
deleted file mode 100644
index af7818d..0000000
--- a/src/img/password-hide.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
diff --git a/src/img/proxy-warning-light.svg b/src/img/proxy-warning-light.svg
deleted file mode 100644
index 72b5332..0000000
--- a/src/img/proxy-warning-light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/img/proxy-warning.svg b/src/img/proxy-warning.svg
deleted file mode 100644
index ce5a29d..0000000
--- a/src/img/proxy-warning.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/img/refresh-16.svg b/src/img/refresh-16.svg
deleted file mode 100644
index 2e40ef6..0000000
--- a/src/img/refresh-16.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
diff --git a/src/img/sort-16_1.svg b/src/img/sort-16_1.svg
deleted file mode 100644
index 83ae0ee..0000000
--- a/src/img/sort-16_1.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
diff --git a/src/img/tab-new-16.svg b/src/img/tab-new-16.svg
deleted file mode 100644
index d8c7ba6..0000000
--- a/src/img/tab-new-16.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
diff --git a/src/img/usercontext.svg b/src/img/usercontext.svg
deleted file mode 100644
index fb48e4a..0000000
--- a/src/img/usercontext.svg
+++ /dev/null
@@ -1,81 +0,0 @@
-
-
-
diff --git a/src/img/warning.svg b/src/img/warning.svg
deleted file mode 100644
index 31b5430..0000000
--- a/src/img/warning.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/img/webicon-facebook.svg b/src/img/webicon-facebook.svg
deleted file mode 100755
index c87adaf..0000000
--- a/src/img/webicon-facebook.svg
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
diff --git a/src/img/webicon-twitter.svg b/src/img/webicon-twitter.svg
deleted file mode 100755
index 6636eaa..0000000
--- a/src/img/webicon-twitter.svg
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
diff --git a/src/js/.eslintrc.js b/src/js/.eslintrc.js
deleted file mode 100644
index 4941e75..0000000
--- a/src/js/.eslintrc.js
+++ /dev/null
@@ -1,14 +0,0 @@
-module.exports = {
- "extends": [
- "../../.eslintrc.js"
- ],
- "globals": {
- "assignManager": true,
- "badge": true,
- "backgroundLogic": true,
- "identityState": true,
- "messageHandler": true,
- "sync": true,
- "Utils": true
- }
-};
diff --git a/src/js/background/assignManager.js b/src/js/background/assignManager.js
deleted file mode 100644
index 907e3c3..0000000
--- a/src/js/background/assignManager.js
+++ /dev/null
@@ -1,790 +0,0 @@
-window.assignManager = {
- MENU_ASSIGN_ID: "open-in-this-container",
- MENU_REMOVE_ID: "remove-open-in-this-container",
- MENU_SEPARATOR_ID: "separator",
- MENU_HIDE_ID: "hide-container",
- MENU_MOVE_ID: "move-to-new-window-container",
- OPEN_IN_CONTAINER: "open-bookmark-in-container-tab",
- storageArea: {
- area: browser.storage.local,
- exemptedTabs: {},
-
- getSiteStoreKey(pageUrlorUrlKey) {
- if (pageUrlorUrlKey.includes("siteContainerMap@@_")) return pageUrlorUrlKey;
- const url = new window.URL(pageUrlorUrlKey);
- const storagePrefix = "siteContainerMap@@_";
- if (url.port === "80" || url.port === "443") {
- return `${storagePrefix}${url.hostname}`;
- } else {
- return `${storagePrefix}${url.hostname}${url.port}`;
- }
- },
-
- setExempted(pageUrlorUrlKey, tabId) {
- const siteStoreKey = this.getSiteStoreKey(pageUrlorUrlKey);
- if (!(siteStoreKey in this.exemptedTabs)) {
- this.exemptedTabs[siteStoreKey] = [];
- }
- this.exemptedTabs[siteStoreKey].push(tabId);
- },
-
- removeExempted(pageUrlorUrlKey) {
- const siteStoreKey = this.getSiteStoreKey(pageUrlorUrlKey);
- this.exemptedTabs[siteStoreKey] = [];
- },
-
- isExempted(pageUrlorUrlKey, tabId) {
- const siteStoreKey = this.getSiteStoreKey(pageUrlorUrlKey);
- if (!(siteStoreKey in this.exemptedTabs)) {
- return false;
- }
- return this.exemptedTabs[siteStoreKey].includes(tabId);
- },
-
- get(pageUrlorUrlKey) {
- const siteStoreKey = this.getSiteStoreKey(pageUrlorUrlKey);
- return this.getByUrlKey(siteStoreKey);
- },
-
- async getSyncEnabled() {
- const { syncEnabled } = await browser.storage.local.get("syncEnabled");
- return !!syncEnabled;
- },
-
- async getReplaceTabEnabled() {
- const { replaceTabEnabled } = await browser.storage.local.get("replaceTabEnabled");
- return !!replaceTabEnabled;
- },
-
- getByUrlKey(siteStoreKey) {
- return new Promise((resolve, reject) => {
- this.area.get([siteStoreKey]).then((storageResponse) => {
- if (storageResponse && siteStoreKey in storageResponse) {
- resolve(storageResponse[siteStoreKey]);
- }
- resolve(null);
- }).catch((e) => {
- reject(e);
- });
- });
- },
-
- async set(pageUrlorUrlKey, data, exemptedTabIds, backup = true) {
- const siteStoreKey = this.getSiteStoreKey(pageUrlorUrlKey);
- if (exemptedTabIds) {
- exemptedTabIds.forEach((tabId) => {
- this.setExempted(pageUrlorUrlKey, tabId);
- });
- }
- // eslint-disable-next-line require-atomic-updates
- data.identityMacAddonUUID =
- await identityState.lookupMACaddonUUID(data.userContextId);
- await this.area.set({
- [siteStoreKey]: data
- });
- const syncEnabled = await this.getSyncEnabled();
- if (backup && syncEnabled) {
- await sync.storageArea.backup({undeleteSiteStoreKey: siteStoreKey});
- }
- return;
- },
-
- async remove(pageUrlorUrlKey, shouldSync = true) {
- const siteStoreKey = this.getSiteStoreKey(pageUrlorUrlKey);
- // When we remove an assignment we should clear all the exemptions
- this.removeExempted(pageUrlorUrlKey);
- await this.area.remove([siteStoreKey]);
- const syncEnabled = await this.getSyncEnabled();
- if (shouldSync && syncEnabled) await sync.storageArea.backup({siteStoreKey});
- return;
- },
-
- async deleteContainer(userContextId) {
- const sitesByContainer = await this.getAssignedSites(userContextId);
- this.area.remove(Object.keys(sitesByContainer));
- },
-
- async getAssignedSites(userContextId = null) {
- const sites = {};
- const siteConfigs = await this.area.get();
- for(const urlKey of Object.keys(siteConfigs)) {
- if (urlKey.includes("siteContainerMap@@_")) {
- // For some reason this is stored as string... lets check
- // them both as that
- if (!!userContextId &&
- String(siteConfigs[urlKey].userContextId)
- !== String(userContextId)) {
- continue;
- }
- const site = siteConfigs[urlKey];
- // In hindsight we should have stored this
- // TODO file a follow up to clean the storage onLoad
- site.hostname = urlKey.replace(/^siteContainerMap@@_/, "");
- sites[urlKey] = site;
- }
- }
- return sites;
- },
-
- /*
- * Looks for abandoned site assignments. If there is no identity with
- * the site assignment's userContextId (cookieStoreId), then the assignment
- * is removed.
- */
- async upgradeData() {
- const identitiesList = await browser.contextualIdentities.query({});
- const macConfigs = await this.area.get();
- for(const configKey of Object.keys(macConfigs)) {
- if (configKey.includes("siteContainerMap@@_")) {
- const cookieStoreId =
- "firefox-container-" + macConfigs[configKey].userContextId;
- const match = identitiesList.find(
- localIdentity => localIdentity.cookieStoreId === cookieStoreId
- );
- if (!match) {
- await this.remove(configKey);
- continue;
- }
- const updatedSiteAssignment = macConfigs[configKey];
- updatedSiteAssignment.identityMacAddonUUID =
- await identityState.lookupMACaddonUUID(match.cookieStoreId);
- await this.set(
- configKey,
- updatedSiteAssignment,
- false,
- false
- );
- }
- }
-
- }
-
- },
-
- _neverAsk(m) {
- const pageUrl = m.pageUrl;
- if (m.neverAsk === true) {
- // If we have existing data and for some reason it hasn't been
- // deleted etc lets update it
- this.storageArea.get(pageUrl).then((siteSettings) => {
- if (siteSettings) {
- siteSettings.neverAsk = true;
- this.storageArea.set(pageUrl, siteSettings);
- }
- }).catch((e) => {
- throw e;
- });
- }
- },
-
- // We return here so the confirm page can load the tab when exempted
- async _exemptTab(m) {
- const pageUrl = m.pageUrl;
- await this.storageArea.setExempted(pageUrl, m.tabId);
- return true;
- },
-
- async handleProxifiedRequest(requestInfo) {
- // The following blocks potentially dangerous requests for privacy that come without a tabId
-
- if(requestInfo.tabId === -1) {
- return {};
- }
-
- const tab = await browser.tabs.get(requestInfo.tabId);
- const result = await proxifiedContainers.retrieve(tab.cookieStoreId);
- if (!result || !result.proxy) {
- return {};
- }
-
- // proxyDNS only works for SOCKS proxies
- if (["socks", "socks4"].includes(result.proxy.type)) {
- result.proxy.proxyDNS = true;
- }
-
- if (!result.proxy.mozProxyEnabled) {
- return result.proxy;
- }
-
- // Let's add the isolation key.
- return [{ ...result.proxy, connectionIsolationKey: "" + MozillaVPN_Background.isolationKey }];
- },
-
- // Before a request is handled by the browser we decide if we should
- // route through a different container
- async onBeforeRequest(options) {
- if (options.frameId !== 0 || options.tabId === -1) {
- return {};
- }
- this.removeContextMenu();
- const [tab, siteSettings] = await Promise.all([
- browser.tabs.get(options.tabId),
- this.storageArea.get(options.url)
- ]);
- let container;
- try {
- container = await browser.contextualIdentities
- .get(backgroundLogic.cookieStoreId(siteSettings.userContextId));
- } catch (e) {
- container = false;
- }
-
- // The container we have in the assignment map isn't present any
- // more so lets remove it then continue the existing load
- if (siteSettings && !container) {
- this.deleteContainer(siteSettings.userContextId);
- return {};
- }
- const userContextId = this.getUserContextIdFromCookieStore(tab);
-
- // https://github.com/mozilla/multi-account-containers/issues/847
- //
- // Handle the case where this request's URL is not assigned to any particular
- // container. We must do the following check:
- //
- // If the current tab's container is "unlocked", we can just go ahead
- // and open the URL in the current tab, since an "unlocked" container accepts
- // any-and-all sites.
- //
- // But if the current tab's container has been "locked" by the user, then we must
- // re-open the page in the default container, because the user doesn't want random
- // sites polluting their locked container.
- //
- // For example:
- // - the current tab's container is locked and only allows "www.google.com"
- // - the incoming request is for "www.amazon.com", which has no specific container assignment
- // - in this case, we must re-open "www.amazon.com" in a new tab in the default container
- const siteIsolatedReloadInDefault =
- await this._maybeSiteIsolatedReloadInDefault(siteSettings, tab);
-
- if (!siteIsolatedReloadInDefault) {
- if (!siteSettings
- || userContextId === siteSettings.userContextId
- || this.storageArea.isExempted(options.url, tab.id)) {
- return {};
- }
- }
- const replaceTabEnabled = await this.storageArea.getReplaceTabEnabled();
- const removeTab = backgroundLogic.NEW_TAB_PAGES.has(tab.url)
- || (messageHandler.lastCreatedTab
- && messageHandler.lastCreatedTab.id === tab.id)
- || replaceTabEnabled;
- const openTabId = removeTab ? tab.openerTabId : tab.id;
-
- if (!this.canceledRequests[tab.id]) {
- // we decided to cancel the request at this point, register
- // canceled request
- this.canceledRequests[tab.id] = {
- requestIds: {
- [options.requestId]: true
- },
- urls: {
- [options.url]: true
- }
- };
-
- // since webRequest onCompleted and onErrorOccurred are not 100%
- // reliable (see #1120)
- // we register a timer here to cleanup canceled requests, just to
- // make sure we don't
- // end up in a situation where certain urls in a tab.id stay canceled
- setTimeout(() => {
- if (this.canceledRequests[tab.id]) {
- delete this.canceledRequests[tab.id];
- }
- }, 2000);
- } else {
- let cancelEarly = false;
- if (this.canceledRequests[tab.id].requestIds[options.requestId] ||
- this.canceledRequests[tab.id].urls[options.url]) {
- // same requestId or url from the same tab
- // this is a redirect that we have to cancel early to prevent
- // opening two tabs
- cancelEarly = true;
- }
- // we decided to cancel the request at this point, register canceled
- // request
- this.canceledRequests[tab.id].requestIds[options.requestId] = true;
- this.canceledRequests[tab.id].urls[options.url] = true;
- if (cancelEarly) {
- return {
- cancel: true
- };
- }
- }
-
- if (siteIsolatedReloadInDefault) {
- this.reloadPageInDefaultContainer(
- options.url,
- tab.index + 1,
- tab.active,
- openTabId
- );
- } else {
- this.reloadPageInContainer(
- options.url,
- userContextId,
- siteSettings.userContextId,
- tab.index + 1,
- tab.active,
- siteSettings.neverAsk,
- openTabId
- );
- }
- this.calculateContextMenu(tab);
-
- /* Removal of existing tabs:
- We aim to open the new assigned container tab / warning prompt in
- it's own tab:
- - As the history won't span from one container to another it
- seems most sane to not try and reopen a tab on history.back()
- - When users open a new tab themselves we want to make sure we
- don't end up with three tabs as per:
- https://github.com/mozilla/testpilot-containers/issues/421
- If we are coming from an internal url that are used for the new
- tab page (NEW_TAB_PAGES), we can safely close as user is unlikely
- losing history
- Detecting redirects on "new tab" opening actions is pretty hard
- as we don't get tab history:
- - Redirects happen from Short URLs and tracking links that act as
- a gateway
- - Extensions don't provide a way to history crawl for tabs, we
- could inject content scripts to do this
- however they don't run on about:blank so this would likely be
- just as hacky.
- We capture the time the tab was created and close if it was within
- the timeout to try to capture pages which haven't had user
- interaction or history.
- */
- if (removeTab) {
- browser.tabs.remove(tab.id);
- }
- return {
- cancel: true,
- };
- },
-
- async _maybeSiteIsolatedReloadInDefault(siteSettings, tab) {
- // Tab doesn't support cookies, so containers not supported either.
- if (!("cookieStoreId" in tab)) {
- return false;
- }
-
- // Requested page has been assigned to a specific container.
- // I.e. it will be opened in that container anyway, so we don't need to check if the
- // current tab's container is locked or not.
- if (siteSettings) {
- return false;
- }
-
- //tab is alredy reopening in the default container
- if (tab.cookieStoreId === "firefox-default") {
- return false;
- }
- // Requested page is not assigned to a specific container. If the current tab's container
- // is locked, then the page must be reloaded in the default container.
- const currentContainerState = await identityState.storageArea.get(tab.cookieStoreId);
- return currentContainerState && currentContainerState.isIsolated;
- },
-
- maybeAddProxyListeners() {
- if (browser.proxy) {
- browser.proxy.onRequest.addListener(this.handleProxifiedRequest, {urls: [""]});
- }
- },
-
- init() {
- browser.contextMenus.onClicked.addListener((info, tab) => {
- info.bookmarkId ?
- this._onClickedBookmark(info) :
- this._onClickedHandler(info, tab);
- });
-
- // Before anything happens we decide if the request should be proxified
- this.maybeAddProxyListeners();
-
- // Before a request is handled by the browser we decide if we should
- // route through a different container
- this.canceledRequests = {};
- browser.webRequest.onBeforeRequest.addListener((options) => {
- return this.onBeforeRequest(options);
- },{urls: [""], types: ["main_frame"]}, ["blocking"]);
-
- // Clean up canceled requests
- browser.webRequest.onCompleted.addListener((options) => {
- if (this.canceledRequests[options.tabId]) {
- delete this.canceledRequests[options.tabId];
- }
- },{urls: [""], types: ["main_frame"]});
- browser.webRequest.onErrorOccurred.addListener((options) => {
- if (this.canceledRequests[options.tabId]) {
- delete this.canceledRequests[options.tabId];
- }
- },{urls: [""], types: ["main_frame"]});
-
- this.resetBookmarksMenuItem();
- },
-
- async resetBookmarksMenuItem() {
- const hasPermission = await browser.permissions.contains({
- permissions: ["bookmarks"]
- });
- if (this.hadBookmark === hasPermission) {
- return;
- }
- this.hadBookmark = hasPermission;
- if (hasPermission) {
- this.initBookmarksMenu();
- browser.contextualIdentities.onCreated
- .addListener(this.contextualIdentityCreated);
- browser.contextualIdentities.onUpdated
- .addListener(this.contextualIdentityUpdated);
- browser.contextualIdentities.onRemoved
- .addListener(this.contextualIdentityRemoved);
- } else {
- this.removeBookmarksMenu();
- browser.contextualIdentities.onCreated
- .removeListener(this.contextualIdentityCreated);
- browser.contextualIdentities.onUpdated
- .removeListener(this.contextualIdentityUpdated);
- browser.contextualIdentities.onRemoved
- .removeListener(this.contextualIdentityRemoved);
- }
- },
-
- contextualIdentityCreated(changeInfo) {
- browser.contextMenus.create({
- parentId: assignManager.OPEN_IN_CONTAINER,
- id: changeInfo.contextualIdentity.cookieStoreId,
- title: changeInfo.contextualIdentity.name,
- icons: { "16": `img/usercontext.svg#${
- changeInfo.contextualIdentity.icon
- }` }
- });
- },
-
- contextualIdentityUpdated(changeInfo) {
- browser.contextMenus.update(
- changeInfo.contextualIdentity.cookieStoreId, {
- title: changeInfo.contextualIdentity.name,
- icons: { "16": `img/usercontext.svg#${
- changeInfo.contextualIdentity.icon}` }
- });
- },
-
- contextualIdentityRemoved(changeInfo) {
- browser.contextMenus.remove(
- changeInfo.contextualIdentity.cookieStoreId
- );
- },
-
- async _onClickedHandler(info, tab) {
- const userContextId = this.getUserContextIdFromCookieStore(tab);
- // Mapping ${URL(info.pageUrl).hostname} to ${userContextId}
- let remove;
- if (userContextId) {
- switch (info.menuItemId) {
- case this.MENU_ASSIGN_ID:
- case this.MENU_REMOVE_ID:
- if (info.menuItemId === this.MENU_ASSIGN_ID) {
- remove = false;
- } else {
- remove = true;
- }
- await this._setOrRemoveAssignment(
- tab.id, info.pageUrl, userContextId, remove
- );
- break;
- case this.MENU_MOVE_ID:
- backgroundLogic.moveTabsToWindow({
- cookieStoreId: tab.cookieStoreId,
- windowId: tab.windowId,
- });
- break;
- case this.MENU_HIDE_ID:
- backgroundLogic.hideTabs({
- cookieStoreId: tab.cookieStoreId,
- windowId: tab.windowId,
- });
- break;
- }
- }
- },
-
- async _onClickedBookmark(info) {
-
- async function _getBookmarksFromInfo(info) {
- const [bookmarkTreeNode] =
- await browser.bookmarks.get(info.bookmarkId);
- if (bookmarkTreeNode.type === "folder") {
- return browser.bookmarks.getChildren(bookmarkTreeNode.id);
- }
- return [bookmarkTreeNode];
- }
-
- const bookmarks = await _getBookmarksFromInfo(info);
- for (const bookmark of bookmarks) {
- // Some checks on the urls from
- // https://github.com/Rob--W/bookmark-container-tab/ thanks!
- if ( !/^(javascript|place):/i.test(bookmark.url) &&
- bookmark.type !== "folder") {
- const openInReaderMode = bookmark.url.startsWith("about:reader");
- if(openInReaderMode) {
- try {
- const parsed = new URL(bookmark.url);
- bookmark.url = parsed.searchParams.get("url") + parsed.hash;
- } catch (err) {
- return err.message;
- }
- }
- browser.tabs.create({
- cookieStoreId: info.menuItemId,
- url: bookmark.url,
- openInReaderMode: openInReaderMode
- });
- }
- }
- },
-
-
- deleteContainer(userContextId) {
- this.storageArea.deleteContainer(userContextId);
- },
-
- getUserContextIdFromCookieStore(tab) {
- if (!("cookieStoreId" in tab)) {
- return false;
- }
- return backgroundLogic.getUserContextIdFromCookieStoreId(
- tab.cookieStoreId
- );
- },
-
- isTabPermittedAssign(tab) {
- // Ensure we are not an important about url
- const url = new URL(tab.url);
- if (url.protocol === "about:"
- || url.protocol === "moz-extension:") {
- return false;
- }
- return true;
- },
-
- async _setOrRemoveAssignment(tabId, pageUrl, userContextId, remove) {
- let actionName;
- // https://github.com/mozilla/testpilot-containers/issues/626
- // Context menu has stored context IDs as strings, so we need to coerce
- // the value to a string for accurate checking
- userContextId = String(userContextId);
-
- if (!remove) {
- const tabs = await browser.tabs.query({});
- const assignmentStoreKey = this.storageArea.getSiteStoreKey(pageUrl);
- const exemptedTabIds = tabs.filter((tab) => {
- const tabStoreKey = this.storageArea.getSiteStoreKey(tab.url);
- /* Auto exempt all tabs that exist for this hostname that are not in the same container */
- if (tabStoreKey === assignmentStoreKey &&
- this.getUserContextIdFromCookieStore(tab) !== userContextId) {
- return true;
- }
- return false;
- }).map((tab) => {
- return tab.id;
- });
-
- await this.storageArea.set(pageUrl, {
- userContextId,
- neverAsk: false
- }, exemptedTabIds);
- actionName = "assigned site to always open in this container";
- } else {
- // Remove assignment
- await this.storageArea.remove(pageUrl);
-
- actionName = "removed from assigned sites list";
-
- // remove site isolation if now empty
- await this._maybeRemoveSiteIsolation(userContextId);
- }
-
- if (tabId) {
- const tab = await browser.tabs.get(tabId);
- setTimeout(function(){
- browser.tabs.sendMessage(tabId, {
- text: `Successfully ${actionName}`
- });
- }, 1000);
-
-
- this.calculateContextMenu(tab);
- }
- },
-
- async _maybeRemoveSiteIsolation(userContextId) {
- const assignments = await this.storageArea.getByContainer(userContextId);
- const hasAssignments = assignments && Object.keys(assignments).length > 0;
- if (hasAssignments) {
- return;
- }
- await backgroundLogic.addRemoveSiteIsolation(
- backgroundLogic.cookieStoreId(userContextId),
- true
- );
- },
-
- async _getAssignment(tab) {
- const cookieStore = this.getUserContextIdFromCookieStore(tab);
- // Ensure we have a cookieStore to assign to
- if (cookieStore
- && this.isTabPermittedAssign(tab)) {
- return this.storageArea.get(tab.url);
- }
- return false;
- },
-
- _getByContainer(userContextId) {
- return this.storageArea.getAssignedSites(userContextId);
- },
-
- removeContextMenu() {
- // There is a focus issue in this menu where if you change window with a context menu click
- // you get the wrong menu display because of async
- // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1215376#c16
- // We also can't change for always private mode
- // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1352102
- browser.contextMenus.remove(this.MENU_ASSIGN_ID);
- browser.contextMenus.remove(this.MENU_REMOVE_ID);
- browser.contextMenus.remove(this.MENU_SEPARATOR_ID);
- browser.contextMenus.remove(this.MENU_HIDE_ID);
- browser.contextMenus.remove(this.MENU_MOVE_ID);
- },
-
- async calculateContextMenu(tab) {
- this.removeContextMenu();
- const siteSettings = await this._getAssignment(tab);
- // Return early and not add an item if we have false
- // False represents assignment is not permitted
- if (siteSettings === false) {
- return false;
- }
- let checked = false;
- let menuId = this.MENU_ASSIGN_ID;
- const tabUserContextId = this.getUserContextIdFromCookieStore(tab);
- if (siteSettings &&
- Number(siteSettings.userContextId) === Number(tabUserContextId)) {
- checked = true;
- menuId = this.MENU_REMOVE_ID;
- }
- browser.contextMenus.create({
- id: menuId,
- title: "Always Open in This Container",
- checked,
- type: "checkbox",
- contexts: ["all"],
- });
-
- browser.contextMenus.create({
- id: this.MENU_SEPARATOR_ID,
- type: "separator",
- contexts: ["all"],
- });
-
- browser.contextMenus.create({
- id: this.MENU_HIDE_ID,
- title: "Hide This Container",
- contexts: ["all"],
- });
-
- browser.contextMenus.create({
- id: this.MENU_MOVE_ID,
- title: "Move Tabs to a New Window",
- contexts: ["all"],
- });
- },
-
- encodeURLProperty(url) {
- return encodeURIComponent(url).replace(/[!'()*]/g, (c) => {
- const charCode = c.charCodeAt(0).toString(16);
- return `%${charCode}`;
- });
- },
-
- reloadPageInDefaultContainer(url, index, active, openerTabId) {
- // To create a new tab in the default container, it is easiest just to omit the
- // cookieStoreId entirely.
- //
- // Unfortunately, if you create a new tab WITHOUT a cookieStoreId but WITH an openerTabId,
- // then the new tab automatically inherits the opener tab's cookieStoreId.
- // I.e. it opens in the wrong container!
- //
- // So we have to explicitly pass in a cookieStoreId when creating the tab, since we
- // are specifying the openerTabId. There doesn't seem to be any way
- // to look up the default container's cookieStoreId programatically, so sadly
- // we have to hardcode it here as "firefox-default". This is potentially
- // not cross-browser compatible.
- //
- // Note that we could have just omitted BOTH cookieStoreId and openerTabId. But the
- // drawback then is that if the user later closes the newly-created tab, the browser
- // does not automatically return to the original opener tab. To get this desired behaviour,
- // we MUST specify the openerTabId when creating the new tab.
- const cookieStoreId = "firefox-default";
- browser.tabs.create({url, cookieStoreId, index, active, openerTabId});
- },
-
- reloadPageInContainer(url, currentUserContextId, userContextId, index, active, neverAsk = false, openerTabId = null) {
- const cookieStoreId = backgroundLogic.cookieStoreId(userContextId);
- const loadPage = browser.runtime.getURL("confirm-page.html");
- // False represents assignment is not permitted
- // If the user has explicitly checked "Never Ask Again" on the warning page we will send them straight there
- if (neverAsk) {
- return browser.tabs.create({url, cookieStoreId, index, active, openerTabId});
- } else {
- let confirmUrl = `${loadPage}?url=${this.encodeURLProperty(url)}&cookieStoreId=${cookieStoreId}`;
- let currentCookieStoreId;
- if (currentUserContextId) {
- currentCookieStoreId = backgroundLogic.cookieStoreId(currentUserContextId);
- confirmUrl += `¤tCookieStoreId=${currentCookieStoreId}`;
- }
- return browser.tabs.create({
- url: confirmUrl,
- cookieStoreId: currentCookieStoreId,
- openerTabId,
- index,
- active
- }).then(() => {
- // We don't want to sync this URL ever nor clutter the users history
- browser.history.deleteUrl({url: confirmUrl});
- }).catch((e) => {
- throw e;
- });
- }
- },
-
- async initBookmarksMenu() {
- browser.contextMenus.create({
- id: this.OPEN_IN_CONTAINER,
- title: "Open Bookmark in Container Tab",
- contexts: ["bookmark"],
- });
-
- const identities = await browser.contextualIdentities.query({});
- for (const identity of identities) {
- browser.contextMenus.create({
- parentId: this.OPEN_IN_CONTAINER,
- id: identity.cookieStoreId,
- title: identity.name,
- icons: { "16": `img/usercontext.svg#${identity.icon}` }
- });
- }
- },
-
- async removeBookmarksMenu() {
- browser.contextMenus.remove(this.OPEN_IN_CONTAINER);
- const identities = await browser.contextualIdentities.query({});
- for (const identity of identities) {
- browser.contextMenus.remove(identity.cookieStoreId);
- }
- },
-};
-
-assignManager.init();
diff --git a/src/js/background/backgroundLogic.js b/src/js/background/backgroundLogic.js
deleted file mode 100644
index 1050d40..0000000
--- a/src/js/background/backgroundLogic.js
+++ /dev/null
@@ -1,406 +0,0 @@
-/* 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 DEFAULT_TAB = "about:newtab";
-
-const backgroundLogic = {
- NEW_TAB_PAGES: new Set([
- "about:startpage",
- "about:newtab",
- "about:home",
- "about:blank"
- ]),
- NUMBER_OF_KEYBOARD_SHORTCUTS: 10,
- unhideQueue: [],
- init() {
- browser.commands.onCommand.addListener(function (command) {
- for (let i=0; i < backgroundLogic.NUMBER_OF_KEYBOARD_SHORTCUTS; i++) {
- const key = "open_container_" + i;
- const cookieStoreId = identityState.keyboardShortcut[key];
- if (command === key) {
- if (cookieStoreId === "none") return;
- browser.tabs.create({cookieStoreId});
- }
- }
- });
-
- browser.permissions.onAdded.addListener(permissions => this.resetPermissions(permissions));
- browser.permissions.onRemoved.addListener(permissions => this.resetPermissions(permissions));
- },
-
- resetPermissions(permissions) {
- permissions.permissions.forEach(async permission => {
- switch (permission) {
- case "bookmarks":
- assignManager.resetBookmarksMenuItem();
- break;
-
- case "nativeMessaging":
- await MozillaVPN_Background.removeMozillaVpnProxies();
- await browser.runtime.reload();
- break;
-
- case "proxy":
- assignManager.maybeAddProxyListeners();
- break;
- }
- });
- },
-
- async getExtensionInfo() {
- const manifestPath = browser.runtime.getURL("manifest.json");
- const response = await fetch(manifestPath);
- const extensionInfo = await response.json();
- return extensionInfo;
- },
-
- getUserContextIdFromCookieStoreId(cookieStoreId) {
- if (!cookieStoreId) {
- return false;
- }
- const container = cookieStoreId.replace("firefox-container-", "");
- if (container !== cookieStoreId) {
- return container;
- }
- return false;
- },
-
- async deleteContainer(userContextId, removed = false) {
- await this._closeTabs(userContextId);
-
- if (!removed) {
- await browser.contextualIdentities.remove(this.cookieStoreId(userContextId));
- }
-
- assignManager.deleteContainer(userContextId);
-
- // Now remove the identity->proxy association in proxifiedContainers also
- proxifiedContainers.delete(this.cookieStoreId(userContextId));
-
- return {done: true, userContextId};
- },
-
- async createOrUpdateContainer(options) {
- if (options.userContextId !== "new") {
- return await browser.contextualIdentities.update(
- this.cookieStoreId(options.userContextId),
- options.params
- );
- }
- return await browser.contextualIdentities.create(options.params);
- },
-
- async openNewTab(options) {
- let url = options.url || undefined;
- const userContextId = ("userContextId" in options) ? options.userContextId : 0;
- const active = ("nofocus" in options) ? options.nofocus : true;
- const discarded = ("noload" in options) ? options.noload : false;
-
- const cookieStoreId = backgroundLogic.cookieStoreId(userContextId);
- // Autofocus url bar will happen in 54: https://bugzilla.mozilla.org/show_bug.cgi?id=1295072
-
- // We can't open new tab pages, so open a blank tab. Used in tab un-hide
- if (this.NEW_TAB_PAGES.has(url)) {
- url = undefined;
- }
-
- if (!this.isPermissibleURL(url)) {
- return;
- }
-
- return browser.tabs.create({
- url,
- active,
- discarded,
- pinned: options.pinned || false,
- cookieStoreId
- });
- },
-
- isPermissibleURL(url) {
- const protocol = new URL(url).protocol;
- // We can't open these we just have to throw them away
- if (protocol === "about:"
- || protocol === "chrome:"
- || protocol === "moz-extension:") {
- return false;
- }
- return true;
- },
-
- checkArgs(requiredArguments, options, methodName) {
- requiredArguments.forEach((argument) => {
- if (!(argument in options)) {
- return new Error(`${methodName} must be called with ${argument} argument.`);
- }
- });
- },
-
- async getTabs(options) {
- const requiredArguments = ["cookieStoreId", "windowId"];
- this.checkArgs(requiredArguments, options, "getTabs");
- const { cookieStoreId, windowId } = options;
-
- const list = [];
- const tabs = await browser.tabs.query({
- cookieStoreId,
- windowId
- });
- tabs.forEach((tab) => {
- list.push(identityState._createTabObject(tab));
- });
-
- const containerState = await identityState.storageArea.get(cookieStoreId);
- return list.concat(containerState.hiddenTabs);
- },
-
- async unhideContainer(cookieStoreId, alreadyShowingUrl) {
- if (!this.unhideQueue.includes(cookieStoreId)) {
- this.unhideQueue.push(cookieStoreId);
- await this.showTabs({
- cookieStoreId,
- alreadyShowingUrl
- });
- this.unhideQueue.splice(this.unhideQueue.indexOf(cookieStoreId), 1);
- }
- },
-
- // https://github.com/mozilla/multi-account-containers/issues/847
- async addRemoveSiteIsolation(cookieStoreId, remove = false) {
- const containerState = await identityState.storageArea.get(cookieStoreId);
- try {
- if ("isIsolated" in containerState || remove) {
- delete containerState.isIsolated;
- } else {
- containerState.isIsolated = "locked";
- }
- return await identityState.storageArea.set(cookieStoreId, containerState);
- } catch (error) {
- // console.error(`No container: ${cookieStoreId}`);
- }
- },
-
- async moveTabsToWindow(options) {
- const requiredArguments = ["cookieStoreId", "windowId"];
- this.checkArgs(requiredArguments, options, "moveTabsToWindow");
- const { cookieStoreId, windowId } = options;
-
- const list = await browser.tabs.query({
- cookieStoreId,
- windowId
- });
-
- const containerState = await identityState.storageArea.get(cookieStoreId);
-
- // Nothing to do
- if (list.length === 0 &&
- containerState.hiddenTabs.length === 0) {
- return;
- }
- let newWindowObj;
- let hiddenDefaultTabToClose;
- if (list.length) {
- newWindowObj = await browser.windows.create();
-
- // Pin the default tab in the new window so existing pinned tabs can be moved after it.
- // From the docs (https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs/move):
- // Note that you can't move pinned tabs to a position after any unpinned tabs in a window, or move any unpinned tabs to a position before any pinned tabs.
- await browser.tabs.update(newWindowObj.tabs[0].id, { pinned: true });
-
- browser.tabs.move(list.map((tab) => tab.id), {
- windowId: newWindowObj.id,
- index: -1
- });
- } else {
- // As we get a blank tab here we will need to await the tabs creation
- newWindowObj = await browser.windows.create({
- });
- hiddenDefaultTabToClose = true;
- }
-
- const showHiddenPromises = [];
-
- // Let's show the hidden tabs.
- if (!this.unhideQueue.includes(cookieStoreId)) {
- this.unhideQueue.push(cookieStoreId);
- for (let object of containerState.hiddenTabs) { // eslint-disable-line prefer-const
- showHiddenPromises.push(browser.tabs.create({
- url: object.url || DEFAULT_TAB,
- windowId: newWindowObj.id,
- cookieStoreId
- }));
- }
- }
-
- if (hiddenDefaultTabToClose) {
- // Lets wait for hidden tabs to show before closing the others
- await showHiddenPromises;
- }
-
- containerState.hiddenTabs = [];
-
- // Let's close all the normal tab in the new window. In theory it
- // should be only the first tab, but maybe there are addons doing
- // crazy stuff.
- const tabs = await browser.tabs.query({windowId: newWindowObj.id});
- for (let tab of tabs) { // eslint-disable-line prefer-const
- if (tab.cookieStoreId !== cookieStoreId) {
- browser.tabs.remove(tab.id);
- }
- }
- const rv = await identityState.storageArea.set(cookieStoreId, containerState);
- this.unhideQueue.splice(this.unhideQueue.indexOf(cookieStoreId), 1);
- return rv;
- },
-
- async _closeTabs(userContextId, windowId = false) {
- const cookieStoreId = this.cookieStoreId(userContextId);
- let tabs;
- /* if we have no windowId we are going to close all this container (used for deleting) */
- if (windowId !== false) {
- tabs = await browser.tabs.query({
- cookieStoreId,
- windowId
- });
- } else {
- tabs = await browser.tabs.query({
- cookieStoreId
- });
- }
- const tabIds = tabs.map((tab) => tab.id);
- return browser.tabs.remove(tabIds);
- },
-
- async queryIdentitiesState(windowId) {
- const identities = await browser.contextualIdentities.query({});
- const identitiesOutput = {};
- const identitiesPromise = identities.map(async (identity) => {
- const { cookieStoreId } = identity;
- const containerState = await identityState.storageArea.get(cookieStoreId);
- const openTabs = await browser.tabs.query({
- cookieStoreId,
- windowId
- });
- identitiesOutput[cookieStoreId] = {
- hasHiddenTabs: !!containerState.hiddenTabs.length,
- hasOpenTabs: !!openTabs.length,
- numberOfHiddenTabs: containerState.hiddenTabs.length,
- numberOfOpenTabs: openTabs.length,
- isIsolated: !!containerState.isIsolated
- };
- return;
- });
- await Promise.all(identitiesPromise);
- return identitiesOutput;
- },
-
- async sortTabs() {
- const windows = await browser.windows.getAll();
- for (let windowObj of windows) { // eslint-disable-line prefer-const
- // First the pinned tabs, then the normal ones.
- await this._sortTabsInternal(windowObj, true);
- await this._sortTabsInternal(windowObj, false);
- }
- },
-
- async _sortTabsInternal(windowObj, pinnedTabs) {
- const tabs = await browser.tabs.query({windowId: windowObj.id});
- let pos = 0;
-
- // Let's collect UCIs/tabs for this window.
- const map = new Map;
- for (const tab of tabs) {
- if (pinnedTabs && !tab.pinned) {
- // We don't have, or we already handled all the pinned tabs.
- break;
- }
-
- if (!pinnedTabs && tab.pinned) {
- // pinned tabs must be consider as taken positions.
- ++pos;
- continue;
- }
-
- if (!map.has(tab.cookieStoreId)) {
- const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(tab.cookieStoreId);
- map.set(tab.cookieStoreId, { order: userContextId, tabs: [] });
- }
- map.get(tab.cookieStoreId).tabs.push(tab);
- }
-
- const containerOrderStorage = await browser.storage.local.get([CONTAINER_ORDER_STORAGE_KEY]);
- const containerOrder =
- containerOrderStorage && containerOrderStorage[CONTAINER_ORDER_STORAGE_KEY];
-
- if (containerOrder) {
- map.forEach((obj, key) => {
- obj.order = (key in containerOrder) ? containerOrder[key] : -1;
- });
- }
-
- // Let's sort the map.
- const sortMap = new Map([...map.entries()].sort((a, b) => a[1].order > b[1].order));
-
- // Let's move tabs.
- sortMap.forEach(obj => {
- for (const tab of obj.tabs) {
- ++pos;
- browser.tabs.move(tab.id, {
- windowId: windowObj.id,
- index: pos
- });
- }
- });
- },
-
- async hideTabs(options) {
- const requiredArguments = ["cookieStoreId", "windowId"];
- this.checkArgs(requiredArguments, options, "hideTabs");
- const { cookieStoreId, windowId } = options;
-
- const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(cookieStoreId);
-
- const containerState = await identityState.storeHidden(cookieStoreId, windowId);
- await this._closeTabs(userContextId, windowId);
- return containerState;
- },
-
- async showTabs(options) {
- if (!("cookieStoreId" in options)) {
- return Promise.reject("showTabs must be called with cookieStoreId argument.");
- }
-
- const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(options.cookieStoreId);
- const promises = [];
-
- const containerState = await identityState.storageArea.get(options.cookieStoreId);
-
- for (let object of containerState.hiddenTabs) { // eslint-disable-line prefer-const
- // do not show already opened url
- const noload = !object.pinned;
- if (object.url !== options.alreadyShowingUrl) {
- promises.push(this.openNewTab({
- userContextId: userContextId,
- url: object.url,
- nofocus: options.nofocus || false,
- noload: noload,
- pinned: object.pinned,
- }));
- }
- }
-
- containerState.hiddenTabs = [];
-
- await Promise.all(promises);
- return identityState.storageArea.set(options.cookieStoreId, containerState);
- },
-
- cookieStoreId(userContextId) {
- if(userContextId === 0) return "firefox-default";
- return `firefox-container-${userContextId}`;
- }
-};
-
-
-backgroundLogic.init();
diff --git a/src/js/background/badge.js b/src/js/background/badge.js
deleted file mode 100644
index f266ad9..0000000
--- a/src/js/background/badge.js
+++ /dev/null
@@ -1,21 +0,0 @@
-const MAJOR_VERSIONS = ["2.3.0", "2.4.0", "6.2.0", "8.0.2"];
-const badge = {
- async init() {
- const currentWindow = await browser.windows.getCurrent();
- this.displayBrowserActionBadge(currentWindow);
- },
-
- async displayBrowserActionBadge() {
- const extensionInfo = await backgroundLogic.getExtensionInfo();
- const storage = await browser.storage.local.get({ browserActionBadgesClicked: [] });
-
- if (MAJOR_VERSIONS.indexOf(extensionInfo.version) > -1 &&
- storage.browserActionBadgesClicked.indexOf(extensionInfo.version) < 0) {
- browser.browserAction.setBadgeBackgroundColor({ color: "rgb(255, 79, 94)" });
- browser.browserAction.setBadgeText({ text: "!" });
- browser.browserAction.setBadgeTextColor({ color: "rgb(255, 255, 255)" });
- }
- }
-};
-
-badge.init();
diff --git a/src/js/background/identityState.js b/src/js/background/identityState.js
deleted file mode 100644
index 9114240..0000000
--- a/src/js/background/identityState.js
+++ /dev/null
@@ -1,194 +0,0 @@
-window.identityState = {
- keyboardShortcut: {},
- storageArea: {
- area: browser.storage.local,
-
- getContainerStoreKey(cookieStoreId) {
- const storagePrefix = "identitiesState@@_";
- return `${storagePrefix}${cookieStoreId}`;
- },
-
- async get(cookieStoreId) {
- const storeKey = this.getContainerStoreKey(cookieStoreId);
- const storageResponse = await this.area.get([storeKey]);
- if (storageResponse && storeKey in storageResponse) {
- if (!storageResponse[storeKey].macAddonUUID){
- storageResponse[storeKey].macAddonUUID = uuidv4();
- await this.set(cookieStoreId, storageResponse[storeKey]);
- }
- return storageResponse[storeKey];
- }
- // If local storage doesn't have an entry, look it up to make sure it's
- // an in-use identity.
- const identities = await browser.contextualIdentities.query({});
- const match = identities.find(
- (identity) => identity.cookieStoreId === cookieStoreId);
- if (match) {
- const defaultContainerState = identityState._createIdentityState();
- await this.set(cookieStoreId, defaultContainerState);
- return defaultContainerState;
- }
- return false;
- },
-
- set(cookieStoreId, data) {
- const storeKey = this.getContainerStoreKey(cookieStoreId);
- return this.area.set({
- [storeKey]: data
- });
- },
-
- async remove(cookieStoreId) {
- const storeKey = this.getContainerStoreKey(cookieStoreId);
- return this.area.remove([storeKey]);
- },
-
- async setKeyboardShortcut(shortcutId, cookieStoreId) {
- identityState.keyboardShortcut[shortcutId] = cookieStoreId;
- return this.area.set({[shortcutId]: cookieStoreId});
- },
-
- async loadKeyboardShortcuts () {
- const identities = await browser.contextualIdentities.query({});
- for (let i=0; i < backgroundLogic.NUMBER_OF_KEYBOARD_SHORTCUTS; i++) {
- const key = "open_container_" + i;
- const storageObject = await this.area.get(key);
- if (storageObject[key]){
- identityState.keyboardShortcut[key] = storageObject[key];
- continue;
- }
- if (identities[i]) {
- identityState.keyboardShortcut[key] = identities[i].cookieStoreId;
- continue;
- }
- identityState.keyboardShortcut[key] = "none";
- }
- return identityState.keyboardShortcut;
- },
-
- /*
- * Looks for abandoned identity keys in local storage, and makes sure all
- * identities registered in the browser are also in local storage. (this
- * appears to not always be the case based on how this.get() is written)
- */
- async upgradeData() {
- const identitiesList = await browser.contextualIdentities.query({});
-
- for (const identity of identitiesList) {
- // ensure all identities have an entry in local storage
- await identityState.addUUID(identity.cookieStoreId);
- }
-
- const macConfigs = await this.area.get();
- for(const configKey of Object.keys(macConfigs)) {
- if (configKey.includes("identitiesState@@_")) {
- const cookieStoreId = String(configKey).replace(/^identitiesState@@_/, "");
- const match = identitiesList.find(
- localIdentity => localIdentity.cookieStoreId === cookieStoreId
- );
- if (cookieStoreId === "firefox-default") continue;
- if (!match) {
- await this.remove(cookieStoreId);
- continue;
- }
- if (!macConfigs[configKey].macAddonUUID) {
- await identityState.storageArea.get(cookieStoreId);
- }
- }
- }
- },
-
- },
-
- _createTabObject(tab) {
- return Object.assign({}, tab);
- },
-
- async getCookieStoreIDuuidMap() {
- const containers = {};
- const identities = await browser.contextualIdentities.query({});
- for(const identity of identities) {
- const containerInfo = await this.storageArea.get(identity.cookieStoreId);
- containers[identity.cookieStoreId] = containerInfo.macAddonUUID;
- }
- return containers;
- },
-
- async storeHidden(cookieStoreId, windowId) {
- const containerState = await this.storageArea.get(cookieStoreId);
- const tabsByContainer = await browser.tabs.query({cookieStoreId, windowId});
- tabsByContainer.forEach((tab) => {
- const tabObject = this._createTabObject(tab);
- if (!backgroundLogic.isPermissibleURL(tab.url)) {
- return;
- }
- // This tab is going to be closed. Let's mark this tabObject as
- // non-active.
- tabObject.active = false;
- tabObject.hiddenState = true;
- containerState.hiddenTabs.push(tabObject);
- });
-
- return this.storageArea.set(cookieStoreId, containerState);
- },
-
- async updateUUID(cookieStoreId, uuid) {
- if (!cookieStoreId || !uuid) {
- throw new Error ("cookieStoreId or uuid missing");
- }
- const containerState = await this.storageArea.get(cookieStoreId);
- containerState.macAddonUUID = uuid;
- await this.storageArea.set(cookieStoreId, containerState);
- return uuid;
- },
-
- async addUUID(cookieStoreId) {
- await this.storageArea.get(cookieStoreId);
- },
-
- async lookupMACaddonUUID(cookieStoreId) {
- // This stays a lookup, because if the cookieStoreId doesn't
- // exist, this.get() will create it, which is not what we want.
- const cookieStoreIdKey = cookieStoreId.includes("firefox-container-") ?
- cookieStoreId : "firefox-container-" + cookieStoreId;
- const macConfigs = await this.storageArea.area.get();
- for(const configKey of Object.keys(macConfigs)) {
- if (configKey === this.storageArea.getContainerStoreKey(cookieStoreIdKey)) {
- return macConfigs[configKey].macAddonUUID;
- }
- }
- return false;
- },
-
- async lookupCookieStoreId(macAddonUUID) {
- const macConfigs = await this.storageArea.area.get();
- for(const configKey of Object.keys(macConfigs)) {
- if (configKey.includes("identitiesState@@_")) {
- if(macConfigs[configKey].macAddonUUID === macAddonUUID) {
- return String(configKey).replace(/^identitiesState@@_/, "");
- }
- }
- }
- return false;
- },
-
- _createIdentityState() {
- return {
- hiddenTabs: [],
- macAddonUUID: uuidv4()
- };
- },
-
- init() {
- this.storageArea.loadKeyboardShortcuts();
- }
-};
-
-identityState.init();
-
-function uuidv4() {
- // https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
- return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
- (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
- );
-}
diff --git a/src/js/background/index.html b/src/js/background/index.html
deleted file mode 100644
index 818dbb4..0000000
--- a/src/js/background/index.html
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/js/background/messageHandler.js b/src/js/background/messageHandler.js
deleted file mode 100644
index 5d644b6..0000000
--- a/src/js/background/messageHandler.js
+++ /dev/null
@@ -1,269 +0,0 @@
-const messageHandler = {
- // After the timer completes we assume it's a tab the user meant to keep open
- // We use this to catch redirected tabs that have just opened
- // If this were in platform we would change how the tab opens based on "new tab" link navigations such as ctrl+click
- LAST_CREATED_TAB_TIMER: 2000,
-
- init() {
- // Handles messages from webextension code
- browser.runtime.onMessage.addListener(async (m) => {
- let response;
- let tab;
-
- switch (m.method) {
- case "getShortcuts":
- response = identityState.storageArea.loadKeyboardShortcuts();
- break;
- case "setShortcut":
- identityState.storageArea.setKeyboardShortcut(m.shortcut, m.cookieStoreId);
- break;
- case "resetSync":
- response = sync.resetSync();
- break;
- case "deleteContainer":
- response = backgroundLogic.deleteContainer(m.message.userContextId);
- break;
- case "createOrUpdateContainer":
- response = backgroundLogic.createOrUpdateContainer(m.message);
- break;
- case "neverAsk":
- assignManager._neverAsk(m);
- break;
- case "addRemoveSiteIsolation":
- response = backgroundLogic.addRemoveSiteIsolation(m.cookieStoreId);
- break;
- case "getAssignment":
- response = browser.tabs.get(m.tabId).then((tab) => {
- return assignManager._getAssignment(tab);
- });
- break;
- case "getAssignmentObjectByContainer":
- response = assignManager._getByContainer(m.message.userContextId);
- break;
- case "setOrRemoveAssignment":
- // m.tabId is used for where to place the in content message
- // m.url is the assignment to be removed/added
- response = assignManager._setOrRemoveAssignment(m.tabId, m.url, m.userContextId, m.value);
- break;
- case "sortTabs":
- backgroundLogic.sortTabs();
- break;
- case "showTabs":
- backgroundLogic.unhideContainer(m.cookieStoreId);
- break;
- case "hideTabs":
- backgroundLogic.hideTabs({
- cookieStoreId: m.cookieStoreId,
- windowId: m.windowId
- });
- break;
- case "checkIncompatibleAddons":
- // TODO
- break;
- case "moveTabsToWindow":
- response = backgroundLogic.moveTabsToWindow({
- cookieStoreId: m.cookieStoreId,
- windowId: m.windowId
- });
- break;
- case "getTabs":
- response = backgroundLogic.getTabs({
- cookieStoreId: m.cookieStoreId,
- windowId: m.windowId
- });
- break;
- case "queryIdentitiesState":
- response = backgroundLogic.queryIdentitiesState(m.message.windowId);
- break;
- case "exemptContainerAssignment":
- response = assignManager._exemptTab(m);
- break;
- case "reloadInContainer":
- response = assignManager.reloadPageInContainer(
- m.url,
- m.currentUserContextId,
- m.newUserContextId,
- m.tabIndex,
- m.active,
- true
- );
- break;
- case "assignAndReloadInContainer":
- tab = await assignManager.reloadPageInContainer(
- m.url,
- m.currentUserContextId,
- m.newUserContextId,
- m.tabIndex,
- m.active,
- true
- );
- // m.tabId is used for where to place the in content message
- // m.url is the assignment to be removed/added
- response = browser.tabs.get(tab.id).then((tab) => {
- return assignManager._setOrRemoveAssignment(tab.id, m.url, m.newUserContextId, m.value);
- });
- break;
-
- case "MozillaVPN_attemptPort":
- MozillaVPN_Background.maybeInitPort();
- break;
- case "MozillaVPN_queryServers":
- MozillaVPN_Background.postToApp("servers");
- break;
- case "MozillaVPN_queryStatus":
- response = MozillaVPN_Background.postToApp("status");
- break;
- case "MozillaVPN_getConnectionStatus":
- response = MozillaVPN_Background.getConnectionStatus();
- break;
- case "MozillaVPN_getInstallationStatus":
- response = MozillaVPN_Background.getInstallationStatus();
- break;
- }
- return response;
- });
-
- // Handles external messages from webextensions
- const externalExtensionAllowed = {};
- browser.runtime.onMessageExternal.addListener(async (message, sender) => {
- if (!externalExtensionAllowed[sender.id]) {
- const extensionInfo = await browser.management.get(sender.id);
- if (!extensionInfo.permissions.includes("contextualIdentities")) {
- throw new Error("Missing contextualIdentities permission");
- }
- // eslint-disable-next-line require-atomic-updates
- externalExtensionAllowed[sender.id] = true;
- }
- let response;
- switch (message.method) {
- case "getAssignment":
- if (typeof message.url === "undefined") {
- throw new Error("Missing message.url");
- }
- response = assignManager.storageArea.get(message.url);
- break;
- default:
- throw new Error("Unknown message.method");
- }
- return response;
- });
- // Delete externalExtensionAllowed if add-on installs/updates; permissions might change
- browser.management.onInstalled.addListener(extensionInfo => {
- if (externalExtensionAllowed[extensionInfo.id]) {
- delete externalExtensionAllowed[extensionInfo.id];
- }
- });
- // Delete externalExtensionAllowed if add-on uninstalls; not needed anymore
- browser.management.onUninstalled.addListener(extensionInfo => {
- if (externalExtensionAllowed[extensionInfo.id]) {
- delete externalExtensionAllowed[extensionInfo.id];
- }
- });
-
- if (browser.contextualIdentities.onRemoved) {
- browser.contextualIdentities.onRemoved.addListener(({contextualIdentity}) => {
- const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(contextualIdentity.cookieStoreId);
- backgroundLogic.deleteContainer(userContextId, true);
- });
- }
-
- browser.tabs.onActivated.addListener((info) => {
- assignManager.removeContextMenu();
- browser.tabs.get(info.tabId).then((tab) => {
- assignManager.calculateContextMenu(tab);
- }).catch((e) => {
- throw e;
- });
- });
-
- browser.windows.onFocusChanged.addListener((windowId) => {
- this.onFocusChangedCallback(windowId);
- });
-
- browser.webRequest.onCompleted.addListener((details) => {
- if (details.frameId !== 0 || details.tabId === -1) {
- return {};
- }
- assignManager.removeContextMenu();
-
- browser.tabs.get(details.tabId).then((tab) => {
- assignManager.calculateContextMenu(tab);
- }).catch((e) => {
- throw e;
- });
- }, {urls: [""], types: ["main_frame"]});
-
- browser.tabs.onCreated.addListener((tab) => {
- // lets remember the last tab created so we can close it if it looks like a redirect
- this.lastCreatedTab = tab;
- if (tab.cookieStoreId) {
- // Don't count firefox-default, firefox-private, nor our own confirm page loads
- if (tab.cookieStoreId !== "firefox-default" &&
- tab.cookieStoreId !== "firefox-private" &&
- !tab.url.startsWith("moz-extension")) {
- // increment the counter of container tabs opened
- this.incrementCountOfContainerTabsOpened();
-
- this.tabUpdateHandler = (tabId, changeInfo) => {
- if (tabId === tab.id && changeInfo.status === "complete") {
- // get current tab's url to not open the same one from hidden tabs
- browser.tabs.get(tabId).then(loadedTab => {
- backgroundLogic.unhideContainer(tab.cookieStoreId, loadedTab.url);
- }).catch((e) => {
- throw e;
- });
-
- browser.tabs.onUpdated.removeListener(this.tabUpdateHandler);
- }
- };
-
- // if it's a container tab wait for it to complete and
- // unhide other tabs from this container
- if (tab.cookieStoreId.startsWith("firefox-container")) {
- browser.tabs.onUpdated.addListener(this.tabUpdateHandler);
- }
- }
- }
- setTimeout(() => {
- this.lastCreatedTab = null;
- }, this.LAST_CREATED_TAB_TIMER);
- });
- },
-
- async incrementCountOfContainerTabsOpened() {
- const key = "containerTabsOpened";
- const count = await browser.storage.local.get({[key]: 0});
- const countOfContainerTabsOpened = ++count[key];
- browser.storage.local.set({[key]: countOfContainerTabsOpened});
-
- // When the user opens their _ tab, give them the achievement
- if (countOfContainerTabsOpened === 100) {
- const storage = await browser.storage.local.get({achievements: []});
- storage.achievements.push({"name": "manyContainersOpened", "done": false});
- // use set and spread to create a unique array
- const achievements = [...new Set(storage.achievements)];
- browser.storage.local.set({achievements});
- browser.browserAction.setBadgeBackgroundColor({color: "rgba(0,217,0,255)"});
- browser.browserAction.setBadgeText({text: "NEW"});
- }
- },
-
- async onFocusChangedCallback(windowId) {
- assignManager.removeContextMenu();
- // browserAction loses background color in new windows ...
- // https://bugzil.la/1314674
- // https://github.com/mozilla/testpilot-containers/issues/608
- // ... so re-call displayBrowserActionBadge on window changes
- badge.displayBrowserActionBadge();
- browser.tabs.query({active: true, windowId}).then((tabs) => {
- if (tabs && tabs[0]) {
- assignManager.calculateContextMenu(tabs[0]);
- }
- }).catch((e) => {
- throw e;
- });
- }
-};
-
-// Lets do this last as theme manager did a check before connecting before
-messageHandler.init();
diff --git a/src/js/background/mozillaVpnBackground.js b/src/js/background/mozillaVpnBackground.js
deleted file mode 100644
index 9337b2a..0000000
--- a/src/js/background/mozillaVpnBackground.js
+++ /dev/null
@@ -1,118 +0,0 @@
-const MozillaVPN_Background = {
- MOZILLA_VPN_SERVERS_KEY: "mozillaVpnServers",
- MOZILLA_VPN_HIDDEN_TOUTS_LIST_KEY: "mozillaVpnHiddenToutsList",
-
- _isolationKey: 0,
-
- async maybeInitPort() {
- if (this.port && this.port.error === null) {
- return;
- }
- try {
- /*
- Find a way to not spam the console when MozillaVPN client is not installed
- File at path ".../../MozillaVPN/..." is not executable.` thrown by resource://gre/modules/Subprocess.jsm:152`
- Which does is not caught by this try/catch
- */
- this.port = await browser.runtime.connectNative("mozillavpn");
- this.port.onMessage.addListener(response => this.handleResponse(response));
-
- this.port.onMessage.addListener(this.handleResponse);
- this.postToApp("status");
- this.postToApp("servers");
-
- // When the mozillavpn dies or the VPN disconnects, we need to increase
- // the isolation key in order to create new proxy connections. Otherwise
- // we could see random timeout when the browser tries to connect to an
- // invalid proxy connection.
- this.port.onDisconnect.addListener(() => this.increaseIsolationKey());
-
- } catch(e) {
- this._installed = false;
- this._connected = false;
- }
- },
-
- async init() {
- const { mozillaVpnServers } = await browser.storage.local.get(this.MOZILLA_VPN_SERVERS_KEY);
- if (typeof(mozillaVpnServers) === "undefined") {
- await browser.storage.local.set({ [this.MOZILLA_VPN_SERVERS_KEY]:[] });
- await browser.storage.local.set({ [this.MOZILLA_VPN_HIDDEN_TOUTS_LIST_KEY]:[] });
- this._installed = false;
- this._connected = false;
- }
- this.maybeInitPort();
- },
-
- getConnectionStatus() {
- return this._connected;
- },
-
- getInstallationStatus() {
- return this._installed;
- },
-
- // Post messages to MozillaVPN client
- postToApp(message) {
- try {
- this.port.postMessage({t: message});
- } catch(e) {
- if (e.message === "Attempt to postMessage on disconnected port") {
- this._installed = false;
- this._connected = false;
- }
- }
- },
-
- // Handle responses from MozillaVPN client
- async handleResponse(response) {
- MozillaVPN_Background._installed = true;
- if (response.error && response.error === "vpn-client-down") {
- MozillaVPN_Background._connected = false;
- return;
- }
- if (response.servers) {
- const servers = response.servers.countries;
- browser.storage.local.set({ [MozillaVPN_Background.MOZILLA_VPN_SERVERS_KEY]: servers});
- return;
- }
-
- if ((response.status && response.status.vpn) || response.t === "status") {
- const status = response.status ? response.status.vpn : response.vpn;
-
- if (status === "StateOn") {
- MozillaVPN_Background._connected = true;
- }
-
- if (status === "StateOff" || status === "StateDisconnecting") {
- MozillaVPN_Background._connected = false;
- }
-
- // Let's increase the network key isolation at any vpn status change.
- MozillaVPN_Background.increaseIsolationKey();
- }
- },
-
- increaseIsolationKey() {
- ++this._isolationKey;
- },
-
- get isolationKey() {
- return this._isolationKey;
- },
-
- async removeMozillaVpnProxies() {
- const proxies = await proxifiedContainers.retrieveAll();
- if (!proxies) {
- return;
- }
- for (const proxyObj of proxies) {
- const { proxy } = proxyObj;
- if (proxy.countryCode !== undefined) {
- await proxifiedContainers.delete(proxyObj.cookieStoreId);
- }
- }
- },
-};
-
-MozillaVPN_Background.init();
diff --git a/src/js/background/sync.js b/src/js/background/sync.js
deleted file mode 100644
index 6dfb629..0000000
--- a/src/js/background/sync.js
+++ /dev/null
@@ -1,580 +0,0 @@
-const SYNC_DEBUG = false;
-
-const sync = {
- storageArea: {
- area: browser.storage.sync,
-
- async get(){
- return this.area.get();
- },
-
- async set(options) {
- return this.area.set(options);
- },
-
- async deleteIdentity(deletedIdentityUUID) {
- const deletedIdentityList =
- await sync.storageArea.getDeletedIdentityList();
- if (
- ! deletedIdentityList.find(element => element === deletedIdentityUUID)
- ) {
- deletedIdentityList.push(deletedIdentityUUID);
- await sync.storageArea.set({ deletedIdentityList });
- }
- await this.removeIdentityKeyFromSync(deletedIdentityUUID);
- },
-
- async removeIdentityKeyFromSync(deletedIdentityUUID) {
- await sync.storageArea.area.remove( "identity@@_" + deletedIdentityUUID);
- },
-
- async deleteSite(siteStoreKey) {
- const deletedSiteList =
- await sync.storageArea.getDeletedSiteList();
- if (deletedSiteList.find(element => element === siteStoreKey)) return;
- deletedSiteList.push(siteStoreKey);
- await sync.storageArea.set({ deletedSiteList });
- await sync.storageArea.area.remove(siteStoreKey);
- },
-
- async getDeletedIdentityList() {
- const storedArray = await this.getStoredItem("deletedIdentityList");
- return storedArray || [];
- },
-
- async getIdentities() {
- const allSyncStorage = await this.get();
- const identities = [];
- for (const storageKey of Object.keys(allSyncStorage)) {
- if (storageKey.includes("identity@@_")) {
- identities.push(allSyncStorage[storageKey]);
- }
- }
- return identities;
- },
-
- async getDeletedSiteList() {
- const storedArray = await this.getStoredItem("deletedSiteList");
- return (storedArray) ? storedArray : [];
- },
-
- async getAssignedSites() {
- const allSyncStorage = await this.get();
- const sites = {};
- for (const storageKey of Object.keys(allSyncStorage)) {
- if (storageKey.includes("siteContainerMap@@_")) {
- sites[storageKey] = allSyncStorage[storageKey];
- }
- }
- return sites;
- },
-
- async getStoredItem(objectKey) {
- const outputObject = await this.get(objectKey);
- if (outputObject && outputObject[objectKey])
- return outputObject[objectKey];
- return false;
- },
-
- async getAllInstanceInfo() {
- const instanceList = {};
- const allSyncInfo = await this.get();
- for (const objectKey of Object.keys(allSyncInfo)) {
- if (objectKey.includes("MACinstance")) {
- instanceList[objectKey] = allSyncInfo[objectKey]; }
- }
- return instanceList;
- },
-
- getInstanceKey() {
- return browser.runtime.getURL("")
- .replace(/moz-extension:\/\//, "MACinstance:")
- .replace(/\//, "");
- },
- async removeInstance(installUUID) {
- if (SYNC_DEBUG) console.log("removing", installUUID);
- await this.area.remove(installUUID);
- return;
- },
-
- async removeThisInstanceFromSync() {
- const installUUID = this.getInstanceKey();
- await this.removeInstance(installUUID);
- return;
- },
-
- async hasSyncStorage(){
- const inSync = await this.get();
- return !(Object.entries(inSync).length === 0);
- },
-
- async backup(options) {
- // remove listeners to avoid an infinite loop!
- await sync.checkForListenersMaybeRemove();
-
- const identities = await updateSyncIdentities();
- const siteAssignments = await updateSyncSiteAssignments();
- await updateInstanceInfo(identities, siteAssignments);
- if (options && options.uuid)
- await this.deleteIdentity(options.uuid);
- if (options && options.undeleteUUID)
- await removeFromDeletedIdentityList(options.undeleteUUID);
- if (options && options.siteStoreKey)
- await this.deleteSite(options.siteStoreKey);
- if (options && options.undeleteSiteStoreKey)
- await removeFromDeletedSitesList(options.undeleteSiteStoreKey);
-
- if (SYNC_DEBUG) console.log("Backed up!");
- await sync.checkForListenersMaybeAdd();
-
- async function updateSyncIdentities() {
- const identities = await browser.contextualIdentities.query({});
-
- for (const identity of identities) {
- delete identity.colorCode;
- delete identity.iconUrl;
- identity.macAddonUUID = await identityState.lookupMACaddonUUID(identity.cookieStoreId);
- if(identity.macAddonUUID) {
- const storageKey = "identity@@_" + identity.macAddonUUID;
- await sync.storageArea.set({ [storageKey]: identity });
- }
- }
- //await sync.storageArea.set({ identities });
- return identities;
- }
-
- async function updateSyncSiteAssignments() {
- const assignedSites =
- await assignManager.storageArea.getAssignedSites();
- for (const siteKey of Object.keys(assignedSites)) {
- await sync.storageArea.set({ [siteKey]: assignedSites[siteKey] });
- }
- return assignedSites;
- }
-
- async function updateInstanceInfo(identitiesInput, siteAssignmentsInput) {
- const date = new Date();
- const timestamp = date.getTime();
- const installUUID = sync.storageArea.getInstanceKey();
- if (SYNC_DEBUG) console.log("adding", installUUID);
- const identities = [];
- const siteAssignments = [];
- for (const identity of identitiesInput) {
- identities.push(identity.macAddonUUID);
- }
- for (const siteAssignmentKey of Object.keys(siteAssignmentsInput)) {
- siteAssignments.push(siteAssignmentKey);
- }
- await sync.storageArea.set({ [installUUID]: { timestamp, identities, siteAssignments } });
- }
-
- async function removeFromDeletedIdentityList(identityUUID) {
- const deletedIdentityList =
- await sync.storageArea.getDeletedIdentityList();
- const newDeletedIdentityList = deletedIdentityList
- .filter(element => element !== identityUUID);
- await sync.storageArea.set({ deletedIdentityList: newDeletedIdentityList });
- }
-
- async function removeFromDeletedSitesList(siteStoreKey) {
- const deletedSiteList =
- await sync.storageArea.getDeletedSiteList();
- const newDeletedSiteList = deletedSiteList
- .filter(element => element !== siteStoreKey);
- await sync.storageArea.set({ deletedSiteList: newDeletedSiteList });
- }
- },
-
- onChangedListener(changes, areaName) {
- if (areaName === "sync") sync.errorHandledRunSync();
- },
-
- async addToDeletedList(changeInfo) {
- const identity = changeInfo.contextualIdentity;
- const deletedUUID =
- await identityState.lookupMACaddonUUID(identity.cookieStoreId);
- await identityState.storageArea.remove(identity.cookieStoreId);
- sync.storageArea.backup({uuid: deletedUUID});
- }
- },
-
- async init() {
- const syncEnabled = await assignManager.storageArea.getSyncEnabled();
- if (syncEnabled) {
- // Add listener to sync storage and containers.
- // Works for all installs that have any sync storage.
- // Waits for sync storage change before kicking off the restore/backup
- // initial sync must be kicked off by user.
- this.checkForListenersMaybeAdd();
- return;
- }
- this.checkForListenersMaybeRemove();
-
- },
-
- async errorHandledRunSync () {
- await sync.runSync().catch( async (error)=> {
- if (SYNC_DEBUG) console.error("Error from runSync", error);
- await sync.checkForListenersMaybeAdd();
- });
- },
-
- async checkForListenersMaybeAdd() {
- const hasStorageListener =
- await browser.storage.onChanged.hasListener(
- sync.storageArea.onChangedListener
- );
-
- const hasCIListener = await sync.hasContextualIdentityListeners();
-
- if (!hasCIListener) {
- await sync.addContextualIdentityListeners();
- }
-
- if (!hasStorageListener) {
- await browser.storage.onChanged.addListener(
- sync.storageArea.onChangedListener);
- }
- },
-
- async checkForListenersMaybeRemove() {
- const hasStorageListener =
- await browser.storage.onChanged.hasListener(
- sync.storageArea.onChangedListener
- );
-
- const hasCIListener = await sync.hasContextualIdentityListeners();
-
- if (hasCIListener) {
- await sync.removeContextualIdentityListeners();
- }
-
- if (hasStorageListener) {
- await browser.storage.onChanged.removeListener(
- sync.storageArea.onChangedListener);
- }
- },
-
- async runSync() {
- if (SYNC_DEBUG) {
- const syncInfo = await sync.storageArea.get();
- const localInfo = await browser.storage.local.get();
- const idents = await browser.contextualIdentities.query({});
- console.log("Initial State:", {syncInfo, localInfo, idents});
- }
- await sync.checkForListenersMaybeRemove();
- if (SYNC_DEBUG) console.log("runSync");
-
- await identityState.storageArea.upgradeData();
- await assignManager.storageArea.upgradeData();
-
- const hasSyncStorage = await sync.storageArea.hasSyncStorage();
- if (hasSyncStorage) await restore();
-
- await sync.storageArea.backup();
- await removeOldDeletedItems();
- return;
- },
-
- async addContextualIdentityListeners() {
- await browser.contextualIdentities.onCreated.addListener(sync.storageArea.backup);
- await browser.contextualIdentities.onRemoved.addListener(sync.storageArea.addToDeletedList);
- await browser.contextualIdentities.onUpdated.addListener(sync.storageArea.backup);
- },
-
- async removeContextualIdentityListeners() {
- await browser.contextualIdentities.onCreated.removeListener(sync.storageArea.backup);
- await browser.contextualIdentities.onRemoved.removeListener(sync.storageArea.addToDeletedList);
- await browser.contextualIdentities.onUpdated.removeListener(sync.storageArea.backup);
- },
-
- async hasContextualIdentityListeners() {
- return (
- await browser.contextualIdentities.onCreated.hasListener(sync.storageArea.backup) &&
- await browser.contextualIdentities.onRemoved.hasListener(sync.storageArea.addToDeletedList) &&
- await browser.contextualIdentities.onUpdated.hasListener(sync.storageArea.backup)
- );
- },
-
- async resetSync() {
- const syncEnabled = await assignManager.storageArea.getSyncEnabled();
- if (syncEnabled) {
- this.errorHandledRunSync();
- return;
- }
- await this.checkForListenersMaybeRemove();
- await this.storageArea.removeThisInstanceFromSync();
- }
-
-};
-
-// attaching to window for use in mocha tests
-window.sync = sync;
-
-sync.init();
-
-async function restore() {
- if (SYNC_DEBUG) console.log("restore");
- await reconcileIdentities();
- await reconcileSiteAssignments();
- return;
-}
-
-/*
- * Checks for the container name. If it exists, they are assumed to be the
- * same container, and the color and icon are overwritten from sync, if
- * different.
- */
-async function reconcileIdentities(){
- if (SYNC_DEBUG) console.log("reconcileIdentities");
-
- // first delete any from the deleted list
- const deletedIdentityList =
- await sync.storageArea.getDeletedIdentityList();
- // first remove any deleted identities
- for (const deletedUUID of deletedIdentityList) {
- const deletedCookieStoreId =
- await identityState.lookupCookieStoreId(deletedUUID);
- if (deletedCookieStoreId){
- try{
- await browser.contextualIdentities.remove(deletedCookieStoreId);
- } catch (error) {
- // if the identity we are deleting is not there, that's fine.
- console.error("Error deleting contextualIdentity", deletedCookieStoreId);
- continue;
- }
- }
- }
- const localIdentities = await browser.contextualIdentities.query({});
- const syncIdentitiesRemoveDupes =
- await sync.storageArea.getIdentities();
- // find any local dupes created on sync storage and delete from sync storage
- for (const localIdentity of localIdentities) {
- const syncIdentitiesOfName = syncIdentitiesRemoveDupes
- .filter(identity => identity.name === localIdentity.name);
- if (syncIdentitiesOfName.length > 1) {
- const identityMatchingContextId = syncIdentitiesOfName
- .find(identity => identity.cookieStoreId === localIdentity.cookieStoreId);
- if (identityMatchingContextId)
- await sync.storageArea.removeIdentityKeyFromSync(identityMatchingContextId.macAddonUUID);
- }
- }
- const syncIdentities =
- await sync.storageArea.getIdentities();
- // now compare all containers for matching names.
- for (const syncIdentity of syncIdentities) {
- if (syncIdentity.macAddonUUID){
- const localMatch = localIdentities.find(
- localIdentity => localIdentity.name === syncIdentity.name
- );
- if (!localMatch) {
- // if there's no name match found, check on uuid,
- const localCookieStoreID =
- await identityState.lookupCookieStoreId(syncIdentity.macAddonUUID);
- if (localCookieStoreID) {
- await ifUUIDMatch(syncIdentity, localCookieStoreID);
- continue;
- }
- await ifNoMatch(syncIdentity);
- continue;
- }
-
- // Names match, so use the info from Sync
- await updateIdentityWithSyncInfo(syncIdentity, localMatch);
- continue;
- }
- // if no macAddonUUID, there is a problem with the sync info and it needs to be ignored.
- }
-
- await updateSiteAssignmentUUIDs();
-
- async function updateSiteAssignmentUUIDs(){
- const sites = assignManager.storageArea.getAssignedSites();
- for (const siteKey of Object.keys(sites)) {
- await assignManager.storageArea.set(siteKey, sites[siteKey]);
- }
- }
-}
-
-async function updateIdentityWithSyncInfo(syncIdentity, localMatch) {
- // Sync is truth. if there is a match, compare data and update as needed
- if (syncIdentity.color !== localMatch.color
- || syncIdentity.icon !== localMatch.icon) {
- await browser.contextualIdentities.update(
- localMatch.cookieStoreId, {
- name: syncIdentity.name,
- color: syncIdentity.color,
- icon: syncIdentity.icon
- });
-
- if (SYNC_DEBUG) {
- if (localMatch.color !== syncIdentity.color) {
- console.log(localMatch.name, "Change color: ", syncIdentity.color);
- }
- if (localMatch.icon !== syncIdentity.icon) {
- console.log(localMatch.name, "Change icon: ", syncIdentity.icon);
- }
- }
- }
- // Sync is truth. If all is the same, update the local uuid to match sync
- if (localMatch.macAddonUUID !== syncIdentity.macAddonUUID) {
- await identityState.updateUUID(
- localMatch.cookieStoreId,
- syncIdentity.macAddonUUID
- );
- }
- // TODOkmw: update any site assignment UUIDs
-}
-
-async function ifUUIDMatch(syncIdentity, localCookieStoreID) {
- // if there's an identical local uuid, it's the same container. Sync is truth
- const identityInfo = {
- name: syncIdentity.name,
- color: syncIdentity.color,
- icon: syncIdentity.icon
- };
- if (SYNC_DEBUG) {
- try {
- const getIdent =
- await browser.contextualIdentities.get(localCookieStoreID);
- if (getIdent.name !== identityInfo.name) {
- console.log(getIdent.name, "Change name: ", identityInfo.name);
- }
- if (getIdent.color !== identityInfo.color) {
- console.log(getIdent.name, "Change color: ", identityInfo.color);
- }
- if (getIdent.icon !== identityInfo.icon) {
- console.log(getIdent.name, "Change icon: ", identityInfo.icon);
- }
- } catch (error) {
- //if this fails, there is probably differing sync info.
- console.error("Error getting info on CI", error);
- }
- }
- try {
- // update the local container with the sync data
- await browser.contextualIdentities
- .update(localCookieStoreID, identityInfo);
- return;
- } catch (error) {
- // If this fails, sync info is off.
- console.error("Error udpating CI", error);
- }
-}
-
-async function ifNoMatch(syncIdentity){
- // if no uuid match either, make new identity
- if (SYNC_DEBUG) console.log("create new ident: ", syncIdentity.name);
- const newIdentity =
- await browser.contextualIdentities.create({
- name: syncIdentity.name,
- color: syncIdentity.color,
- icon: syncIdentity.icon
- });
- await identityState.updateUUID(
- newIdentity.cookieStoreId,
- syncIdentity.macAddonUUID
- );
- return;
-}
-/*
- * Checks for site previously assigned. If it exists, and has the same
- * container assignment, the assignment is kept. If it exists, but has
- * a different assignment, the user is prompted (not yet implemented).
- * If it does not exist, it is created.
- */
-async function reconcileSiteAssignments() {
- if (SYNC_DEBUG) console.log("reconcileSiteAssignments");
- const assignedSitesLocal =
- await assignManager.storageArea.getAssignedSites();
- const assignedSitesFromSync =
- await sync.storageArea.getAssignedSites();
- const deletedSiteList =
- await sync.storageArea.getDeletedSiteList();
- for(const siteStoreKey of deletedSiteList) {
- if (Object.prototype.hasOwnProperty.call(assignedSitesLocal,siteStoreKey)) {
- await assignManager
- .storageArea
- .remove(siteStoreKey, false);
- }
- }
-
- for(const urlKey of Object.keys(assignedSitesFromSync)) {
- const assignedSite = assignedSitesFromSync[urlKey];
- try{
- if (assignedSite.identityMacAddonUUID) {
- // Sync is truth.
- // Not even looking it up. Just overwrite
- if (SYNC_DEBUG){
- const isInStorage = await assignManager.storageArea.getByUrlKey(urlKey);
- if (!isInStorage)
- console.log("new assignment ", assignedSite);
- }
-
- await setAssignmentWithUUID(assignedSite, urlKey);
- continue;
- }
- } catch (error) {
- // this is probably old or incorrect site info in Sync
- // skip and move on.
- }
- }
-}
-
-const MILISECONDS_IN_THIRTY_DAYS = 2592000000;
-
-async function removeOldDeletedItems() {
- const instanceList = await sync.storageArea.getAllInstanceInfo();
- const deletedSiteList = await sync.storageArea.getDeletedSiteList();
- const deletedIdentityList = await sync.storageArea.getDeletedIdentityList();
-
- for (const instanceKey of Object.keys(instanceList)) {
- const date = new Date();
- const currentTimestamp = date.getTime();
- if (instanceList[instanceKey].timestamp < currentTimestamp - MILISECONDS_IN_THIRTY_DAYS) {
- delete instanceList[instanceKey];
- sync.storageArea.removeInstance(instanceKey);
- continue;
- }
- }
- for (const siteStoreKey of deletedSiteList) {
- let hasMatch = false;
- for (const instance of Object.values(instanceList)) {
- const match = instance.siteAssignments.find(element => element === siteStoreKey);
- if (!match) continue;
- hasMatch = true;
- }
- if (!hasMatch) {
- await sync.storageArea.backup({undeleteSiteStoreKey: siteStoreKey});
- }
- }
- for (const identityUUID of deletedIdentityList) {
- let hasMatch = false;
- for (const instance of Object.values(instanceList)) {
- const match = instance.identities.find(element => element === identityUUID);
- if (!match) continue;
- hasMatch = true;
- }
- if (!hasMatch) {
- await sync.storageArea.backup({undeleteUUID: identityUUID});
- }
- }
-}
-
-async function setAssignmentWithUUID(assignedSite, urlKey) {
- const uuid = assignedSite.identityMacAddonUUID;
- const cookieStoreId = await identityState.lookupCookieStoreId(uuid);
- if (cookieStoreId) {
- // eslint-disable-next-line require-atomic-updates
- assignedSite.userContextId = cookieStoreId
- .replace(/^firefox-container-/, "");
- await assignManager.storageArea.set(
- urlKey,
- assignedSite,
- false,
- false
- );
- return;
- }
- throw new Error (`No cookieStoreId found for: ${uuid}, ${urlKey}`);
-}
diff --git a/src/js/confirm-page.js b/src/js/confirm-page.js
deleted file mode 100644
index 21a445c..0000000
--- a/src/js/confirm-page.js
+++ /dev/null
@@ -1,80 +0,0 @@
-async function load() {
- const searchParams = new URL(window.location).searchParams;
- const redirectUrl = searchParams.get("url");
- const cookieStoreId = searchParams.get("cookieStoreId");
- const currentCookieStoreId = searchParams.get("currentCookieStoreId");
- const redirectUrlElement = document.getElementById("redirect-url");
- redirectUrlElement.textContent = redirectUrl;
- appendFavicon(redirectUrl, redirectUrlElement);
-
- document.getElementById("deny").addEventListener("click", (e) => {
- e.preventDefault();
- denySubmit(redirectUrl);
- });
-
- const container = await browser.contextualIdentities.get(cookieStoreId);
- const currentContainer = currentCookieStoreId ? await browser.contextualIdentities.get(currentCookieStoreId) : null;
- const currentContainerName = currentContainer ? currentContainer.name : "";
-
- document.querySelectorAll("[data-message-id]").forEach(el => {
- const elementData = el.dataset;
- const containerName = elementData.messageArg === "container-name" ? container.name : currentContainerName;
- el.textContent = browser.i18n.getMessage(elementData.messageId, containerName);
- });
-
- document.getElementById("confirm").addEventListener("click", (e) => {
- e.preventDefault();
- confirmSubmit(redirectUrl, cookieStoreId);
- });
-}
-
-function appendFavicon(pageUrl, redirectUrlElement) {
- const origin = new URL(pageUrl).origin;
- const favIconElement = Utils.createFavIconElement(`${origin}/favicon.ico`);
-
- redirectUrlElement.prepend(favIconElement);
-}
-
-function confirmSubmit(redirectUrl, cookieStoreId) {
- const neverAsk = document.getElementById("never-ask").checked;
- // Sending neverAsk message to background to store for next time we see this process
- if (neverAsk) {
- browser.runtime.sendMessage({
- method: "neverAsk",
- neverAsk: true,
- pageUrl: redirectUrl
- });
- }
- openInContainer(redirectUrl, cookieStoreId);
-}
-
-function getCurrentTab() {
- return browser.tabs.query({
- active: true,
- windowId: browser.windows.WINDOW_ID_CURRENT
- });
-}
-
-async function denySubmit(redirectUrl) {
- const tab = await getCurrentTab();
- await browser.runtime.sendMessage({
- method: "exemptContainerAssignment",
- tabId: tab[0].id,
- pageUrl: redirectUrl
- });
- document.location.replace(redirectUrl);
-}
-
-load();
-
-async function openInContainer(redirectUrl, cookieStoreId) {
- const tab = await getCurrentTab();
- await browser.tabs.create({
- index: tab[0].index + 1,
- cookieStoreId,
- url: redirectUrl
- });
- if (tab.length > 0) {
- browser.tabs.remove(tab[0].id);
- }
-}
diff --git a/src/js/content-script.js b/src/js/content-script.js
deleted file mode 100644
index 539e43a..0000000
--- a/src/js/content-script.js
+++ /dev/null
@@ -1,46 +0,0 @@
-async function delayAnimation(delay = 350) {
- return new Promise((resolve) => {
- setTimeout(resolve, delay);
- });
-}
-
-async function doAnimation(element, property, value) {
- return new Promise((resolve) => {
- const handler = () => {
- resolve();
- element.removeEventListener("transitionend", handler);
- };
- element.addEventListener("transitionend", handler);
- window.requestAnimationFrame(() => {
- element.style[property] = value;
- });
- });
-}
-
-async function addMessage(message) {
- const divElement = document.createElement("div");
- divElement.classList.add("container-notification");
- // Ideally we would use https://bugzilla.mozilla.org/show_bug.cgi?id=1340930 when this is available
- divElement.innerText = message.text;
-
- const imageElement = document.createElement("img");
- const imagePath = browser.runtime.getURL("/img/container-site-d-24.png");
- const response = await fetch(imagePath);
- const blob = await response.blob();
- const objectUrl = URL.createObjectURL(blob);
- imageElement.src = objectUrl;
- divElement.prepend(imageElement);
-
- document.body.appendChild(divElement);
-
- await delayAnimation(100);
- await doAnimation(divElement, "transform", "translateY(0)");
- await delayAnimation(3000);
- await doAnimation(divElement, "transform", "translateY(-100%)");
-
- divElement.remove();
-}
-
-browser.runtime.onMessage.addListener((message) => {
- addMessage(message);
-});
diff --git a/src/js/i18n.js b/src/js/i18n.js
deleted file mode 100644
index 3c07064..0000000
--- a/src/js/i18n.js
+++ /dev/null
@@ -1,9 +0,0 @@
-document.addEventListener("DOMContentLoaded", async () => {
- document.querySelectorAll("[data-i18n-message-id]").forEach(el => {
- const messageArgs = el.dataset.i18nPlaceholder ? el.dataset.i18nPlaceholder : null;
- el.textContent = browser.i18n.getMessage(el.dataset.i18nMessageId, [messageArgs]);
- });
- document.querySelectorAll("[data-i18n-attribute]").forEach(el => {
- el.setAttribute(el.dataset.i18nAttribute, browser.i18n.getMessage(el.dataset.i18nAttributeMessageId));
- });
-});
diff --git a/src/js/mozillaVpn.js b/src/js/mozillaVpn.js
deleted file mode 100644
index 941e148..0000000
--- a/src/js/mozillaVpn.js
+++ /dev/null
@@ -1,260 +0,0 @@
-const MozillaVPN = {
-
- async handleContainerList(identities) {
- const mozillaVpnConnected = await browser.runtime.sendMessage({ method: "MozillaVPN_getConnectionStatus" });
- const mozillaVpnInstalled = await browser.runtime.sendMessage({ method: "MozillaVPN_getInstallationStatus" });
- this.handleStatusIndicatorsInContainerLists(mozillaVpnInstalled);
-
- const permissionsEnabled = await this.bothPermissionsEnabled();
- if (!permissionsEnabled) {
- return;
- }
-
- const proxies = await this.getProxies(identities);
- if (Object.keys(proxies).length === 0) {
- return;
- }
-
- const tooltipProxyWarning = browser.i18n.getMessage("tooltipWarning");
- for (const el of document.querySelectorAll("[data-cookie-store-id]")) {
- const cookieStoreId = el.dataset.cookieStoreId;
-
- if (!proxies[cookieStoreId]) {
- continue;
- }
- const { proxy } = proxies[cookieStoreId];
-
- if (typeof(proxy) !== "undefined") {
- const flag = el.querySelector(".flag-img");
- if (proxy.countryCode) {
- flag.src = `/img/flags/${proxy.countryCode.toUpperCase()}.png`;
- }
- if (typeof(proxy.mozProxyEnabled) === "undefined" && typeof(proxy.countryCode) !== "undefined") {
- flag.classList.add("proxy-disabled");
- }
- if (!mozillaVpnConnected && proxy.mozProxyEnabled) {
- flag.classList.add("proxy-unavailable");
- const tooltip = el.querySelector(".tooltip.proxy-unavailable");
- if (tooltip) {
- tooltip.textContent = tooltipProxyWarning;
- }
- const menuItemName = el.querySelector(".menu-item-name");
- if (menuItemName) {
- el.querySelector(".menu-item-name").dataset.mozProxyWarning = "proxy-unavailable";
- }
- }
- }
- }
- },
-
- async setStatusIndicatorIcons(mozillaVpnInstalled) {
-
- const statusIconEls = document.querySelectorAll(".moz-vpn-connection-status-indicator");
-
- if (!mozillaVpnInstalled) {
- statusIconEls.forEach(el => {
- el.style.backgroundImage = "none";
- if (el.querySelector(".tooltip")) {
- el.querySelector(".tooltip").textContent = "";
- }
- el.textContent = "";
- });
- return;
- }
-
- const connectedIndicatorSrc = "url(./img/moz-vpn-connected.svg)";
- const disconnectedIndicatorSrc = "url(./img/moz-vpn-disconnected.svg)";
-
- const mozillaVpnConnected = await browser.runtime.sendMessage({ method: "MozillaVPN_getConnectionStatus" });
- const connectionStatusStringId = mozillaVpnConnected ? "moz-vpn-connected" : "moz-vpn-disconnected";
- const connectionStatusLocalizedString = browser.i18n.getMessage(connectionStatusStringId);
-
- statusIconEls.forEach(el => {
- el.style.backgroundImage = mozillaVpnConnected ? connectedIndicatorSrc : disconnectedIndicatorSrc;
- if (el.querySelector(".tooltip")) {
- el.querySelector(".tooltip").textContent = connectionStatusLocalizedString;
- } else {
- el.textContent = connectionStatusLocalizedString;
- }
- });
- },
-
- async handleStatusIndicatorsInContainerLists(mozillaVpnInstalled) {
- const mozVpnLogotypes = document.querySelectorAll(".moz-vpn-logotype.vpn-status-container-list");
-
- try {
- if (!mozillaVpnInstalled) {
- mozVpnLogotypes.forEach(el => {
- el.style.display = "none";
- });
- return;
- }
- mozVpnLogotypes.forEach(el => {
- el.style.display = "flex";
- el.classList.remove("display-none");
- });
- this.setStatusIndicatorIcons(mozillaVpnInstalled);
- } catch (e) {
- mozVpnLogotypes.forEach(el => {
- el.style.display = "none";
- });
- return;
- }
- },
-
- handleMozillaCtaClick(buttonIdentifier) {
- browser.tabs.create({
- url: MozillaVPN.attachUtmParameters("https://www.mozilla.org/products/vpn", buttonIdentifier),
- });
- },
-
- getRandomInteger(min, max) {
- return Math.floor(Math.random() * (max - min + 1)) + min;
- },
-
- proxyIsDisabled(proxy) {
- return (
- // Mozilla VPN proxy is disabled, last location data is stored
- (proxy.mozProxyEnabled === undefined && proxy.countryCode !== undefined && proxy.cityName !== undefined) ||
- // Mozilla VPN proxy is enabled but Mozilla VPN is not connected
- proxy.mozProxyEnabled !== undefined
- );
- },
-
- attachUtmParameters(baseUrl, utmContent) {
- const url = new URL(baseUrl);
- const utmParameters = {
- utm_source: "multi.account.containers",
- utm_medium: "mac-browser-addon",
- utm_content: utmContent,
- utm_campaign: "vpn-better-together",
- };
-
- for (const param in utmParameters) {
- url.searchParams.append(param, utmParameters[param]);
- }
- return url.href;
- },
-
- async getProxies(identities) {
- const proxies = {};
- const mozillaVpnInstalled = await browser.runtime.sendMessage({ method: "MozillaVPN_getInstallationStatus" });
-
- if (mozillaVpnInstalled) {
- for (const identity of identities) {
- try {
- const proxy = await proxifiedContainers.retrieve(identity.cookieStoreId);
- proxies[identity.cookieStoreId] = proxy;
- } catch (e) {
- proxies[identity.cookieStoreId] = {};
- }
- }
- }
- return proxies;
- },
-
- getMozillaProxyInfoObj() {
- return {
- countryCode: undefined,
- cityName: undefined,
- mozProxyEnabled: undefined
- };
- },
-
- async bothPermissionsEnabled() {
- return await browser.permissions.contains({ permissions: ["proxy", "nativeMessaging"] });
- },
-
-
- async getProxyWarnings(proxyObj) {
- if (!proxyObj) {
- return "";
- }
-
- const { proxy } = proxyObj;
-
- if (typeof(proxy) === "undefined") {
- return "";
- }
-
- const mozillaVpnConnected = await browser.runtime.sendMessage({ method: "MozillaVPN_getConnectionStatus" });
- if (typeof(proxy.mozProxyEnabled) !== "undefined" && !mozillaVpnConnected) {
- return "proxy-unavailable";
- }
- },
-
- async getFlag(proxyObj) {
- const flag = {
- imgCode: "default",
- elemClasses: "display-none",
- imgAlt: "",
- };
-
- if (!proxyObj) {
- return flag;
- }
-
- const { proxy } = proxyObj;
- const mozillaVpnInstalled = await browser.runtime.sendMessage({ method: "MozillaVPN_getInstallationStatus" });
- if (typeof(proxy) === "undefined" || !mozillaVpnInstalled) {
- return flag;
- }
-
- const mozillaVpnConnected = await browser.runtime.sendMessage({ method: "MozillaVPN_getConnectionStatus" });
- if (mozillaVpnInstalled && typeof(proxy.cityName) !== "undefined") {
- flag.imgCode = proxy.countryCode.toUpperCase();
- flag.imgAlt = proxy.cityName;
- flag.elemClasses = typeof(proxy.mozProxyEnabled) === "undefined" || !mozillaVpnConnected ? "proxy-disabled" : "";
- }
-
- return flag;
- },
-
- getProxy(countryCode, cityName, mozProxyEnabled, mozillaVpnServers) {
- const selectedServerCountry = mozillaVpnServers.find(({code}) => code === countryCode);
- const selectedServerCity = selectedServerCountry.cities.find(({name}) => name === cityName);
- const proxyServer = this.pickServerBasedOnWeight(selectedServerCity.servers);
- return proxifiedContainers.parseProxy(
- this.makeProxyString(proxyServer.socksName),
- {
- countryCode: countryCode,
- cityName: cityName,
- mozProxyEnabled,
- }
- );
- },
-
- makeProxyString(socksName) {
- return `socks://${socksName}.mullvad.net:1080`;
- },
-
- async pickRandomLocation() {
- const { mozillaVpnServers } = await browser.storage.local.get("mozillaVpnServers");
- const randomInteger = this.getRandomInteger(0, mozillaVpnServers.length - 1);
- const randomServerCountry = mozillaVpnServers[randomInteger];
-
- return {
- randomServerCountryCode: randomServerCountry.code,
- randomServerCityName: randomServerCountry.cities[0].name,
- };
-
- },
-
- pickServerBasedOnWeight(serverList) {
- const filteredServerList = serverList.filter(server => typeof(server.socksName) !== "undefined" && server.socksName !== "");
-
- const sumWeight = filteredServerList.reduce((sum, { weight }) => sum + weight, 0);
- let randomInteger = this.getRandomInteger(0, sumWeight);
-
- let nextServer = {};
- for (const server of filteredServerList) {
- if (server.weight >= randomInteger) {
- return nextServer = server;
- }
- randomInteger = (randomInteger - server.weight);
- }
- return nextServer;
- },
-};
-
-window.MozillaVPN = MozillaVPN;
diff --git a/src/js/options.js b/src/js/options.js
deleted file mode 100644
index 726827b..0000000
--- a/src/js/options.js
+++ /dev/null
@@ -1,137 +0,0 @@
-const NUMBER_OF_KEYBOARD_SHORTCUTS = 10;
-
-async function setUpCheckBoxes() {
- document.querySelectorAll("[data-permission-id]").forEach(async(el) => {
- const permissionId = el.dataset.permissionId;
- const permissionEnabled = await browser.permissions.contains({ permissions: [permissionId] });
- el.checked = !!permissionEnabled;
- });
-}
-
-function disablePermissionsInputs() {
- document.querySelectorAll("[data-permission-id").forEach(el => {
- el.disabled = true;
- });
-}
-
-function enablePermissionsInputs() {
- document.querySelectorAll("[data-permission-id").forEach(el => {
- el.disabled = false;
- });
-}
-
-document.querySelectorAll("[data-permission-id").forEach(async(el) => {
- const permissionId = el.dataset.permissionId;
- el.addEventListener("change", async() => {
- if (el.checked) {
- disablePermissionsInputs();
- const granted = await browser.permissions.request({ permissions: [permissionId] });
- if (!granted) {
- el.checked = false;
- enablePermissionsInputs();
- }
- return;
- }
- await browser.permissions.remove({ permissions: [permissionId] });
- });
-});
-
-async function maybeShowPermissionsWarningIcon() {
- const bothMozillaVpnPermissionsEnabled = await MozillaVPN.bothPermissionsEnabled();
- const permissionsWarningEl = document.querySelector(".warning-icon");
- permissionsWarningEl.classList.toggle("show-warning", !bothMozillaVpnPermissionsEnabled);
-}
-
-async function enableDisableSync() {
- const checkbox = document.querySelector("#syncCheck");
- await browser.storage.local.set({syncEnabled: !!checkbox.checked});
- browser.runtime.sendMessage({ method: "resetSync" });
-}
-
-async function enableDisableReplaceTab() {
- const checkbox = document.querySelector("#replaceTabCheck");
- await browser.storage.local.set({replaceTabEnabled: !!checkbox.checked});
-}
-
-async function setupOptions() {
- const { syncEnabled } = await browser.storage.local.get("syncEnabled");
- const { replaceTabEnabled } = await browser.storage.local.get("replaceTabEnabled");
- document.querySelector("#syncCheck").checked = !!syncEnabled;
- document.querySelector("#replaceTabCheck").checked = !!replaceTabEnabled;
- setupContainerShortcutSelects();
-}
-
-async function setupContainerShortcutSelects () {
- const keyboardShortcut = await browser.runtime.sendMessage({method: "getShortcuts"});
- const identities = await browser.contextualIdentities.query({});
- const fragment = document.createDocumentFragment();
- const noneOption = document.createElement("option");
- noneOption.value = "none";
- noneOption.id = "none";
- noneOption.textContent = "None";
- fragment.append(noneOption);
-
- for (const identity of identities) {
- const option = document.createElement("option");
- option.value = identity.cookieStoreId;
- option.id = identity.cookieStoreId;
- option.textContent = identity.name;
- fragment.append(option);
- }
-
- for (let i=0; i < NUMBER_OF_KEYBOARD_SHORTCUTS; i++) {
- const shortcutKey = "open_container_"+i;
- const shortcutSelect = document.getElementById(shortcutKey);
- shortcutSelect.appendChild(fragment.cloneNode(true));
- if (keyboardShortcut && keyboardShortcut[shortcutKey]) {
- const cookieStoreId = keyboardShortcut[shortcutKey];
- shortcutSelect.querySelector("#" + cookieStoreId).selected = true;
- }
- }
-}
-
-function storeShortcutChoice (event) {
- browser.runtime.sendMessage({
- method: "setShortcut",
- shortcut: event.target.id,
- cookieStoreId: event.target.value
- });
-}
-
-function resetOnboarding() {
- browser.storage.local.set({"onboarding-stage": 0});
-}
-
-async function resetPermissionsUi() {
- await maybeShowPermissionsWarningIcon();
- await setUpCheckBoxes();
- enablePermissionsInputs();
-}
-
-browser.permissions.onAdded.addListener(resetPermissionsUi);
-browser.permissions.onRemoved.addListener(resetPermissionsUi);
-
-document.addEventListener("DOMContentLoaded", setupOptions);
-document.querySelector("#syncCheck").addEventListener( "change", enableDisableSync);
-document.querySelector("#replaceTabCheck").addEventListener( "change", enableDisableReplaceTab);
-maybeShowPermissionsWarningIcon();
-for (let i=0; i < NUMBER_OF_KEYBOARD_SHORTCUTS; i++) {
- document.querySelector("#open_container_"+i)
- .addEventListener("change", storeShortcutChoice);
-}
-
-document.querySelectorAll("[data-btn-id]").forEach(btn => {
- btn.addEventListener("click", () => {
- switch (btn.dataset.btnId) {
- case "reset-onboarding":
- resetOnboarding();
- break;
- case "moz-vpn-learn-more":
- browser.tabs.create({
- url: MozillaVPN.attachUtmParameters("https://support.mozilla.org/kb/protect-your-container-tabs-mozilla-vpn", "options-learn-more")
- });
- break;
- }
- });
-});
-resetPermissionsUi();
diff --git a/src/js/pageAction.js b/src/js/pageAction.js
deleted file mode 100644
index bc0ba3c..0000000
--- a/src/js/pageAction.js
+++ /dev/null
@@ -1,37 +0,0 @@
-async function init() {
- const fragment = document.createDocumentFragment();
- const identities = await browser.contextualIdentities.query({});
-
- for (const identity of identities) {
- const tr = document.createElement("tr");
- tr.classList.add("menu-item", "hover-highlight");
- tr.setAttribute("data-cookie-store-id", identity.cookieStoreId);
- const td = document.createElement("td");
- td.innerHTML = Utils.escaped`
-
-
-
- `;
-
- tr.appendChild(td);
- fragment.appendChild(tr);
-
- Utils.addEnterHandler(tr, async () => {
- Utils.alwaysOpenInContainer(identity);
- window.close();
- });
- }
-
- const list = document.querySelector("#picker-identities-list");
- list.innerHTML = "";
- list.appendChild(fragment);
-
- MozillaVPN.handleContainerList(identities);
-}
-
-init();
diff --git a/src/js/popup.js b/src/js/popup.js
deleted file mode 100644
index 4ad1501..0000000
--- a/src/js/popup.js
+++ /dev/null
@@ -1,2283 +0,0 @@
-/* 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 CONTAINER_HIDE_SRC = "/img/password-hide.svg";
-const CONTAINER_UNHIDE_SRC = "/img/password-hide.svg";
-
-const DEFAULT_COLOR = "blue";
-const DEFAULT_ICON = "circle";
-const NEW_CONTAINER_ID = "new";
-
-const ONBOARDING_STORAGE_KEY = "onboarding-stage";
-const CONTAINER_DRAG_DATA_TYPE = "firefox-container";
-
-// List of panels
-const P_ONBOARDING_1 = "onboarding1";
-const P_ONBOARDING_2 = "onboarding2";
-const P_ONBOARDING_3 = "onboarding3";
-const P_ONBOARDING_4 = "onboarding4";
-const P_ONBOARDING_5 = "onboarding5";
-const P_ONBOARDING_6 = "onboarding6";
-const P_ONBOARDING_7 = "onboarding7";
-const P_ONBOARDING_8 = "onboarding8";
-
-const P_CONTAINERS_LIST = "containersList";
-const OPEN_NEW_CONTAINER_PICKER = "new-tab";
-const MANAGE_CONTAINERS_PICKER = "manage";
-const REOPEN_IN_CONTAINER_PICKER = "reopen-in";
-const ALWAYS_OPEN_IN_PICKER = "always-open-in";
-const P_CONTAINER_INFO = "containerInfo";
-const P_CONTAINER_EDIT = "containerEdit";
-const P_CONTAINER_DELETE = "containerDelete";
-const P_CONTAINERS_ACHIEVEMENT = "containersAchievement";
-const P_CONTAINER_ASSIGNMENTS = "containerAssignments";
-
-const P_MOZILLA_VPN_SERVER_LIST = "moz-vpn-server-list";
-const P_ADVANCED_PROXY_SETTINGS = "advanced-proxy-settings-panel";
-
-function addRemoveSiteIsolation() {
- const identity = Logic.currentIdentity();
- browser.runtime.sendMessage({
- method: "addRemoveSiteIsolation",
- cookieStoreId: identity.cookieStoreId
- });
-}
-
-async function getExtensionInfo() {
- const manifestPath = browser.runtime.getURL("manifest.json");
- const response = await fetch(manifestPath);
- const extensionInfo = await response.json();
- return extensionInfo;
-}
-
-// This object controls all the panels, identities and many other things.
-const Logic = {
- _identities: [],
- _currentIdentity: null,
- _currentPanel: null,
- _previousPanelPath: [],
- _panels: {},
- _onboardingVariation: null,
-
- async init() {
- browser.runtime.sendMessage({
- method: "MozillaVPN_attemptPort"
- }),
-
- // Remove browserAction "upgraded" badge when opening panel
- this.clearBrowserActionBadge();
-
- // Retrieve the list of identities.
- const identitiesPromise = this.refreshIdentities();
-
- try {
- await identitiesPromise;
- } catch (e) {
- throw new Error("Failed to retrieve the identities or variation. We cannot continue. ", e.message);
- }
-
- // Routing to the correct panel.
- // If localStorage is disabled, we don't show the onboarding.
- const onboardingData = await browser.storage.local.get([ONBOARDING_STORAGE_KEY]);
- let onboarded = onboardingData[ONBOARDING_STORAGE_KEY];
- if (!onboarded) {
- onboarded = 9;
- this.setOnboardingStage(onboarded);
- }
-
- switch (onboarded) {
- case 8:
- this.showAchievementOrContainersListPanel();
- break;
- case 7:
- this.showPanel(P_ONBOARDING_8);
- break;
- case 6:
- this.showPanel(P_ONBOARDING_8);
- break;
- case 5:
- this.showPanel(P_ONBOARDING_6);
- break;
- case 4:
- this.showPanel(P_ONBOARDING_5);
- break;
- case 3:
- this.showPanel(P_ONBOARDING_4);
- break;
- case 2:
- this.showPanel(P_ONBOARDING_3);
- break;
- case 1:
- this.showPanel(P_ONBOARDING_2);
- break;
- case 0:
- default:
- this.showPanel(P_ONBOARDING_1);
- break;
- }
-
- },
-
- async showAchievementOrContainersListPanel() {
- // Do we need to show an achievement panel?
- let showAchievements = false;
- const achievementsStorage = await browser.storage.local.get({ achievements: [] });
- for (const achievement of achievementsStorage.achievements) {
- if (!achievement.done) {
- showAchievements = true;
- }
- }
- if (showAchievements) {
- this.showPanel(P_CONTAINERS_ACHIEVEMENT);
- } else {
- this.showPanel(P_CONTAINERS_LIST);
- }
- },
-
- // In case the user wants to click multiple actions,
- // they have to click the "Done" button to stop the panel
- // from showing
- async setAchievementDone(achievementName) {
- const achievementsStorage = await browser.storage.local.get({ achievements: [] });
- const achievements = achievementsStorage.achievements;
- achievements.forEach((achievement, index, achievementsArray) => {
- if (achievement.name === achievementName) {
- achievement.done = true;
- achievementsArray[index] = achievement;
- }
- });
- browser.storage.local.set({ achievements });
- },
-
- setOnboardingStage(stage) {
- return browser.storage.local.set({
- [ONBOARDING_STORAGE_KEY]: stage
- });
- },
-
- async clearBrowserActionBadge() {
- const extensionInfo = await getExtensionInfo();
- const storage = await browser.storage.local.get({ browserActionBadgesClicked: [] });
- browser.browserAction.setBadgeBackgroundColor({ color: "#ffffff" });
- browser.browserAction.setBadgeText({ text: "" });
- storage.browserActionBadgesClicked.push(extensionInfo.version);
- // use set and spread to create a unique array
- const browserActionBadgesClicked = [...new Set(storage.browserActionBadgesClicked)];
- browser.storage.local.set({
- browserActionBadgesClicked
- });
- },
-
- async identity(cookieStoreId) {
- const defaultContainer = {
- name: "Default",
- cookieStoreId,
- icon: "default-tab",
- color: "default-tab",
- numberOfHiddenTabs: 0,
- numberOfOpenTabs: 0
- };
- // Handle old style rejection with null and also Promise.reject new style
- try {
- return await browser.contextualIdentities.get(cookieStoreId) || defaultContainer;
- } catch (e) {
- return defaultContainer;
- }
- },
-
- async numTabs() {
- const activeTabs = await browser.tabs.query({ windowId: browser.windows.WINDOW_ID_CURRENT });
- return activeTabs.length;
- },
-
- _disableMenuItem(message, elementToDisable = document.querySelector("#move-to-new-window")) {
- elementToDisable.setAttribute("title", message);
- elementToDisable.removeAttribute("tabindex");
- elementToDisable.classList.remove("hover-highlight");
- elementToDisable.classList.add("disabled-menu-item");
- },
-
- _enableMenuItems(elementToEnable = document.querySelector("#move-to-new-window")) {
- elementToEnable.removeAttribute("title");
- elementToEnable.setAttribute("tabindex", "0");
- elementToEnable.classList.add("hover-highlight");
- elementToEnable.classList.remove("disabled-menu-item");
- },
-
- async saveContainerOrder(rows) {
- const containerOrder = {};
- rows.forEach((node, index) => {
- return containerOrder[node.dataset.containerId] = index;
- });
- await browser.storage.local.set({
- [CONTAINER_ORDER_STORAGE_KEY]: containerOrder
- });
- },
-
- async refreshIdentities() {
- const [identities, state, containerOrderStorage] = await Promise.all([
- browser.contextualIdentities.query({}),
- browser.runtime.sendMessage({
- method: "queryIdentitiesState",
- message: {
- windowId: browser.windows.WINDOW_ID_CURRENT
- }
- }),
- browser.storage.local.get([CONTAINER_ORDER_STORAGE_KEY])
- ]);
- const containerOrder =
- containerOrderStorage && containerOrderStorage[CONTAINER_ORDER_STORAGE_KEY];
- this._identities = identities.map((identity) => {
- const stateObject = state[identity.cookieStoreId];
- if (stateObject) {
- identity.hasOpenTabs = stateObject.hasOpenTabs;
- identity.hasHiddenTabs = stateObject.hasHiddenTabs;
- identity.numberOfHiddenTabs = stateObject.numberOfHiddenTabs;
- identity.numberOfOpenTabs = stateObject.numberOfOpenTabs;
- identity.isIsolated = stateObject.isIsolated;
- }
- if (containerOrder) {
- identity.order = containerOrder[identity.cookieStoreId];
- }
- return identity;
- }).sort((i1, i2) => i1.order - i2.order);
- },
-
- getPanelSelector(panel) {
- if (this._onboardingVariation === "securityOnboarding" &&
- // eslint-disable-next-line no-prototype-builtins
- panel.hasOwnProperty("securityPanelSelector")) {
- return panel.securityPanelSelector;
- } else {
- return panel.panelSelector;
- }
- },
-
- async showPanel(panel, currentIdentity = null, backwards = false, addToPreviousPanelPath = true) {
- if ((!backwards && addToPreviousPanelPath) || !this._currentPanel) {
- this._previousPanelPath.push(this._currentPanel);
- }
-
- // If invalid panel, reset panels.
- if (!(panel in this._panels)) {
- panel = P_CONTAINERS_LIST;
- this._previousPanelPath = [];
- }
-
- this._currentPanel = panel;
-
- this._currentIdentity = currentIdentity;
-
- // Initialize the panel before showing it.
- await this._panels[panel].prepare();
- Object.keys(this._panels).forEach((panelKey) => {
- const panelItem = this._panels[panelKey];
- const panelElement = document.querySelector(this.getPanelSelector(panelItem));
- if (!panelElement.classList.contains("hide")) {
- panelElement.classList.add("hide");
- if ("unregister" in panelItem) {
- panelItem.unregister();
- }
- }
- });
- const panelEl = document.querySelector(this.getPanelSelector(this._panels[panel]));
- panelEl.classList.remove("hide");
-
- const focusEl = panelEl.querySelector(".firstTabindex");
- if(focusEl) {
- focusEl.focus();
- }
- },
-
- showPreviousPanel() {
- if (!this._previousPanelPath) {
- throw new Error("Current panel not set!");
- }
- this.showPanel(this._previousPanelPath.pop(), this._currentIdentity, true);
- },
-
- registerPanel(panelName, panelObject) {
- this._panels[panelName] = panelObject;
- panelObject.initialize();
- },
-
- identities() {
- return this._identities;
- },
-
- currentIdentity() {
- if (!this._currentIdentity) {
- throw new Error("CurrentIdentity must be set before calling Logic.currentIdentity.");
- }
- return this._currentIdentity;
- },
-
- currentUserContextId() {
- const identity = Logic.currentIdentity();
- return Utils.userContextId(identity.cookieStoreId);
- },
-
- cookieStoreId(userContextId) {
- return `firefox-container-${userContextId}`;
- },
-
- currentCookieStoreId() {
- const identity = Logic.currentIdentity();
- return identity.cookieStoreId;
- },
-
- removeIdentity(userContextId) {
- if (!userContextId) {
- return Promise.reject("removeIdentity must be called with userContextId argument.");
- }
-
- return browser.runtime.sendMessage({
- method: "deleteContainer",
- message: { userContextId }
- });
- },
-
- getAssignment(tab) {
- return browser.runtime.sendMessage({
- method: "getAssignment",
- tabId: tab.id
- });
- },
-
- getAssignmentObjectByContainer(userContextId) {
- if (!userContextId) {
- return {};
- }
- return browser.runtime.sendMessage({
- method: "getAssignmentObjectByContainer",
- message: { userContextId }
- });
- },
-
- generateIdentityName() {
- const defaultName = "Container #";
- const ids = [];
-
- // This loop populates the 'ids' array with all the already-used ids.
- this._identities.forEach(identity => {
- if (identity.name.startsWith(defaultName)) {
- const id = parseInt(identity.name.substr(defaultName.length), 10);
- if (id) {
- ids.push(id);
- }
- }
- });
-
- // Here we find the first valid id.
- for (let id = 1; ; ++id) {
- if (ids.indexOf(id) === -1) {
- return defaultName + (id < 10 ? "0" : "") + id;
- }
- }
- },
-
- getCurrentPanelElement() {
- const panelItem = this._panels[this._currentPanel];
- return document.querySelector(this.getPanelSelector(panelItem));
- },
-
- listenToPickerBackButton() {
- const closeContEl = document.querySelector("#close-container-picker-panel");
- if (!this._listenerSet) {
- Utils.addEnterHandler(closeContEl, () => {
- Logic.showPanel(P_CONTAINERS_LIST);
- });
- this._listenerSet = true;
- }
- },
-
- shortcutListener(e){
- function openNewContainerTab(identity) {
- try {
- browser.tabs.create({
- cookieStoreId: identity.cookieStoreId
- });
- window.close();
- } catch (e) {
- window.close();
- }
- }
- const identities = Logic.identities();
- if ((e.keyCode >= 49 && e.keyCode <= 57) &&
- Logic._currentPanel === "containersList") {
- const identity = identities[e.keyCode - 49];
- if (identity) {
- openNewContainerTab(identity);
- }
- }
- },
-
- keyboardNavListener(e){
- const panelSelector = Logic.getPanelSelector(Logic._panels[Logic._currentPanel]);
- const selectables = [...document.querySelectorAll(`${panelSelector} .keyboard-nav[tabindex='0']`)];
- const element = document.activeElement;
- const backButton = document.querySelector(`${panelSelector} .keyboard-nav-back`);
- const index = selectables.indexOf(element) || 0;
- function next() {
- const nextElement = selectables[index + 1];
- if (nextElement) {
- nextElement.focus();
- }
- }
- function previous() {
- const previousElement = selectables[index - 1];
- if (previousElement) {
- previousElement.focus();
- }
- }
- switch (e.keyCode) {
- case 40:
- next();
- break;
- case 38:
- previous();
- break;
- case 39:
- {
- if(element){
- element.click();
- }
-
- // If one Container is highlighted,
- if (element.classList.contains("keyboard-right-arrow-override")) {
- element.querySelector(".menu-right-float").click();
- }
-
- break;
- }
- case 37:
- {
- if(backButton){
- backButton.click();
- }
- break;
- }
- default:
- break;
- }
- }
-};
-
-// P_ONBOARDING_1: First page for Onboarding.
-// ----------------------------------------------------------------------------
-
-Logic.registerPanel(P_ONBOARDING_1, {
- panelSelector: ".onboarding-panel-1",
- securityPanelSelector: ".security-onboarding-panel-1",
-
- // This method is called when the object is registered.
- initialize() {
- // Let's move to the next panel.
- [...document.querySelectorAll(".onboarding-start-button")].forEach(startElement => {
- Utils.addEnterHandler(startElement, async () => {
- await Logic.setOnboardingStage(1);
- Logic.showPanel(P_ONBOARDING_2);
- });
- });
- },
-
- // This method is called when the panel is shown.
- prepare() {
- return Promise.resolve(null);
- },
-});
-
-// P_ONBOARDING_2: Second page for Onboarding.
-// ----------------------------------------------------------------------------
-
-Logic.registerPanel(P_ONBOARDING_2, {
- panelSelector: ".onboarding-panel-2",
- securityPanelSelector: ".security-onboarding-panel-2",
-
- // This method is called when the object is registered.
- initialize() {
- // Let's move to the containers list panel.
- [...document.querySelectorAll(".onboarding-next-button")].forEach(nextElement => {
- Utils.addEnterHandler(nextElement, async () => {
- await Logic.setOnboardingStage(2);
- Logic.showPanel(P_ONBOARDING_3);
- });
- });
- },
-
- // This method is called when the panel is shown.
- prepare() {
- return Promise.resolve(null);
- },
-});
-
-// P_ONBOARDING_3: Third page for Onboarding.
-// ----------------------------------------------------------------------------
-
-Logic.registerPanel(P_ONBOARDING_3, {
- panelSelector: ".onboarding-panel-3",
- securityPanelSelector: ".security-onboarding-panel-3",
-
- // This method is called when the object is registered.
- initialize() {
- // Let's move to the containers list panel.
- [...document.querySelectorAll(".onboarding-almost-done-button")].forEach(almostElement => {
- Utils.addEnterHandler(almostElement, async () => {
- await Logic.setOnboardingStage(3);
- Logic.showPanel(P_ONBOARDING_4);
- });
- });
- },
-
- // This method is called when the panel is shown.
- prepare() {
- return Promise.resolve(null);
- },
-});
-
-// P_ONBOARDING_4: Fourth page for Onboarding.
-// ----------------------------------------------------------------------------
-
-Logic.registerPanel(P_ONBOARDING_4, {
- panelSelector: ".onboarding-panel-4",
-
- // This method is called when the object is registered.
- initialize() {
- // Let's move to the containers list panel.
- Utils.addEnterHandler(document.querySelector("#onboarding-done-button"), async () => {
- await Logic.setOnboardingStage(4);
- Logic.showPanel(P_ONBOARDING_5);
- });
- },
-
- // This method is called when the panel is shown.
- prepare() {
- return Promise.resolve(null);
- },
-});
-
-// P_ONBOARDING_5: Fifth page for Onboarding: new tab long-press behavior
-// ----------------------------------------------------------------------------
-
-Logic.registerPanel(P_ONBOARDING_5, {
- panelSelector: ".onboarding-panel-5",
-
- // This method is called when the object is registered.
- initialize() {
- // Let's move to the containers list panel.
- Utils.addEnterHandler(document.querySelector("#onboarding-longpress-button"), async () => {
- await Logic.setOnboardingStage(5);
- Logic.showPanel(P_ONBOARDING_6);
- });
- },
-
- // This method is called when the panel is shown.
- prepare() {
- return Promise.resolve(null);
- },
-});
-
-// P_ONBOARDING_6: Sixth page for Onboarding: new tab long-press behavior
-// ----------------------------------------------------------------------------
-
-Logic.registerPanel(P_ONBOARDING_6, {
- panelSelector: ".onboarding-panel-6",
-
- // This method is called when the object is registered.
- initialize() {
- // Let's move to the containers list panel.
- Utils.addEnterHandler(document.querySelector("#start-sync-button"), async () => {
- await Logic.setOnboardingStage(6);
- await browser.storage.local.set({syncEnabled: true});
- await browser.runtime.sendMessage({
- method: "resetSync"
- });
- Logic.showPanel(P_ONBOARDING_7);
- });
- Utils.addEnterHandler(document.querySelector("#no-sync"), async () => {
- await Logic.setOnboardingStage(6);
- await browser.storage.local.set({syncEnabled: false});
- await browser.runtime.sendMessage({
- method: "resetSync"
- });
- Logic.showPanel(P_ONBOARDING_8);
- });
- },
-
- // This method is called when the panel is shown.
- prepare() {
- return Promise.resolve(null);
- },
-});
-// -----------------------------------------------------------------------
-
-Logic.registerPanel(P_ONBOARDING_7, {
- panelSelector: ".onboarding-panel-7",
-
- // This method is called when the object is registered.
- initialize() {
- // Let's move to the containers list panel.
- Utils.addEnterHandler(document.querySelector("#sign-in"), async () => {
- browser.tabs.create({
- url: "https://accounts.firefox.com/?service=sync&action=email&context=fx_desktop_v3&entrypoint=multi-account-containers&utm_source=addon&utm_medium=panel&utm_campaign=container-sync",
- });
- await Logic.setOnboardingStage(7);
- Logic.showPanel(P_ONBOARDING_8);
- });
- Utils.addEnterHandler(document.querySelector("#no-sign-in"), async () => {
- await Logic.setOnboardingStage(7);
- Logic.showPanel(P_ONBOARDING_8);
- });
- },
-
- // This method is called when the panel is shown.
- prepare() {
- return Promise.resolve(null);
- },
-});
-
-Logic.registerPanel(P_ONBOARDING_8, {
- panelSelector: ".onboarding-panel-8",
-
- // This method is called when the object is registered.
- initialize() {
- document.querySelectorAll(".onboarding-done").forEach(el => {
- Utils.addEnterHandler(el, async () => {
- await Logic.setOnboardingStage(8);
- Logic.showPanel(P_CONTAINERS_LIST);
- });
- });
-
- },
-
- // This method is called when the panel is shown.
- async prepare() {
- const mozillaVpnPermissionsEnabled = await MozillaVPN.bothPermissionsEnabled();
- if (!mozillaVpnPermissionsEnabled) {
- const panel = document.querySelector(".onboarding-panel-8");
- panel.classList.add("optional-permissions-disabled");
-
- Utils.addEnterHandler(panel.querySelector("#onboarding-enable-permissions"), async () => {
- const granted = await browser.permissions.request({ permissions: ["proxy", "nativeMessaging"] });
- if (granted) {
- await Logic.setOnboardingStage(8);
- }
- });
- }
- return Promise.resolve(null);
- },
-});
-// P_CONTAINERS_LIST: The list of containers. The main page.
-// ----------------------------------------------------------------------------
-
-Logic.registerPanel(P_CONTAINERS_LIST, {
- panelSelector: "#container-panel",
-
- // This method is called when the object is registered.
- async initialize() {
- await browser.runtime.sendMessage({ method: "MozillaVPN_queryStatus" });
- Utils.addEnterHandler(document.querySelector("#manage-containers-link"), (e) => {
- if (!e.target.classList.contains("disable-edit-containers")) {
- Logic.showPanel(MANAGE_CONTAINERS_PICKER);
- }
- });
- Utils.addEnterHandler(document.querySelector("#open-new-tab-in"), () => {
- Logic.showPanel(OPEN_NEW_CONTAINER_PICKER);
- });
- Utils.addEnterHandler(document.querySelector("#reopen-site-in"), () => {
- Logic.showPanel(REOPEN_IN_CONTAINER_PICKER);
- });
- Utils.addEnterHandler(document.querySelector("#always-open-in"), () => {
- Logic.showPanel(ALWAYS_OPEN_IN_PICKER);
- });
- Utils.addEnterHandler(document.querySelector("#sort-containers-link"), async () => {
- try {
- await browser.runtime.sendMessage({
- method: "sortTabs"
- });
- window.close();
- } catch (e) {
- window.close();
- }
- });
-
- const mozillaVpnToutName = "moz-tout-main-panel";
- const mozillaVpnPermissionsWarningDotName = "moz-permissions-warning-dot";
-
- let { mozillaVpnHiddenToutsList } = await browser.storage.local.get("mozillaVpnHiddenToutsList");
- if (typeof(mozillaVpnHiddenToutsList) === "undefined") {
- await browser.storage.local.set({ "mozillaVpnHiddenToutsList": [] });
- mozillaVpnHiddenToutsList = [];
- }
-
- // Decide whether to show Mozilla VPN tout
- const mozVpnTout = document.getElementById("moz-vpn-tout");
- const mozillaVpnInstalled = await browser.runtime.sendMessage({ method: "MozillaVPN_getInstallationStatus" });
- const mozillaVpnToutShouldBeHidden = mozillaVpnHiddenToutsList.find(tout => tout.name === mozillaVpnToutName);
- if (mozillaVpnInstalled || mozillaVpnToutShouldBeHidden) {
- mozVpnTout.remove();
- }
-
- // Add handlers if tout is visible
- const mozVpnDismissTout = document.querySelector(".dismiss-moz-vpn-tout");
- if (mozVpnDismissTout) {
- Utils.addEnterHandler((mozVpnDismissTout), async() => {
- mozVpnTout.remove();
- mozillaVpnHiddenToutsList.push({
- name: mozillaVpnToutName
- });
- await browser.storage.local.set({ mozillaVpnHiddenToutsList });
- });
-
- Utils.addEnterHandler(document.querySelector("#moz-vpn-learn-more"), () => {
- MozillaVPN.handleMozillaCtaClick("mac-main-panel-btn");
- window.close();
- });
- }
-
- // Badge Options icon if both nativeMessaging and/or proxy permissions are disabled
- const bothMozillaVpnPermissionsEnabled = await MozillaVPN.bothPermissionsEnabled();
- const warningDotShouldBeHidden = mozillaVpnHiddenToutsList.find(tout => tout.name === mozillaVpnPermissionsWarningDotName);
- const optionsIcon = document.getElementById("info-icon");
- if (optionsIcon && !bothMozillaVpnPermissionsEnabled && !warningDotShouldBeHidden) {
- optionsIcon.classList.add("info-icon-alert");
- }
-
- Utils.addEnterHandler((document.querySelector("#info-icon")), async() => {
- browser.runtime.openOptionsPage();
- if (!mozillaVpnHiddenToutsList.find(tout => tout.name === mozillaVpnPermissionsWarningDotName)) {
- optionsIcon.classList.remove("info-icon-alert");
- mozillaVpnHiddenToutsList.push({
- name: mozillaVpnPermissionsWarningDotName
- });
- }
- await browser.storage.local.set({ mozillaVpnHiddenToutsList });
- });
- },
-
- unregister() {
- },
-
- // This method is called when the panel is shown.
- async prepare() {
- const fragment = document.createDocumentFragment();
- const identities = Logic.identities();
-
- for (const identity of identities) {
- const tr = document.createElement("tr");
- tr.classList.add("menu-item", "hover-highlight", "keyboard-nav", "keyboard-right-arrow-override");
- tr.setAttribute("tabindex", "0");
- tr.setAttribute("data-cookie-store-id", identity.cookieStoreId);
- const td = document.createElement("td");
- const openTabs = identity.numberOfOpenTabs || "" ;
-
- // TODO get UX and content decision on how to message and block clicks to containers with Mozilla VPN proxy configs
- // when Mozilla VPN app is disconnected.
-
- td.innerHTML = Utils.escaped`
-
- `;
-
-
-
- fragment.appendChild(tr);
-
- tr.appendChild(td);
-
- const openInThisContainer = tr.querySelector(".menu-item-name");
- Utils.addEnterHandler(openInThisContainer, (e) => {
- e.preventDefault();
- if (openInThisContainer.dataset.mozProxyWarning === "proxy-unavailable") {
- return;
- }
- try {
- browser.tabs.create({
- cookieStoreId: identity.cookieStoreId
- });
- window.close();
- } catch (e) {
- window.close();
- }
- });
-
- Utils.addEnterOnlyHandler(tr, () => {
- try {
- browser.tabs.create({
- cookieStoreId: identity.cookieStoreId
- });
- window.close();
- } catch (e) {
- window.close();
- }
- });
-
- // Select only the ">" from the container list
- const showPanelButton = tr.querySelector(".menu-right-float");
-
- Utils.addEnterHandler(showPanelButton, () => {
- Logic.showPanel(P_CONTAINER_INFO, identity);
- });
- }
-
- const list = document.querySelector("#identities-list");
-
- list.innerHTML = "";
- list.appendChild(fragment);
-
- document.addEventListener("keydown", Logic.keyboardNavListener);
- document.addEventListener("keydown", Logic.shortcutListener);
-
- MozillaVPN.handleContainerList(identities);
-
- // reset path
- this._previousPanelPath = [];
- return Promise.resolve();
- },
-});
-
-// P_CONTAINER_INFO: More info about a container.
-// ----------------------------------------------------------------------------
-
-Logic.registerPanel(P_CONTAINER_INFO, {
- panelSelector: "#container-info-panel",
-
- // This method is called when the object is registered.
- async initialize() {
- const closeContEl = document.querySelector("#close-container-info-panel");
- Utils.addEnterHandler(closeContEl, () => {
- Logic.showPanel(P_CONTAINERS_LIST);
- });
-
- // Check if the user has incompatible add-ons installed
- // Note: this is not implemented in messageHandler.js
- let incompatible = false;
- try {
- incompatible = await browser.runtime.sendMessage({
- method: "checkIncompatibleAddons"
- });
- } catch (e) {
- throw new Error("Could not check for incompatible add-ons.");
- }
-
- const moveTabsEl = document.querySelector("#move-to-new-window");
- const numTabs = await Logic.numTabs();
- if (incompatible) {
- Logic._disableMenuItem("Moving container tabs is incompatible with Pulse, PageShot, and SnoozeTabs.");
- return;
- } else if (numTabs === 1) {
- Logic._disableMenuItem("Cannot move a tab from a single-tab window.");
- return;
- }
-
- Utils.addEnterHandler(moveTabsEl, async () => {
- await browser.runtime.sendMessage({
- method: "moveTabsToWindow",
- windowId: browser.windows.WINDOW_ID_CURRENT,
- cookieStoreId: Logic.currentIdentity().cookieStoreId,
- });
- window.close();
- });
- },
-
- // This method is called when the panel is shown.
- async prepare() {
- const identity = Logic.currentIdentity();
-
- const newTab = document.querySelector("#open-new-tab-in-info");
- Utils.addEnterHandler(newTab, () => {
- try {
- browser.tabs.create({
- cookieStoreId: identity.cookieStoreId
- });
- window.close();
- } catch (e) {
- window.close();
- }
- });
- // Populating the panel: name and icon
- document.getElementById("container-info-title").textContent = identity.name;
-
- const alwaysOpen = document.querySelector("#always-open-in-info-panel");
- Utils.addEnterHandler(alwaysOpen, async () => {
- Utils.alwaysOpenInContainer(identity);
- window.close();
- });
- // Show or not the has-tabs section.
- for (let trHasTabs of document.getElementsByClassName("container-info-has-tabs")) { // eslint-disable-line prefer-const
- trHasTabs.style.display = !identity.hasHiddenTabs && !identity.hasOpenTabs ? "none" : "";
- }
-
- if (identity.numberOfOpenTabs === 0) {
- Logic._disableMenuItem("No tabs available for this container");
- } else {
- Logic._enableMenuItems();
- }
-
- this.intializeShowHide(identity);
-
- // Let's retrieve the list of tabs.
- const tabs = await browser.runtime.sendMessage({
- method: "getTabs",
- windowId: browser.windows.WINDOW_ID_CURRENT,
- cookieStoreId: Logic.currentIdentity().cookieStoreId
- });
- const manageContainer = document.querySelector("#manage-container-link");
- Utils.addEnterHandler(manageContainer, async () => {
- Logic.showPanel(P_CONTAINER_EDIT, identity);
- });
- return this.buildOpenTabTable(tabs);
- },
-
- intializeShowHide(identity) {
- const hideContEl = document.querySelector("#hideorshow-container");
- if (identity.numberOfOpenTabs === 0 && !identity.hasHiddenTabs) {
- return Logic._disableMenuItem("No tabs available for this container", hideContEl);
- } else {
- Logic._enableMenuItems(hideContEl);
- }
-
- Utils.addEnterHandler(hideContEl, async () => {
- try {
- browser.runtime.sendMessage({
- method: identity.hasHiddenTabs ? "showTabs" : "hideTabs",
- windowId: browser.windows.WINDOW_ID_CURRENT,
- cookieStoreId: Logic.currentCookieStoreId()
- });
- window.close();
- } catch (e) {
- window.close();
- }
- });
-
- const hideShowIcon = document.getElementById("container-info-hideorshow-icon");
- hideShowIcon.src = identity.hasHiddenTabs ? CONTAINER_UNHIDE_SRC : CONTAINER_HIDE_SRC;
-
- const hideShowLabel = document.getElementById("container-info-hideorshow-label");
- hideShowLabel.textContent = browser.i18n.getMessage(identity.hasHiddenTabs ? "showThisContainer" : "hideThisContainer");
- return;
- },
-
- buildOpenTabTable(tabs) {
- // Let's remove all the previous tabs.
- const table = document.getElementById("container-info-table");
- while (table.firstChild) {
- table.firstChild.remove();
- }
-
- // For each one, let's create a new line.
- const fragment = document.createDocumentFragment();
- for (let tab of tabs) { // eslint-disable-line prefer-const
- const tr = document.createElement("tr");
- fragment.appendChild(tr);
- tr.classList.add("menu-item", "hover-highlight", "keyboard-nav");
- tr.setAttribute("tabindex", "0");
- tr.innerHTML = Utils.escaped`
-
-
-
-
- | `;
- tr.querySelector(".favicon").appendChild(Utils.createFavIconElement(tab.favIconUrl));
- tr.setAttribute("tabindex", "0");
- table.appendChild(fragment);
-
- // On click, we activate this tab. But only if this tab is active.
- if (!tab.hiddenState) {
- Utils.addEnterHandler(tr, async () => {
- await browser.tabs.update(tab.id, { active: true });
- window.close();
- });
-
- const closeTab = tr.querySelector(".trash-button");
- if (closeTab) {
- Utils.addEnterHandler(closeTab, async (e) => {
- await browser.tabs.remove(Number(e.target.id));
- window.close();
- });
- }
- }
- }
- },
-});
-
-// OPEN_NEW_CONTAINER_PICKER: Opens a new container tab.
-// ----------------------------------------------------------------------------
-
-Logic.registerPanel(OPEN_NEW_CONTAINER_PICKER, {
- panelSelector: "#container-picker-panel",
-
- // This method is called when the object is registered.
- initialize() {
- },
-
- // This method is called when the panel is shown.
- prepare() {
- Logic.listenToPickerBackButton();
- document.getElementById("picker-title").textContent = browser.i18n.getMessage("openANewTabIn");
- const fragment = document.createDocumentFragment();
- const pickedFunction = function (identity) {
- try {
- browser.tabs.create({
- cookieStoreId: identity.cookieStoreId
- });
- window.close();
- } catch (e) {
- window.close();
- }
- };
-
- document.getElementById("new-container-div").innerHTML = "";
-
- Logic.identities().forEach(identity => {
- const tr = document.createElement("tr");
- tr.classList.add("menu-item", "hover-highlight", "keyboard-nav");
- tr.setAttribute("tabindex", "0");
- const td = document.createElement("td");
-
- td.innerHTML = Utils.escaped`
-
- `;
-
- fragment.appendChild(tr);
-
- tr.appendChild(td);
-
- Utils.addEnterHandler(tr, () => {
- pickedFunction(identity);
- });
-
- });
-
- const list = document.querySelector("#picker-identities-list");
-
- list.innerHTML = "";
- list.appendChild(fragment);
-
- return Promise.resolve(null);
- }
-});
-
-// MANAGE_CONTAINERS_PICKER: Makes the list editable.
-// ----------------------------------------------------------------------------
-
-Logic.registerPanel(MANAGE_CONTAINERS_PICKER, {
- panelSelector: "#container-picker-panel",
-
- // This method is called when the object is registered.
- initialize() {
- },
-
- // This method is called when the panel is shown.
- async prepare() {
- Logic.listenToPickerBackButton();
- const closeContEl = document.querySelector("#close-container-picker-panel");
- if (!this._listenerSet) {
- Utils.addEnterHandler(closeContEl, () => {
- Logic.showPanel(P_CONTAINERS_LIST);
- });
- this._listenerSet = true;
- }
- document.getElementById("picker-title").textContent = browser.i18n.getMessage("manageContainers");
- const fragment = document.createDocumentFragment();
- const pickedFunction = function (identity) {
- Logic.showPanel(P_CONTAINER_EDIT, identity);
- };
-
- document.getElementById("new-container-div").innerHTML = Utils.escaped`
-
-
- `;
-
- Utils.addEnterHandler(document.querySelector("#new-container"), () => {
- Logic.showPanel(P_CONTAINER_EDIT, { name: Logic.generateIdentityName() });
- });
-
- const identities = Logic.identities();
-
- for (const identity of identities) {
- const tr = document.createElement("tr");
- tr.classList.add("menu-item", "hover-highlight", "keyboard-nav");
- tr.setAttribute("tabindex", "0");
- tr.setAttribute("data-cookie-store-id", identity.cookieStoreId);
-
- const td = document.createElement("td");
-
- td.innerHTML = Utils.escaped`
-
-
-
-
-
- `;
-
- fragment.appendChild(tr);
-
- tr.appendChild(td);
-
- tr.draggable = true;
- tr.dataset.containerId = identity.cookieStoreId;
- tr.addEventListener("dragstart", (e) => {
- e.dataTransfer.setData(CONTAINER_DRAG_DATA_TYPE, identity.cookieStoreId);
- });
- tr.addEventListener("dragover", (e) => {
- if (e.dataTransfer.types.includes(CONTAINER_DRAG_DATA_TYPE)) {
- tr.classList.add("drag-over");
- e.preventDefault();
- }
- });
- tr.addEventListener("dragenter", (e) => {
- if (e.dataTransfer.types.includes(CONTAINER_DRAG_DATA_TYPE)) {
- e.preventDefault();
- tr.classList.add("drag-over");
- }
- });
- tr.addEventListener("dragleave", (e) => {
- if (e.dataTransfer.types.includes(CONTAINER_DRAG_DATA_TYPE)) {
- e.preventDefault();
- tr.classList.remove("drag-over");
- }
- });
- tr.addEventListener("drop", async (e) => {
- e.preventDefault();
- const parent = tr.parentNode;
- const containerId = e.dataTransfer.getData(CONTAINER_DRAG_DATA_TYPE);
- let droppedElement;
- parent.childNodes.forEach((node) => {
- if (node.dataset.containerId === containerId) {
- droppedElement = node;
- }
- });
- if (droppedElement && droppedElement !== tr) {
- tr.classList.remove("drag-over");
- parent.insertBefore(droppedElement, tr);
- await Logic.saveContainerOrder(parent.childNodes);
- await Logic.refreshIdentities();
- }
- });
-
- Utils.addEnterHandler(tr, () => {
- pickedFunction(identity);
- });
- }
-
- const list = document.querySelector("#picker-identities-list");
-
- list.innerHTML = "";
- list.appendChild(fragment);
-
- MozillaVPN.handleContainerList(identities);
-
- return Promise.resolve();
- }
-});
-
-// REOPEN_IN_CONTAINER_PICKER: Makes the list editable.
-// ----------------------------------------------------------------------------
-
-Logic.registerPanel(REOPEN_IN_CONTAINER_PICKER, {
- panelSelector: "#container-picker-panel",
-
- // This method is called when the object is registered.
- initialize() {
- },
-
- // This method is called when the panel is shown.
- async prepare() {
- Logic.listenToPickerBackButton();
- document.getElementById("picker-title").textContent = browser.i18n.getMessage("reopenThisSiteIn");
- const fragment = document.createDocumentFragment();
- const currentTab = await Utils.currentTab();
- const pickedFunction = function (identity) {
- const newUserContextId = Utils.userContextId(identity.cookieStoreId);
- Utils.reloadInContainer(
- currentTab.url,
- false,
- newUserContextId,
- currentTab.index + 1,
- currentTab.active
- );
- window.close();
- };
-
- document.getElementById("new-container-div").innerHTML = "";
-
- if (currentTab.cookieStoreId !== "firefox-default") {
- const tr = document.createElement("tr");
- tr.classList.add("menu-item", "hover-highlight", "keyboard-nav");
- tr.setAttribute("tabindex", "0");
- const td = document.createElement("td");
-
- td.innerHTML = Utils.escaped`
-
- `;
-
- fragment.appendChild(tr);
-
- tr.appendChild(td);
-
- Utils.addEnterHandler(tr, () => {
- Utils.reloadInContainer(
- currentTab.url,
- false,
- 0,
- currentTab.index + 1,
- currentTab.active
- );
- window.close();
- });
- }
-
- Logic.identities().forEach(identity => {
- if (currentTab.cookieStoreId !== identity.cookieStoreId) {
- const tr = document.createElement("tr");
- tr.classList.add("menu-item", "hover-highlight", "keyboard-nav");
- tr.setAttribute("tabindex", "0");
- const td = document.createElement("td");
-
- td.innerHTML = Utils.escaped`
-
- `;
-
- fragment.appendChild(tr);
-
- tr.appendChild(td);
-
- Utils.addEnterHandler(tr, () => {
- pickedFunction(identity);
- });
- }
- });
-
- const list = document.querySelector("#picker-identities-list");
-
- list.innerHTML = "";
- list.appendChild(fragment);
-
- return Promise.resolve(null);
- }
-});
-
-// ALWAYS_OPEN_IN_PICKER: Makes the list editable.
-// ----------------------------------------------------------------------------
-
-Logic.registerPanel(ALWAYS_OPEN_IN_PICKER, {
- panelSelector: "#container-picker-panel",
-
- // This method is called when the object is registered.
- initialize() {
- },
-
- // This method is called when the panel is shown.
- async prepare() {
- const identities = Logic.identities();
- Logic.listenToPickerBackButton();
- document.getElementById("picker-title").textContent = browser.i18n.getMessage("alwaysOpenIn");
- const fragment = document.createDocumentFragment();
-
- document.getElementById("new-container-div").innerHTML = "";
-
- for (const identity of identities) {
- const tr = document.createElement("tr");
- tr.classList.add("menu-item", "hover-highlight", "keyboard-nav");
- tr.setAttribute("tabindex", "0");
- const td = document.createElement("td");
-
- td.innerHTML = Utils.escaped`
-
-
- `;
-
- fragment.appendChild(tr);
-
- tr.appendChild(td);
-
- Utils.addEnterHandler(tr, () => {
- Utils.alwaysOpenInContainer(identity);
- window.close();
- });
- }
-
- const list = document.querySelector("#picker-identities-list");
-
- list.innerHTML = "";
- list.appendChild(fragment);
-
- return Promise.resolve(null);
- }
-});
-
-// P_CONTAINER_ASSIGNMENTS: Shows Site Assignments and allows editing.
-// ----------------------------------------------------------------------------
-
-Logic.registerPanel(P_CONTAINER_ASSIGNMENTS, {
- panelSelector: "#edit-container-assignments",
-
- // This method is called when the object is registered.
- initialize() { },
-
- // This method is called when the panel is shown.
- async prepare() {
- const identity = Logic.currentIdentity();
-
- // Populating the panel: name and icon
- document.getElementById("edit-assignments-title").textContent = identity.name;
-
- const userContextId = Logic.currentUserContextId();
- const assignments = await Logic.getAssignmentObjectByContainer(userContextId);
- this.showAssignedContainers(assignments);
-
- return Promise.resolve(null);
- },
-
- showAssignedContainers(assignments) {
- const closeContEl = document.querySelector("#close-container-assignment-panel");
- Utils.addEnterHandler(closeContEl, () => {
- const identity = Logic.currentIdentity();
- Logic.showPanel(P_CONTAINER_EDIT, identity, false, false);
- });
-
- const assignmentPanel = document.getElementById("edit-sites-assigned");
- const assignmentKeys = Object.keys(assignments);
- assignmentPanel.hidden = !(assignmentKeys.length > 0);
- if (assignments) {
- const tableElement = document.querySelector("#edit-sites-assigned");
- /* Remove previous assignment list,
- after removing one we rerender the list */
- while (tableElement.firstChild) {
- tableElement.firstChild.remove();
- }
- assignmentKeys.forEach((siteKey) => {
- const site = assignments[siteKey];
- const trElement = document.createElement("tr");
- /* As we don't have the full or correct path the best we can assume is the path is HTTPS and then replace with a broken icon later if it doesn't load.
- This is pending a better solution for favicons from web extensions */
- const assumedUrl = `https://${site.hostname}/favicon.ico`;
- trElement.innerHTML = Utils.escaped`
-
-
-
-
- | `;
- trElement.getElementsByClassName("favicon")[0].appendChild(Utils.createFavIconElement(assumedUrl));
- const deleteButton = trElement.querySelector(".trash-button");
- Utils.addEnterHandler(deleteButton, async () => {
- const userContextId = Logic.currentUserContextId();
- // Lets show the message to the current tab
- // const currentTab = await Utils.currentTab();
- Utils.setOrRemoveAssignment(false, assumedUrl, userContextId, true);
- delete assignments[siteKey];
- this.showAssignedContainers(assignments);
- });
- trElement.classList.add("menu-item", "hover-highlight", "keyboard-nav");
- tableElement.appendChild(trElement);
- });
- }
- },
-});
-
-// P_CONTAINER_EDIT: Editor for a container.
-// ----------------------------------------------------------------------------
-
-Logic.registerPanel(P_CONTAINER_EDIT, {
- panelSelector: "#edit-container-panel",
-
- // This method is called when the object is registered.
- async initialize() {
- this.initializeRadioButtons();
-
- await browser.runtime.sendMessage({ method: "MozillaVPN_queryServers" });
- await browser.runtime.sendMessage({ method: "MozillaVPN_queryStatus" });
-
- class MozVpnContainerUi extends HTMLElement {
- constructor() {
- super();
-
- this.subtitle = this.querySelector(".moz-vpn-subtitle");
- this.collapsibleContent = this.querySelector(".collapsible-content");
-
- this.visibilityTogglers = this.querySelectorAll(".hide-show-label");
- this.hideShowButton = this.querySelector(".expand-collapse");
- this.primaryCta = this.querySelector("#get-mozilla-vpn");
- this.advancedProxySettingsButton = document.querySelector(".advanced-proxy-settings-btn");
- this.toutName = "moz-tout-edit-container-panel";
-
- // Switch
- this.switch = this.querySelector("#moz-vpn-switch");
- this.switchLabel = this.querySelector(".switch");
-
- // Current server button
- this.currentServerButton = this.querySelector("#moz-vpn-current-server");
- this.currentCityName = this.querySelector(".current-city-name");
- this.currentCountryFlag = this.querySelector(".current-country-flag");
- this.currentCountryCode;
-
- // Proxy inputs + viewer
- this.advancedProxyAddress = document.getElementById("advanced-proxy-address");
- this.proxyAddressInput = document.querySelector("#edit-container-panel-proxy");
- this.cityNameInput = document.getElementById("city-name-input");
- this.countryCodeInput = document.getElementById("country-code-input");
- this.mozProxyEnabledInput = document.getElementById("moz-proxy-enabled");
- }
-
- async connectedCallback() {
- const { mozillaVpnHiddenToutsList } = await browser.storage.local.get("mozillaVpnHiddenToutsList");
- const mozillaVpnCollapseEditContainerTout = mozillaVpnHiddenToutsList && mozillaVpnHiddenToutsList.find(tout => tout.name === this.toutName);
- const mozillaVpnInstalled = await browser.runtime.sendMessage({ method: "MozillaVPN_getInstallationStatus" });
-
- this.hideShowButton.addEventListener("click", this);
-
- if (mozillaVpnCollapseEditContainerTout && !mozillaVpnInstalled) {
- this.collapseUi();
- }
-
- // Add listeners
- if (!this.classList.contains("has-attached-listeners")) {
-
- const bothMozillaVpnPermissionsEnabled = await MozillaVPN.bothPermissionsEnabled();
- this.primaryCta.addEventListener("click", async() => {
- if (!bothMozillaVpnPermissionsEnabled && mozillaVpnInstalled) {
- await browser.permissions.request({ permissions: ["proxy", "nativeMessaging"] });
- } else {
- MozillaVPN.handleMozillaCtaClick("mac-edit-container-panel-btn");
- }
-
- });
-
- this.switch.addEventListener("click", async() => {
- const { mozillaVpnServers } = await browser.storage.local.get("mozillaVpnServers");
- const id = Logic.currentIdentity();
- this.enableDisableProxyButtons();
-
- if (!this.switch.checked) {
- const deactivatedMozProxy = MozillaVPN.getProxy(
- this.countryCodeInput.value,
- this.cityNameInput.value,
- undefined,
- mozillaVpnServers
- );
-
- if (!deactivatedMozProxy) {
- return;
- }
-
- await proxifiedContainers.set(id.cookieStoreId, deactivatedMozProxy);
- this.switch.checked = false;
- return;
- }
- let proxy;
-
- if (this.countryCodeInput.value.length === 2) {
- // User is re-enabling a Mozilla proxy for this container.
- // Use the stored location information to select a server
- // in the same location.
- proxy = MozillaVPN.getProxy(
- this.countryCodeInput.value,
- this.cityNameInput.value,
- true,
- mozillaVpnServers
- );
-
- } else {
- // No saved Mozilla VPN proxy information. Get something new.
- const { randomServerCountryCode, randomServerCityName } = await MozillaVPN.pickRandomLocation();
-
- proxy = MozillaVPN.getProxy(
- randomServerCountryCode,
- randomServerCityName,
- true,
- mozillaVpnServers
- );
- }
-
- if (proxy) {
- await proxifiedContainers.set(id.cookieStoreId, proxy);
- this.switch.checked = true;
- this.updateProxyDependentUi(proxy);
- } else {
- this.switch.checked = false;
- this.updateProxyDependentUi({});
- return;
- }
- });
- }
-
- this.classList.add("has-attached-listeners");
- this.currentServerButton.classList.add("hidden");
- }
-
- async updateMozVpnStatusDependentUi() {
- const mozillaVpnInstalled = await browser.runtime.sendMessage({ method: "MozillaVPN_getInstallationStatus" });
- const mozillaVpnConnected = await browser.runtime.sendMessage({ method: "MozillaVPN_getConnectionStatus" });
-
- this.subtitle.textContent = browser.i18n.getMessage("integrateContainers");
-
- const bothMozillaVpnPermissionsEnabled = await MozillaVPN.bothPermissionsEnabled();
-
- if (mozillaVpnInstalled && !bothMozillaVpnPermissionsEnabled) {
- this.subtitle.style.flex = "1 1 100%";
- this.classList.remove("show-server-button");
- this.subtitle.textContent = browser.i18n.getMessage("additionalPermissionNeeded");
- this.hideEls(this.hideShowButton, this.switch, this.switchLabel, this.currentServerButton);
- this.primaryCta.style.display = "block";
- this.primaryCta.textContent = browser.i18n.getMessage("enable");
- return;
- }
-
- if (mozillaVpnInstalled) {
- // Hide cta and hide/show button
- this.hideEls(this.primaryCta, this.hideShowButton);
-
- // Update subtitle
- this.subtitle.textContent = mozillaVpnConnected ? browser.i18n.getMessage("useCustomLocation") : browser.i18n.getMessage("mozillaVpnMustBeOn");
- this.subtitle.style.flex = "1 1 80%";
- this.currentServerButton.style.display = "flex";
- }
-
- if (mozillaVpnConnected) {
- [this.switchLabel, this.switch].forEach(el => {
- el.style.display = "inline-block";
- });
- } else {
- this.hideEls(this.switch, this.switchLabel, this.currentServerButton);
- this.switch.checked = false;
- }
-
- if ((mozillaVpnInstalled && !mozillaVpnConnected) || mozillaVpnConnected) {
- this.expandUi();
- }
- }
-
-
- async enableDisableProxyButtons() {
- const mozillaVpnConnected = await browser.runtime.sendMessage({ method: "MozillaVPN_getConnectionStatus" });
-
- if (!this.switch.checked || this.switch.disabled || !mozillaVpnConnected) {
- this.currentServerButton.disabled = true;
- this.advancedProxySettingsButton.disabled = false;
- document.getElementById("moz-proxy-enabled").value = undefined;
- return;
- }
-
- this.currentServerButton.disabled = false;
- this.advancedProxySettingsButton.disabled = true;
- this.advancedProxyAddress.textContent = "";
- }
-
- updateProxyInputs(proxyInfo) {
- const resetProxyStorageEls = () => {
- [this.proxyAddressInput, this.cityNameInput, this.countryCodeInput, this.mozProxyEnabledInput].forEach(el => {
- el.value = "";
-
- });
- this.advancedProxyAddress.textContent = "";
- };
-
- resetProxyStorageEls();
-
- if (typeof(proxyInfo) === "undefined" || typeof(proxyInfo.type) === "undefined") {
- // no custom proxy is set
- return;
- }
-
- this.cityNameInput.value = proxyInfo.cityName;
- this.countryCodeInput.value = proxyInfo.countryCode;
- this.mozProxyEnabledInput.value = proxyInfo.mozProxyEnabled;
- this.proxyAddressInput.value = `${proxyInfo.type}://${proxyInfo.host}:${proxyInfo.port}`;
-
- if (typeof(proxyInfo.countryCode) === "undefined" && proxyInfo.type) {
- // Set custom proxy URL below 'Advanced proxy settings' button label
- this.advancedProxyAddress.textContent = `${proxyInfo.type}://${proxyInfo.host}:${proxyInfo.port}`;
- }
- }
-
- async updateProxyDependentUi(proxyInfo) {
- const mozillaVpnProxyLocationAvailable = (proxy) => {
- return typeof(proxy) !== "undefined" && typeof(proxy.countryCode) !== "undefined" && typeof(proxy.cityName) !== "undefined";
- };
-
- const mozillaVpnProxyIsEnabled = (proxy) => {
- return typeof(proxy) !== "undefined" && typeof(proxy.mozProxyEnabled) !== "undefined" && proxy.mozProxyEnabled === true;
- };
-
- this.switch.checked = mozillaVpnProxyIsEnabled(proxyInfo);
- this.updateProxyInputs(proxyInfo);
- this.enableDisableProxyButtons();
-
- const mozillaVpnConnected = await browser.runtime.sendMessage({ method: "MozillaVPN_getConnectionStatus" });
- if (
- !proxyInfo ||
- !mozillaVpnProxyLocationAvailable(proxyInfo) ||
- !mozillaVpnConnected
- ) {
- // Hide server location button
- this.currentServerButton.classList.add("hidden");
- this.classList.remove("show-server-button");
- } else {
- // Unhide server location button
- this.currentServerButton.style.display = "flex";
- this.currentServerButton.classList.remove("hidden");
- this.classList.add("show-server-button");
- }
-
- // Populate inputs and server button with current or previously stored mozilla vpn proxy
- if(proxyInfo && mozillaVpnProxyLocationAvailable(proxyInfo)) {
- this.currentCountryFlag.style.backgroundImage = `url("./img/flags/${proxyInfo.countryCode.toUpperCase()}.png")`;
- this.currentCountryFlag.style.backgroundImage = proxyInfo.countryCode + ".png";
- this.currentCityName.textContent = proxyInfo.cityName;
- this.countryCode = proxyInfo.countryCode;
- }
- }
-
- expandUi() {
- this.classList.add("expanded");
- }
-
- collapseUi() {
- this.classList.remove("expanded");
- }
-
- hideEls(...els) {
- els.forEach(el => {
- el.style.display = "none";
- });
- }
-
- async handleEvent(e) {
- e.preventDefault();
- e.stopPropagation();
- if (e.type === "keyup" && e.key !== " ") {
- return;
- }
- this.classList.toggle("expanded");
-
- const { mozillaVpnHiddenToutsList } = await browser.storage.local.get("mozillaVpnHiddenToutsList");
- if (typeof(mozillaVpnHiddenToutsList) === "undefined") {
- await browser.storage.local.set({ "mozillaVpnHiddenToutsList":[] });
- }
-
- const toutIndex = mozillaVpnHiddenToutsList.findIndex(tout => tout.name === mozillaVpnUi.toutName);
- if (toutIndex === -1) {
- mozillaVpnHiddenToutsList.push({ name: mozillaVpnUi.toutName });
- } else {
- this.expandUi();
- mozillaVpnHiddenToutsList.splice(toutIndex, 1);
- }
- return await browser.storage.local.set({ mozillaVpnHiddenToutsList });
- }
-
- }
-
- customElements.define("moz-vpn-container-ui", MozVpnContainerUi);
- const mozillaVpnUi = document.querySelector("moz-vpn-container-ui");
- mozillaVpnUi.updateMozVpnStatusDependentUi();
-
- browser.permissions.onAdded.addListener(() => { mozillaVpnUi.updateMozVpnStatusDependentUi(); });
- browser.permissions.onRemoved.addListener(() => { mozillaVpnUi.updateMozVpnStatusDependentUi(); });
-
- const advancedProxySettingsButton = document.querySelector(".advanced-proxy-settings-btn");
- Utils.addEnterHandler(advancedProxySettingsButton, () => {
- Logic.showPanel(P_ADVANCED_PROXY_SETTINGS, this.getEditInProgressIdentity(), false, false);
- });
-
- const serverListButton = document.getElementById("moz-vpn-current-server");
- Utils.addEnterHandler(serverListButton, () => {
- const mozVpnEnabled = document.querySelector("#moz-vpn-switch").checked;
- if (!mozVpnEnabled) {
- return;
- }
- Logic.showPanel(P_MOZILLA_VPN_SERVER_LIST, this.getEditInProgressIdentity(), false);
- });
-
- Utils.addEnterHandler(document.querySelector("#close-container-edit-panel"), () => {
- // Resets listener from siteIsolation checkbox to keep the update queue to 0.
- const siteIsolation = document.querySelector("#site-isolation");
- siteIsolation.removeEventListener("change", addRemoveSiteIsolation, false);
- const formValues = new FormData(this._editForm);
- if (formValues.get("container-id") !== NEW_CONTAINER_ID) {
- this._submitForm();
- } else {
- Logic.showPreviousPanel();
- }
- });
-
- this._editForm = document.getElementById("edit-container-panel-form");
- this._editForm.addEventListener("submit", () => {
- this._submitForm();
- });
- Utils.addEnterHandler(document.querySelector("#create-container-cancel-link"), () => {
- Logic.showPanel(MANAGE_CONTAINERS_PICKER);
- });
-
- Utils.addEnterHandler(document.querySelector("#create-container-ok-link"), () => {
- this._submitForm();
- });
- },
-
- async _submitForm() {
- const formValues = new FormData(this._editForm);
-
- try {
- await browser.runtime.sendMessage({
- method: "createOrUpdateContainer",
- message: {
- userContextId: formValues.get("container-id") || NEW_CONTAINER_ID,
- params: {
- name: document.getElementById("edit-container-panel-name-input").value || Logic.generateIdentityName(),
- icon: formValues.get("container-icon") || DEFAULT_ICON,
- color: formValues.get("container-color") || DEFAULT_COLOR
- },
- }
- });
- await Logic.refreshIdentities();
- Logic.showPreviousPanel();
- } catch (e) {
- Logic.showPreviousPanel();
- }
- },
-
- openServerList() {
- const updatedIdentity = this.getEditInProgressIdentity();
- Logic.showPanel(P_MOZILLA_VPN_SERVER_LIST, updatedIdentity, false);
- },
-
- // This prevents identity edits (change of icon, color, etc)
- // from getting lost when navigating to and from one
- // of the edit sub-pages (advanced proxy settings, for instance).
- getEditInProgressIdentity() {
- const formValues = new FormData(this._editForm);
- const editedIdentity = Logic.currentIdentity();
-
- editedIdentity.color = formValues.get("container-color") || DEFAULT_COLOR;
- editedIdentity.icon = formValues.get("container-icon") || DEFAULT_ICON;
- editedIdentity.name = document.getElementById("edit-container-panel-name-input").value || Logic.generateIdentityName();
- return editedIdentity;
- },
-
- initializeRadioButtons() {
- const colorRadioTemplate = (containerColor) => {
- return Utils.escaped`
-