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 @@ -account \ 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 @@ -Sync \ 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 @@ - Created with Sketch. \ 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 @@ - - - - - Arrow - Created with Sketch. - - - - - - - - - - - - - - - - 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 @@ - - - - - Arrow - Created with Sketch. - - - - - - - - - - - - - - - - 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 @@ - - - - - -icon-newtab - - - - - - - - - - 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 @@ - - - - - - image/svg+xml - - - - - - - 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` - - ${identity.name} - - `; - - 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` - - - - ${openTabs} - - Container Info - - - `; - - - - 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` - -
- ${tab.title} - - `; - 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` - - ${identity.name}`; - - 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` - - ${identity.name} - - - - `; - - 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` - - Default Container`; - - 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` - - ${identity.name}`; - - 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` - - ${identity.name} - `; - - 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` - -
- ${site.hostname} - - `; - 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` -