From 747df1c425acefe6d35cc510de2eaa20506b8b77 Mon Sep 17 00:00:00 2001 From: Brad Sharp Date: Wed, 10 May 2023 18:38:35 -0700 Subject: [PATCH] Add option to prompt container selection --- src/css/select-page.css | 107 +++++++++++++++++++++++++++++ src/js/background/assignManager.js | 45 ++++++++++-- src/js/options.js | 8 +++ src/js/select-page.js | 83 ++++++++++++++++++++++ src/options.html | 5 ++ src/select-page.html | 32 +++++++++ 6 files changed, 275 insertions(+), 5 deletions(-) create mode 100644 src/css/select-page.css create mode 100644 src/js/select-page.js create mode 100644 src/select-page.html diff --git a/src/css/select-page.css b/src/css/select-page.css new file mode 100644 index 0000000..e183b43 --- /dev/null +++ b/src/css/select-page.css @@ -0,0 +1,107 @@ +/* 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; +} + +@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; +} + +.check-label { + align-items: center; + display: flex; +} + +#redirect-identities { + display: flex; + flex-wrap: wrap; + gap: 7.5px; +} + +#redirect-identities button { + flex-grow: 1; + flex-basis: 180px; + margin: 0; + padding: 7.5px; + height: 60px; + display: flex; + align-items: center; + gap: 7.5px; +} + +#redirect-identities button .primary { + flex-grow: 2; + flex-basis: 360px; +} + +#redirect-identities button img { + position: relative; + width: 30px; + height: 30px; + filter: url('/img/filters.svg#fill'); +} + +#redirect-identities button label { + position: relative; + flex-grow: 1; +} \ No newline at end of file diff --git a/src/js/background/assignManager.js b/src/js/background/assignManager.js index 058329a..86d98cc 100644 --- a/src/js/background/assignManager.js +++ b/src/js/background/assignManager.js @@ -56,6 +56,11 @@ window.assignManager = { return !!replaceTabEnabled; }, + async getContainerPromptEnabled() { + const { containerPromptEnabled } = await browser.storage.local.get("containerPromptEnabled"); + return !!containerPromptEnabled; + }, + getByUrlKey(siteStoreKey) { return new Promise((resolve, reject) => { this.area.get([siteStoreKey]).then((storageResponse) => { @@ -258,13 +263,18 @@ window.assignManager = { const siteIsolatedReloadInDefault = await this._maybeSiteIsolatedReloadInDefault(siteSettings, tab); - if (!siteIsolatedReloadInDefault) { - if (!siteSettings - || userContextId === siteSettings.userContextId - || this.storageArea.isExempted(options.url, tab.id)) { + // The site is not associated with a container and a container is not already + // being used to access it. In this case, we want to prompt the user to select + // a container before continuing. + const containerPromptEnabled = await this.storageArea.getContainerPromptEnabled(); + const promptReloadInContainer = containerPromptEnabled && !siteSettings && !userContextId; + + if (!siteIsolatedReloadInDefault && !promptReloadInContainer) { + 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 @@ -314,7 +324,15 @@ window.assignManager = { } } - if (siteIsolatedReloadInDefault) { + if (promptReloadInContainer) + { + this.promptReloadPageInContainer( + options.url, + tab.index + 1, + tab.active, + openTabId + ); + } else if (siteIsolatedReloadInDefault) { this.reloadPageInDefaultContainer( options.url, tab.index + 1, @@ -332,6 +350,7 @@ window.assignManager = { openTabId ); } + this.calculateContextMenu(tab); /* Removal of existing tabs: @@ -732,6 +751,22 @@ window.assignManager = { browser.tabs.create({url, cookieStoreId, index, active, openerTabId}); }, + promptReloadPageInContainer(url, index, active, openerTabId = null) { + const loadPage = browser.runtime.getURL("select-page.html"); + let confirmUrl = `${loadPage}?url=${this.encodeURLProperty(url)}`; + return browser.tabs.create({ + url: confirmUrl, + openerTabId, + index, + active + }).then(async () => { + // We don't want to sync this URL ever nor clutter the users history + browser.history.deleteUrl({url: confirmUrl}); + }).catch((e) => { + throw e; + }); + }, + reloadPageInContainer(url, currentUserContextId, userContextId, index, active, neverAsk = false, openerTabId = null) { const cookieStoreId = backgroundLogic.cookieStoreId(userContextId); const loadPage = browser.runtime.getURL("confirm-page.html"); diff --git a/src/js/options.js b/src/js/options.js index 7679e2b..5ed9a57 100644 --- a/src/js/options.js +++ b/src/js/options.js @@ -53,6 +53,11 @@ async function enableDisableReplaceTab() { await browser.storage.local.set({replaceTabEnabled: !!checkbox.checked}); } +async function enableDisableContainerPrompt() { + const checkbox = document.querySelector("#containerPromptCheck"); + await browser.storage.local.set({containerPromptEnabled: !!checkbox.checked}); +} + async function changeTheme(event) { const theme = event.currentTarget; await browser.storage.local.set({currentTheme: theme.value}); @@ -62,10 +67,12 @@ async function changeTheme(event) { async function setupOptions() { const { syncEnabled } = await browser.storage.local.get("syncEnabled"); const { replaceTabEnabled } = await browser.storage.local.get("replaceTabEnabled"); + const { containerPromptEnabled } = await browser.storage.local.get("containerPromptEnabled"); const { currentThemeId } = await browser.storage.local.get("currentThemeId"); document.querySelector("#syncCheck").checked = !!syncEnabled; document.querySelector("#replaceTabCheck").checked = !!replaceTabEnabled; + document.querySelector("#containerPromptCheck").checked = !!containerPromptEnabled; document.querySelector("#changeTheme").selectedIndex = currentThemeId; setupContainerShortcutSelects(); } @@ -123,6 +130,7 @@ browser.permissions.onRemoved.addListener(resetPermissionsUi); document.addEventListener("DOMContentLoaded", setupOptions); document.querySelector("#syncCheck").addEventListener( "change", enableDisableSync); document.querySelector("#replaceTabCheck").addEventListener( "change", enableDisableReplaceTab); +document.querySelector("#containerPromptCheck").addEventListener( "change", enableDisableContainerPrompt); document.querySelector("#changeTheme").addEventListener( "change", changeTheme); maybeShowPermissionsWarningIcon(); diff --git a/src/js/select-page.js b/src/js/select-page.js new file mode 100644 index 0000000..3e3b00c --- /dev/null +++ b/src/js/select-page.js @@ -0,0 +1,83 @@ +async function load() { + const searchParams = new URL(window.location).searchParams; + const redirectUrl = searchParams.get("url"); + const redirectUrlElement = document.getElementById("redirect-url"); + redirectUrlElement.textContent = redirectUrl; + appendFavicon(redirectUrl, redirectUrlElement); + + const identities = await browser.contextualIdentities.query({}); + const redirectFormElement = document.getElementById("redirect-identities"); + + identities.forEach(identity => { + const cookieStoreId = identity.cookieStoreId; + var containerButtonElement = document.createElement("button"); + containerButtonElement.classList.add("button"); + containerButtonElement.id = "container-" + identity.name; + containerButtonElement.setAttribute("container-id", cookieStoreId); + containerButtonElement.addEventListener("click", e => { + e.preventDefault(); + selectContainer(redirectUrl, identity) + }) + var containerIconElement = document.createElement("img"); + containerIconElement.src = identity.iconUrl; + containerIconElement.alt = identity.icon; + containerIconElement.style.fill = identity.colorCode; + var containerLabelElement = document.createElement("label"); + containerLabelElement.textContent = identity.name; + containerButtonElement.appendChild(containerIconElement); + containerButtonElement.appendChild(containerLabelElement); + redirectFormElement.appendChild(containerButtonElement); + }) + + document.querySelectorAll("[data-message-id]").forEach(el => { + const elementData = el.dataset; + el.textContent = browser.i18n.getMessage(elementData.messageId); + }); +} + +function appendFavicon(pageUrl, redirectUrlElement) { + const origin = new URL(pageUrl).origin; + const favIconElement = Utils.createFavIconElement(`${origin}/favicon.ico`); + + redirectUrlElement.prepend(favIconElement); +} + +function getCurrentTab() { + return browser.tabs.query({ + active: true, + windowId: browser.windows.WINDOW_ID_CURRENT + }); +} + +load(); + +function selectContainer(redirectUrl, identity){ + const neverAsk = document.getElementById("never-ask").checked; + if (neverAsk) { + assignContainer(redirectUrl, identity); + } + openInContainer(redirectUrl, identity.cookieStoreId); +} + +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); + } +} + +async function assignContainer(redirectUrl, identity) { + const currentTab = await Utils.currentTab(); + const assignedUserContextId = Utils.userContextId(identity.cookieStoreId); + await Utils.setOrRemoveAssignment( + null, + redirectUrl, + assignedUserContextId, + false + ); +} \ No newline at end of file diff --git a/src/options.html b/src/options.html index c8b8ff9..b67356d 100644 --- a/src/options.html +++ b/src/options.html @@ -57,6 +57,11 @@

+ +