diff --git a/README.md b/README.md index 47dce0c..870cd7d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ The Firefox Multi-Account Containers extension lets you carve out a separate box for each of your online lives – no more opening a different browser just to check your work email! [Learn More Here](https://blog.mozilla.org/firefox/introducing-firefox-multi-account-containers/) -[Available on addons.mozilla.org](https://addons.mozilla.org/en-GB/firefox/addon/multi-account-containers/) +[Available on addons.mozilla.org](https://addons.mozilla.org/firefox/addon/multi-account-containers/) For more info, see: @@ -18,17 +18,43 @@ For more info, see: ## Development -1. `npm install` -2. `./node_modules/web-ext/bin/web-ext run -s src/` +### Running Locally + +#### Via WebExtensions API (web-ext) + +1. Install the [web-ext](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Getting_started_with_web-ext) tool. +2. Run `web-ext run -s src/`. This launches Firefox and installs the extension automatically. + +This tool provides some additional development features, such as [automatic reloading](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Getting_started_with_web-ext#Automatic_extension_reloading). + +#### Via about:debugging in Firefox + +1. Open the `about:debugging` page in Firefox. +2. Click on `This Firefox`. +3. Click on [Load Temporary Add-on](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Temporary_Installation_in_Firefox). +4. Select `src/manifest.json`. + +Here is a [video](https://www.youtube.com/watch?v=cer9EUKegG4) that demonstrates how to do this. ### Testing -`npm run test` -or +* Install dependencies: -`npm run lint` + ``` + npm install + ``` -for just the linter +* Run all tests: + + ``` + npm run test + ``` + +* Only run the linter: + + ``` + npm run lint + ``` There is a timeout test that sometimes fails on certain machines, so make sure to run the tests on your clone before you make any changes to see if you have this problem. @@ -43,12 +69,12 @@ There is a timeout test that sometimes fails on certain machines, so make sure t #### Publish to AMO 1. `npm run-script build` -2. [Upload the `.zip` to AMO](https://addons.mozilla.org/en-US/developers/addon/multi-account-containers/versions/submit/) +2. [Upload the `.zip` to AMO](https://addons.mozilla.org/developers/addon/multi-account-containers/versions/submit/) #### Publish to GitHub Finally, we also publish the release to GitHub for those followers. -1. Download the signed `.xpi` from [the addon versions page](https://addons.mozilla.org/en-US/developers/addon/multi-account-containers/versions) +1. Download the signed `.xpi` from [the addon versions page](https://addons.mozilla.org/developers/addon/multi-account-containers/versions) 2. [Make the new release on GitHub](https://github.com/mozilla/multi-account-containers/releases/new) * Use the version number for "Tag version" and "Release title" diff --git a/package.json b/package.json index 988a762..26d2e1a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "testpilot-containers", "title": "Multi-Account Containers", "description": "Containers helps you keep all the parts of your online life contained in different tabs. Custom labels and color-coded tabs help keep different activities — like online shopping, travel planning, or checking work email — separate.", - "version": "8.0.0", + "version": "7.4.0", "author": "Andrea Marchesini, Luke Crouch and Jonathan Kingston", "bugs": { "url": "https://github.com/mozilla/multi-account-containers/issues" diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json new file mode 100644 index 0000000..80ea76e --- /dev/null +++ b/src/_locales/en/messages.json @@ -0,0 +1,270 @@ +{ + "extensionDescription": { + "message": "Multi-Account Containers helps you keep all the parts of your online life contained in different tabs. Custom labels and color-coded tabs help keep different activities — like online shopping, travel planning, or checking work email — separate.", + "description": "Description of the extension. DO NOT TRANSLATE \"Multi-Account Containers\"." + }, + "openInNewTabTitle": { + "message": "Open New Tab in…", + "description": "Menu title" + }, + "reopenThisSiteIn": { + "message": "Reopen This Site in…", + "description": "Menu title" + }, + "sortTabsByContainer": { + "message": "Sort Tabs by Container", + "description": "Menu title" + }, + "alwaysOpenThisSiteIn": { + "message": "Always Open This Site in…", + "description": "Menu title" + }, + "alwaysOpenIn": { + "message": "Always open in…", + "description": "Menu title" + }, + "alwaysOpenSiteInContainer": { + "message": "Always Open Site in Container" + }, + "openANewTabIn": { + "message" : "Open a New Tab in…" + }, + "openNewTabInThisContainer": { + "message" : "Open New Tab in this Container" + }, + "openTabs": { + "message": "Open Tabs" + }, + "moveTabsToANewWindow": { + "message": "Move Tabs to a New Window" + }, + "onboarding-1-header": { + "message": "A better way to manage all the things you do online" + }, + "onboarding-1-description": { + "message": "Use containers to organize tasks, manage accounts, and keep your focus where you want it." + }, + "onboarding-1-sec-header": { + "message": "A simple and secure way to manage your online life" + }, + "onboarding-1-sec-description": { + "message" : "Use containers to organize tasks, manage accounts, and store sensitive data." + }, + "onboarding-2-header": { + "message": "Put containers to work for you." + }, + "onboarding-2-description": { + "message": "Features like color-coding and separate container tabs help you find things easily, focus your attention, and minimize distractions." + }, + "onboarding-2-sec-description": { + "message": "Color-coding helps you categorize your online life, find things easily, and minimize distractions." + }, + "onboarding-3-header": { + "message": "A place for everything, and everything in its place." + }, + "onboarding-3-description": { + "message": "Start with the containers we've created, or create your own." + }, + "onboarding-3-sec-header": { + "message": "Set boundaries for your browsing." + }, + "onboarding-3-sec-description": { + "message": "Cookies are stored within a container, so you can segment sensitive data and browsing history to stay organized and to limit the impact of online trackers." + }, + "onboarding-4-header": { + "message": "Always open sites in the containers you want." + }, + "onboarding-4-description": { + "message": "Right-click inside a container tab to assign the site to always open in the container." + }, + "onboarding-5-header": { + "message": "Container tabs when you need them." + }, + "onboarding-5-description": { + "message": "Long-press the New Tab button to create a new container tab." + }, + "onboarding-6-header": { + "message": "Syncing Containers is now Available!" + }, + "onboarding-6-description": { + "message": "Turn on Sync to share container and site assignments with any computer connected to your Firefox Account." + }, + "onboarding-7-header": { + "message": "Firefox Account is required to sync." + }, + "onboarding-7-description": { + "message": "Click Sign In to confirm that your Firefox Account is active." + }, + "oneHundredTabsHeader": { + "message": "100 tabs!" + }, + "youHaveOpened": { + "message": "You've opened 100 Container tabs." + }, + "spreadTheWord": { + "message": "If you enjoy Containers, help us spread the word!" + }, + "rate": { + "message": "Rate" + }, + "share": { + "message": "Share" + }, + "tweet": { + "message": "Tweet" + }, + "default": { + "message": "Default" + }, + "manageContainers": { + "message": "Manage Containers" + }, + "newContainer": { + "message": "New Container" + }, + "hideThisContainer": { + "message": "Hide This Container" + }, + "removeThisContainer": { + "message": "Remove This Container" + }, + "removeThisContainerConfirmation": { + "message": "Are you sure you want to remove this Container?" + }, + "cancel": { + "message": "Cancel" + }, + "ok": { + "message": "OK" + }, + "sitesAssignedToThisContainer": { + "message": "Sites assigned to this container" + }, + "options": { + "message": "Options" + }, + "name": { + "message": "Name" + }, + "color": { + "message": "Color" + }, + "icon": { + "message": "Icon" + }, + "limitToDesignatedSites": { + "message": "Limit to Designated Sites" + }, + "deleteThisContainer": { + "message": "Delete This Container" + }, + "manageSiteList": { + "message": "Manage Site List…" + }, + "manageThisContainer": { + "message": "Manage This Container" + }, + "containers": { + "message": "Containers" + }, + "done": { + "message": "Done" + }, + "getStarted": { + "message": "Get Started" + }, + "signIn": { + "message": "Sign In" + }, + "notNow": { + "message": "Not Now" + }, + "startSyncing": { + "message": "Start Syncing" + }, + "info": { + "message": "info" + }, + "next": { + "message": "Next" + }, + "openThisSiteConfirmation": { + "message": "Open this site in your assigned container?" + }, + "wouldYouStillLikeToOpenConfirmation": { + "message": "Would you still like to open in this current container?" + }, + "rememberMyDecision": { + "message": "Remember my decision for this site" + }, + "optionalPermissions": { + "message": "Optional Permissions:" + }, + "keyboardShortCuts": { + "message": "Keyboard Shortcuts:" + }, + "onboarding": { + "message": "Onboarding" + }, + "resetOnboardingPanels": { + "message": "Reset Onboarding Panels" + }, + "onboardingToggle": { + "message": "Toggle this to see the onboarding panels again." + }, + "tabBehavior": { + "message": "Tab behavior:" + }, + "firefoxAccountsSync": { + "message": "Firefox Accounts Sync:" + }, + "enableBookMarkMenus": { + "message": "Enable Bookmark Menus" + }, + "enableSync": { + "message": "Enable Sync" + }, + "enableBookMarkMenusDescription": { + "message": "This setting allows you to open a bookmark or folder of bookmarks in a container." + }, + "enableSyncDescription": { + "message": "This setting allows you to sync your containers and site assignments across devices." + }, + "replaceTab": { + "message": "This setting allows you to sync your containers and site assignments across devices." + }, + "replaceTabDescription": { + "message": "Replace the current tab if a page which is assigned to another container is opened (instead of keeping the current tab open). Opening tabs with middle mouse button is not affected." + }, + "editWhichContainer": { + "message": "Edit which container is opened when using the numbered shortcuts." + }, + "keyboardShortCut": { + "message": "Container to open with Keyboard Shortcut $keyId$", + "placeholders": { + "keyId": { + "content": "$1" + } + } + }, + "confirmNavigationTitle": { + "message": "Multi-Account Containers Confirm Navigation", + "description": "This is the title of a confirmation page. Please do not translate \"Multi-Account Containers\"." + }, + "openInContainer": { + "message": "Open in $containerName$ Container", + "placeholders": { + "containerName": { + "content": "$1" + } + } + }, + "youAskedFirefox": { + "message": "You asked Firefox to always open $containerName$ for this site:", + "placeholders": { + "containerName": { + "content": "$1" + } + } + } +} diff --git a/src/confirm-page.html b/src/confirm-page.html index 9ebe6be..a3d7704 100644 --- a/src/confirm-page.html +++ b/src/confirm-page.html @@ -1,31 +1,30 @@ - Multi-Account Containers Confirm Navigation + +
-

Open this site in your assigned container?

+

-

- You asked Firefox to always open for this site:
-

+

-

Would you still like to open in this current container?

+




- - + +
diff --git a/src/css/confirm-page.css b/src/css/confirm-page.css index b954443..49990f3 100644 --- a/src/css/confirm-page.css +++ b/src/css/confirm-page.css @@ -27,7 +27,7 @@ button .container-name, /* for a mid sized window we have enough for this but not our image */ .title { - background-image: url("chrome://global/skin/icons/info.svg"); + background-image: url('chrome://global/skin/icons/info.svg'); } } @@ -76,6 +76,11 @@ dfn { font-style: normal; } +#deny, +#confirm { + flex-grow: 1; +} + .button-container > button { min-inline-size: 240px; } diff --git a/src/js/background/assignManager.js b/src/js/background/assignManager.js index 1ac5bc5..41a787c 100644 --- a/src/js/background/assignManager.js +++ b/src/js/background/assignManager.js @@ -712,7 +712,7 @@ window.assignManager = { reloadPageInContainer(url, currentUserContextId, userContextId, index, active, neverAsk = false, openerTabId = null) { const cookieStoreId = backgroundLogic.cookieStoreId(userContextId); - const loadPage = browser.extension.getURL("confirm-page.html"); + 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) { diff --git a/src/js/background/backgroundLogic.js b/src/js/background/backgroundLogic.js index f3ef957..e33ee6a 100644 --- a/src/js/background/backgroundLogic.js +++ b/src/js/background/backgroundLogic.js @@ -22,7 +22,7 @@ const backgroundLogic = { }, async getExtensionInfo() { - const manifestPath = browser.extension.getURL("manifest.json"); + const manifestPath = browser.runtime.getURL("manifest.json"); const response = await fetch(manifestPath); const extensionInfo = await response.json(); return extensionInfo; diff --git a/src/js/confirm-page.js b/src/js/confirm-page.js index 5ca5323..21a445c 100644 --- a/src/js/confirm-page.js +++ b/src/js/confirm-page.js @@ -7,21 +7,21 @@ async function load() { redirectUrlElement.textContent = redirectUrl; appendFavicon(redirectUrl, redirectUrlElement); - const container = await browser.contextualIdentities.get(cookieStoreId); - [...document.querySelectorAll(".container-name")].forEach((containerNameElement) => { - containerNameElement.textContent = container.name; - }); - - // If default container, button will default to normal HTML content - if (currentCookieStoreId) { - const currentContainer = await browser.contextualIdentities.get(currentCookieStoreId); - document.getElementById("current-container-name").textContent = currentContainer.name; - } 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); diff --git a/src/js/content-script.js b/src/js/content-script.js index 1bf6a6e..539e43a 100644 --- a/src/js/content-script.js +++ b/src/js/content-script.js @@ -24,7 +24,7 @@ async function addMessage(message) { divElement.innerText = message.text; const imageElement = document.createElement("img"); - const imagePath = browser.extension.getURL("/img/container-site-d-24.png"); + 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); diff --git a/src/js/i18n.js b/src/js/i18n.js new file mode 100644 index 0000000..3c07064 --- /dev/null +++ b/src/js/i18n.js @@ -0,0 +1,9 @@ +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/popup.js b/src/js/popup.js index 6c0d7ab..ce91c91 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -41,7 +41,7 @@ function addRemoveSiteIsolation() { } async function getExtensionInfo() { - const manifestPath = browser.extension.getURL("manifest.json"); + const manifestPath = browser.runtime.getURL("manifest.json"); const response = await fetch(manifestPath); const extensionInfo = await response.json(); return extensionInfo; @@ -924,7 +924,7 @@ Logic.registerPanel(OPEN_NEW_CONTAINER_PICKER, { // This method is called when the panel is shown. prepare() { Logic.listenToPickerBackButton(); - document.getElementById("picker-title").textContent = "Open a New Tab in"; + document.getElementById("picker-title").textContent = browser.i18n.getMessage("openANewTabIn"); const fragment = document.createDocumentFragment(); const pickedFunction = function (identity) { try { @@ -992,7 +992,7 @@ Logic.registerPanel(MANAGE_CONTAINERS_PICKER, { }); this._listenerSet = true; } - document.getElementById("picker-title").textContent = "Manage Containers"; + document.getElementById("picker-title").textContent = browser.i18n.getMessage("manageContainers"); const fragment = document.createDocumentFragment(); const pickedFunction = function (identity) { Logic.showPanel(P_CONTAINER_EDIT, identity); @@ -1004,7 +1004,7 @@ Logic.registerPanel(MANAGE_CONTAINERS_PICKER, { - New Container + ${ browser.i18n.getMessage("newContainer") } @@ -1108,7 +1108,7 @@ Logic.registerPanel(REOPEN_IN_CONTAINER_PICKER, { // This method is called when the panel is shown. async prepare() { Logic.listenToPickerBackButton(); - document.getElementById("picker-title").textContent = "Reopen This Site in"; + document.getElementById("picker-title").textContent = browser.i18n.getMessage("reopenThisSiteIn"); const fragment = document.createDocumentFragment(); const currentTab = await Utils.currentTab(); const pickedFunction = function (identity) { @@ -1201,7 +1201,7 @@ Logic.registerPanel(ALWAYS_OPEN_IN_PICKER, { // This method is called when the panel is shown. prepare() { Logic.listenToPickerBackButton(); - document.getElementById("picker-title").textContent = "Reopen This Site in"; + document.getElementById("picker-title").textContent = browser.i18n.getMessage("alwaysOpenIn"); const fragment = document.createDocumentFragment(); document.getElementById("new-container-div").innerHTML = ""; diff --git a/src/manifest.json b/src/manifest.json index e2bbc1e..08f43b8 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,9 +1,9 @@ { "manifest_version": 2, "name": "Firefox Multi-Account Containers", - "version": "8.0.0", + "version": "7.4.0", "incognito": "not_allowed", - "description": "Multi-Account Containers helps you keep all the parts of your online life contained in different tabs. Custom labels and color-coded tabs help keep different activities — like online shopping, travel planning, or checking work email — separate.", + "description": "__MSG_extensionDescription__", "icons": { "48": "img/container-site-d-48.png", "96": "img/container-site-d-96.png" @@ -25,6 +25,7 @@ "idle", "management", "storage", + "unlimitedStorage", "tabs", "webRequestBlocking", "webRequest", @@ -140,6 +141,7 @@ "run_at": "document_start" } ], + "default_locale": "en", "web_accessible_resources": [ "/img/container-site-d-24.png" ], diff --git a/src/options.html b/src/options.html index 7fe1b24..3f9b66c 100644 --- a/src/options.html +++ b/src/options.html @@ -3,85 +3,86 @@ +
-

Optional Permissions:

-