Compare commits
No commits in common. "main" and "flodolo-patch-1" have entirely different histories.
main
...
flodolo-pa
|
@ -1,6 +1,6 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 2021
|
"ecmaVersion": 2018
|
||||||
},
|
},
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
|
@ -19,7 +19,6 @@ module.exports = {
|
||||||
"OS": true,
|
"OS": true,
|
||||||
"ADDON_UNINSTALL": true,
|
"ADDON_UNINSTALL": true,
|
||||||
"ADDON_DISABLE": true,
|
"ADDON_DISABLE": true,
|
||||||
"CONTAINER_ORDER_STORAGE_KEY": true,
|
|
||||||
"proxifiedContainers": true,
|
"proxifiedContainers": true,
|
||||||
"MozillaVPN": true,
|
"MozillaVPN": true,
|
||||||
"MozillaVPN_Background": true
|
"MozillaVPN_Background": true
|
||||||
|
|
27
.github/ISSUE_TEMPLATE.md
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<!--
|
||||||
|
Feel free to ignore this Issue template if you just want to ask or suggest something. If you experience an Issue then please provide all asked information.
|
||||||
|
|
||||||
|
Also please make sure that:
|
||||||
|
- "Firefox will: Never remember history" in the Firefox Preferences/Options under "Privacy & Security > History" is NOT selected
|
||||||
|
- You are NOT using Firefox in a Private Window
|
||||||
|
- You can see a grayed out but ticked Checkbox with the description "Enable Container Tabs" in the Firefox Preferences/Options under "Tabs"
|
||||||
|
-->
|
||||||
|
- Multi-Account Containers Version:
|
||||||
|
- Operating System + Version:
|
||||||
|
- Firefox Version:
|
||||||
|
- Other installed Add-ons + Version + Enabled/Disabled-Status:
|
||||||
|
<!-- To be able to copy & paste the full list of your Add-ons navigate to "about:support" and scroll down to "Extensions" -->
|
||||||
|
|
||||||
|
|
||||||
|
### Actual behavior
|
||||||
|
|
||||||
|
|
||||||
|
### Expected behavior
|
||||||
|
|
||||||
|
|
||||||
|
### Steps to reproduce
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
### Notes
|
53
.github/ISSUE_TEMPLATE/bug.yml
vendored
|
@ -1,53 +0,0 @@
|
||||||
name: Bug Report
|
|
||||||
description: Report a problem in Multi-Account Containers
|
|
||||||
labels: [bug]
|
|
||||||
body:
|
|
||||||
- type: checkboxes
|
|
||||||
id: before-bug-report
|
|
||||||
attributes:
|
|
||||||
label: Before submitting a bug report
|
|
||||||
options:
|
|
||||||
- label: "I updated to the latest version of Multi-Account Container and tested if I can reproduce the issue"
|
|
||||||
required: true
|
|
||||||
- label: "I searched for existing reports to see if it hasn't already been reported"
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: step_to_reproduce
|
|
||||||
attributes:
|
|
||||||
label: "Step to reproduce"
|
|
||||||
description: "Provide a list of steps you did to trigger this bug"
|
|
||||||
placeholder: |
|
|
||||||
1. I opened ...
|
|
||||||
2. I clicked on ...
|
|
||||||
3. ...
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: actual_behavior
|
|
||||||
attributes:
|
|
||||||
label: "Actual behavior"
|
|
||||||
description: "Provide a description of what is currently happening"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: expected_behavior
|
|
||||||
attributes:
|
|
||||||
label: "Expected behavior"
|
|
||||||
description: "Provide a description of what should happen"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: additional_informations
|
|
||||||
attributes:
|
|
||||||
label: "Additional informations"
|
|
||||||
description: "Provide any other information revelant to this issue"
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: textarea
|
|
||||||
id: about_support
|
|
||||||
attributes:
|
|
||||||
label: "Provide a copy of Troubleshooting Information page (optional)"
|
|
||||||
description: "To get a copy of the Troubleshooting Information page, type *about:support* in the address bar and click on the *Copy text to clipboard* button."
|
|
||||||
render: "plain text"
|
|
||||||
validations:
|
|
||||||
required: false
|
|
17
.github/ISSUE_TEMPLATE/config.yml
vendored
|
@ -1,17 +0,0 @@
|
||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: "Explore our help articles"
|
|
||||||
url: "https://support.mozilla.org/kb/containers"
|
|
||||||
about: "Dig into the knowledge base, tips and tricks, troubleshooting, and so much more."
|
|
||||||
- name: "Ask a support question"
|
|
||||||
url: "https://support.mozilla.org/questions/new/desktop/form"
|
|
||||||
about: "Get support from our contributors or staff members."
|
|
||||||
- name: "Submit new ideas"
|
|
||||||
url: "https://connect.mozilla.org/t5/discussions/how-to-submit-a-great-idea-in-five-easy-steps/td-p/24"
|
|
||||||
about: "Have an idea for a new product feature? Share it with our community and staff members!"
|
|
||||||
- name: "Discussions"
|
|
||||||
url: "https://connect.mozilla.org/t5/discussions/bd-p/discussions"
|
|
||||||
about: "Give feedback and participate in meaningful conversations with the community and Mozilla employees"
|
|
||||||
- name: "Discover more awesome tools"
|
|
||||||
url: "https://www.mozilla.org/firefox/products/"
|
|
||||||
about: "Learn more about other products from Mozilla"
|
|
24
.github/pull_request_template.md
vendored
|
@ -1,24 +0,0 @@
|
||||||
**Before submitting your pull request**
|
|
||||||
|
|
||||||
- [ ] I agree to license my code under the [MPL 2.0 license](https://www.mozilla.org/en-US/MPL/2.0/).
|
|
||||||
- [ ] I rebased my work on top of the main branch.
|
|
||||||
- [ ] I ran `npm test` and all tests passed.
|
|
||||||
- [ ] I added test coverages if relevant.
|
|
||||||
|
|
||||||
# Description
|
|
||||||
|
|
||||||
*Please include a summary of the changes including relevant motivation and context.*
|
|
||||||
|
|
||||||
## Type of change
|
|
||||||
|
|
||||||
*Select all that apply.*
|
|
||||||
|
|
||||||
- [ ] Bug fix
|
|
||||||
- [ ] New feature
|
|
||||||
- [ ] Major change (fix or feature that would cause existing functionality to work differently than in the current version)
|
|
||||||
|
|
||||||
Tag issues related to this pull request:
|
|
||||||
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
8
.github/workflows/builds.yaml
vendored
|
@ -9,16 +9,16 @@ on:
|
||||||
- main
|
- main
|
||||||
- production
|
- production
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 2 * * *' # Daily at 2AM UTC
|
- cron: '0 2 * * *' # Daily at 2AM UTC
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
builds:
|
builds:
|
||||||
name: Builds
|
name: Builds
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repository
|
- name: Clone repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Create the package
|
- name: Create the package
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -26,7 +26,7 @@ jobs:
|
||||||
./bin/build-addon.sh nightly.xpi
|
./bin/build-addon.sh nightly.xpi
|
||||||
|
|
||||||
- name: Uploading
|
- name: Uploading
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: ${{matrix.config.name}} Build
|
name: ${{matrix.config.name}} Build
|
||||||
path: src/web-ext-artifacts
|
path: src/web-ext-artifacts
|
||||||
|
|
28
.github/workflows/test.yaml
vendored
|
@ -1,28 +0,0 @@
|
||||||
name: Test
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- production
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
name: Run tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Clone repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up node
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: lts/*
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm install --legacy-peer-deps
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: npm run test
|
|
2
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
package-lock.json
|
||||||
node_modules
|
node_modules
|
||||||
README.html
|
README.html
|
||||||
*.xpi
|
*.xpi
|
||||||
|
@ -8,7 +9,6 @@ README.html
|
||||||
addon.env
|
addon.env
|
||||||
|
|
||||||
src/web-ext-artifacts/*
|
src/web-ext-artifacts/*
|
||||||
web-ext-artifacts
|
|
||||||
|
|
||||||
# JetBrains IDE files
|
# JetBrains IDE files
|
||||||
.idea
|
.idea
|
||||||
|
|
7
.travis.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- "lts/*"
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
irc:
|
||||||
|
- "ircs://irc.mozilla.org:6697/#testpilot-containers-bots"
|
|
@ -1,54 +1,35 @@
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
## Requirements
|
Everyone is welcome to contribute to containers. Reach out to team members if you have questions:
|
||||||
|
|
||||||
* Firefox 91.1.0+
|
- Matrix chat: [#containers:mozilla.org](https://matrix.to/#/#containers:mozilla.org)
|
||||||
* Git 2.13+
|
- Email: containers@mozilla.com
|
||||||
* Node 7+
|
|
||||||
|
|
||||||
## Getting Started
|
## Filing bugs
|
||||||
|
|
||||||
1. Follow the instructions on [How to fork a repository][fork]
|
If you find a bug with containers, please file a issue.
|
||||||
2. Fetch the locales:
|
|
||||||
|
|
||||||
```
|
Check first if the bug might already exist: https://github.com/mozilla/multi-account-containers/issues
|
||||||
cd multi-account-containers
|
|
||||||
git submodule update --init
|
|
||||||
```
|
|
||||||
3. Install the project dependencies
|
|
||||||
```
|
|
||||||
npm install --legacy-peer-deps
|
|
||||||
```
|
|
||||||
4. Run `npm run dev`.
|
|
||||||
|
|
||||||
## Translations
|
[Open an issue](https://github.com/mozilla/multi-account-containers/issues/new)
|
||||||
|
|
||||||
The translations are located in `src/_locales`. This directory is a git
|
1. Visit about:support
|
||||||
repository like any other. Before editing files in this folder, you need to:
|
2. Click "Copy raw data to clipboard" and paste into the bug. Alternatively copy the following sections into the issue:
|
||||||
|
- Application Basics
|
||||||
|
- Nightly Features (if you are in nightly)
|
||||||
|
- Extensions
|
||||||
|
- Experimental Features
|
||||||
|
3. Include clear steps to reproduce the issue you have experienced.
|
||||||
|
4. Include screenshots if possible.
|
||||||
|
|
||||||
1. `cd src/_locales/`
|
## Sending Pull Requests
|
||||||
2. `git checkout -b message-updates-yyyymmdd`
|
|
||||||
3. `git push -u origin message-updates-yyyymmdd`
|
|
||||||
|
|
||||||
You can then [open a pull request][pr] on [the l10n repository][l10n].
|
Patches should be submitted as pull requests. When submitting patches as PRs:
|
||||||
|
|
||||||
## Tips for contributing
|
- You agree to license your code under the project's open source license (MPL 2.0).
|
||||||
|
- Base your branch off the current master (see below for an example workflow).
|
||||||
|
- Add both your code and new tests if relevant.
|
||||||
|
- Run npm test to make sure all tests still pass.
|
||||||
|
- Please do not include merge commits in pull requests; include only commits with the new relevant code.
|
||||||
|
|
||||||
1. Choose [an issue][issues] that you would like to work on.
|
See the main [README](./README.md) for information on prerequisites, installing, running and testing.
|
||||||
2. Fork the repository and follow the instructions for setting it up locally.
|
|
||||||
3. Run the add-on locally and try reproducing the issue.
|
|
||||||
4. Debug add-ons by clicking the “Settings” icon in about:addons, and then clicking “Debug Add-ons”
|
|
||||||
5. Click “Inspect” on the MAC add-on to open developer tools for the popup extension (see [this documentation][extension-doc] for more information)
|
|
||||||
6. Once you have a fix ready, commit your changes with the following commit message template: “Fix #<insert issue id #>: <short description>”
|
|
||||||
7. Push your changes and open a pull request for review.
|
|
||||||
|
|
||||||
If you run into an issue, you can always ask the other community members in the [discussions board][discussions].
|
|
||||||
|
|
||||||
<!-- Please keep the list in alphabetical order -->
|
|
||||||
[discussions]: https://github.com/mozilla/multi-account-containers/discussions
|
|
||||||
[extension-doc]: https://extensionworkshop.com/documentation/develop/debugging/
|
|
||||||
[fork]: https://docs.github.com/en/get-started/quickstart/fork-a-repo
|
|
||||||
[issues]: https://github.com/mozilla/multi-account-containers/issues
|
|
||||||
[l10n]: https://github.com/mozilla-l10n/multi-account-containers-l10n/
|
|
||||||
[pr]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests
|
|
||||||
[web-ext]: https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Getting_started_with_web-ext
|
|
||||||
|
|
120
README.md
|
@ -1,33 +1,109 @@
|
||||||
# Multi-Account Containers
|
# Multi-Account Containers
|
||||||
|
|
||||||
[](https://github.com/mozilla/multi-account-containers/actions/workflows/test.yaml)
|
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/)
|
||||||
|
|
||||||
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!
|
[Available on addons.mozilla.org](https://addons.mozilla.org/firefox/addon/multi-account-containers/)
|
||||||
|
|
||||||
Learn more about Multi-Account Containers in
|
For more info, see:
|
||||||
[our end-user documentation][enduser].
|
|
||||||
|
|
||||||
## Contributing
|
* [Test Pilot Product Hypothesis Document](https://docs.google.com/document/d/1WQdHTVXROk7dYkSFluc6_hS44tqZjIrG9I-uPyzevE8/edit#)
|
||||||
|
* [Shield Product Hypothesis Document](https://docs.google.com/document/d/1vMD-fH_5hGDDqNvpRZk12_RhCN2WAe4_yaBamaNdtik/edit#)
|
||||||
|
|
||||||
Everyone is welcome to contribute to Multi-Account Containers. To learn how
|
|
||||||
to contribute a patch to Multi-Account Container, please
|
|
||||||
[read our contributing guide][contributing].
|
|
||||||
|
|
||||||
You can also chat with us on [our Matrix room][matrix] or ask in [our discussions board][discussions].
|
## Requirements
|
||||||
|
|
||||||
This repository is governed by Mozilla's code of conduct and etiquette
|
* node 7+ (for jpm)
|
||||||
guidelines. For more details, [please read the Mozilla Community Participation Guidelines][cpg].
|
* Firefox 57+
|
||||||
|
|
||||||
### License
|
|
||||||
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public
|
## Development
|
||||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
<!-- Please keep the list in alphabetical order -->
|
### Running Locally
|
||||||
[contributing]: CONTRIBUTING.md
|
|
||||||
[cpg]: https://www.mozilla.org/about/governance/policies/participation/
|
#### Via WebExtensions API (web-ext)
|
||||||
[enduser]: https://support.mozilla.org/en-US/kb/containers
|
|
||||||
[forum]: https://discourse.mozilla.org/c/containers/223
|
1. Fetch the locales updating the git-submodules: `git submodule init && git submodule update --remote --depth 1 src/_locales`
|
||||||
[discussions]: https://github.com/mozilla/multi-account-containers/discussions
|
2. Install the [web-ext](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Getting_started_with_web-ext) tool.
|
||||||
[matrix]: https://matrix.to/#/#containers:mozilla.org
|
3. 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. Fetch the locales updating the git-submodules: `git submodule init && git submodule update --remote --depth 1 src/_locales`
|
||||||
|
2. Open the `about:debugging` page in Firefox.
|
||||||
|
3. Click on `This Firefox`.
|
||||||
|
4. Click on [Load Temporary Add-on](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Temporary_Installation_in_Firefox).
|
||||||
|
5. Select `src/manifest.json`.
|
||||||
|
|
||||||
|
Here is a [video](https://www.youtube.com/watch?v=cer9EUKegG4) that demonstrates how to do this.
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
* Install dependencies:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
#### Add/update messages for translation
|
||||||
|
|
||||||
|
The `src/_locales` directory is a git repository like any other, so to make changes to the messages:
|
||||||
|
|
||||||
|
1. Make whatever changes you need in `src/_locales/en` as you work.
|
||||||
|
|
||||||
|
2. `cd src/_locales/en`
|
||||||
|
|
||||||
|
3. `git branch message-updates-yyyymmdd`
|
||||||
|
|
||||||
|
4. `git push -u origin message-updates-yyyymmdd`
|
||||||
|
|
||||||
|
You can then open a pull request from the `message-updates-yyyymmdd` branch to
|
||||||
|
|
||||||
|
[the l10n repo](https://github.com/mozilla-l10n/multi-account-containers-l10n/) `main` branch.
|
||||||
|
|
||||||
|
### Distributing
|
||||||
|
#### Make the new version
|
||||||
|
|
||||||
|
1. Bump the version number in `package.json` and `manifest.json`
|
||||||
|
2. Commit the version number bump
|
||||||
|
3. Create a git tag for the version: `git tag <version>`
|
||||||
|
4. Push the tag up to GitHub: `git push --tags`
|
||||||
|
|
||||||
|
#### Publish to AMO
|
||||||
|
|
||||||
|
1. `./bin/build-addon.sh`
|
||||||
|
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/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"
|
||||||
|
* Release notes: copy the output of `git log --no-merges --pretty=format:"%h %s" <previous-version>..<new-version>`
|
||||||
|
* Attach binaries: select the signed `.xpi` file
|
||||||
|
|
||||||
|
### Links
|
||||||
|
|
||||||
|
Facebook & Twitter icons CC-Attrib https://fairheadcreative.com.
|
||||||
|
|
||||||
|
- [License](./LICENSE.txt)
|
||||||
|
- [Contributing](./CONTRIBUTING.md)
|
||||||
|
- [Code Of Conduct](./CODE_OF_CONDUCT.md)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/env bash
|
#!/bin/bash
|
||||||
|
|
||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
# 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
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
@ -32,4 +32,4 @@ rm -rf $TMPDIR/src/_locales/.github || die
|
||||||
print G "done."
|
print G "done."
|
||||||
|
|
||||||
print Y "Running the test..."
|
print Y "Running the test..."
|
||||||
npx addons-linter $TMPDIR/src || die
|
$(npm bin)/addons-linter $TMPDIR/src || die
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/env bash
|
#!/bin/bash
|
||||||
|
|
||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
# 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
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
@ -11,7 +11,7 @@ git submodule init || die
|
||||||
git submodule update --remote --depth 1 src/_locales || die
|
git submodule update --remote --depth 1 src/_locales || die
|
||||||
|
|
||||||
print Y "Installing dependencies..."
|
print Y "Installing dependencies..."
|
||||||
npm install --legacy-peer-deps || die
|
npm install || die
|
||||||
|
|
||||||
print Y "Running tests..."
|
print Y "Running tests..."
|
||||||
npm test
|
npm test
|
||||||
|
@ -23,4 +23,4 @@ if [[ $# -gt 0 ]]; then
|
||||||
EXTRA_PARAMS="--filename $1"
|
EXTRA_PARAMS="--filename $1"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
npx web-ext build --overwrite-dest $EXTRA_PARAMS || die
|
$(npm bin)/web-ext build --overwrite-dest $EXTRA_PARAMS || die
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/env bash
|
#!/bin/bash
|
||||||
|
|
||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
# 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
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
|
31
docs/acceptance.md
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# Acceptance Criteria
|
||||||
|
|
||||||
|
## User Experience
|
||||||
|
- [ ] It should place a containers button on the user’s browser toolbar
|
||||||
|
- [ ] It should include all containers management UI inside a door hanger from this toolbar button
|
||||||
|
- [ ] It should provide a set of default container choices for users to pick from (work, home, travel etc.)
|
||||||
|
- [ ] Each container type should have a color, icon and name
|
||||||
|
- [ ] It should allow users to create new containers
|
||||||
|
- [ ] It should allow users to delete containers
|
||||||
|
- [ ] It should allow users to open container tabs
|
||||||
|
- [ ] each container tab should be clearly demarcated by color/icon etc.
|
||||||
|
- [ ] It should provide a control to show/hide all open tabs of one container type
|
||||||
|
- [ ] It should provide a control to sort tabs by container type
|
||||||
|
- [ ] It should provide a control to allow users to open containers in new tab or new window by default
|
||||||
|
|
||||||
|
## User Experience Non-Requirements (out of scope)
|
||||||
|
- [ ] It should not show any UI in browser settings
|
||||||
|
- [ ] It should not effect the awesome bar
|
||||||
|
- [ ] It should not effect the Firefox Library
|
||||||
|
- [ ] It should not make any changes to sync functionality
|
||||||
|
|
||||||
|
## Measurements
|
||||||
|
- [ ] It should measure container tab creations per session
|
||||||
|
- [ ] It should measure the type of container tab created
|
||||||
|
- [ ] It should measure container tab creations
|
||||||
|
- [ ] It should measure new container creations
|
||||||
|
- [ ] It should measure container deletes
|
||||||
|
- [ ] It should measure container sorts
|
||||||
|
- [ ] It should measure container hides
|
||||||
|
- [ ] It should report URIs accessed across multiple containers
|
||||||
|
- [ ] It should measure the % of tab-based vs windows-based containers
|
BIN
docs/kpi-1.png
Normal file
After Width: | Height: | Size: 95 KiB |
|
@ -1,31 +0,0 @@
|
||||||
# Release a new version
|
|
||||||
|
|
||||||
## Make the new version
|
|
||||||
|
|
||||||
1. Bump the version number in `package.json` and `manifest.json`
|
|
||||||
2. Commit the version number bump
|
|
||||||
3. Create a git tag for the version: `git tag <version>`
|
|
||||||
4. Push the tag up to GitHub: `git push --tags`
|
|
||||||
|
|
||||||
## Publish to AMO
|
|
||||||
|
|
||||||
1. Run `./bin/build-addon.sh`
|
|
||||||
2. [Upload the zip file to AMO][amo-upload]
|
|
||||||
|
|
||||||
## Publish to GitHub
|
|
||||||
|
|
||||||
Finally, we also publish the release to GitHub.
|
|
||||||
|
|
||||||
1. Download the signed `.xpi` from [the addon versions page][addon-page]
|
|
||||||
2. [Create a new release on GitHub][gh-release]
|
|
||||||
* For *Tag version* and *Release title*, use the version number
|
|
||||||
* For *Release notes*, copy the output of:
|
|
||||||
```
|
|
||||||
git log --no-merges \
|
|
||||||
--pretty=format:"%h %s" <previous-version>..<new-version>
|
|
||||||
```
|
|
||||||
* For the *Attach binaries*, select the signed `.xpi` file
|
|
||||||
|
|
||||||
[addon-page]: https://addons.mozilla.org/developers/addon/multi-account-containers/versions
|
|
||||||
[amo-upload]: https://addons.mozilla.org/developers/addon/multi-account-containers/versions/submit/
|
|
||||||
[gh-release]: https://github.com/mozilla/multi-account-containers/releases/new
|
|
25098
package-lock.json
generated
21
package.json
|
@ -2,22 +2,22 @@
|
||||||
"name": "testpilot-containers",
|
"name": "testpilot-containers",
|
||||||
"title": "Multi-Account 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.",
|
"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.3.0",
|
"version": "7.4.0",
|
||||||
"author": "Andrea Marchesini, Luke Crouch, Lesley Norton, Kendall Werts, Maxx Crawford, Jonathan Kingston",
|
"author": "Andrea Marchesini, Luke Crouch and Jonathan Kingston",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/mozilla/multi-account-containers/issues"
|
"url": "https://github.com/mozilla/multi-account-containers/issues"
|
||||||
},
|
},
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"addons-linter": "^5.28.0",
|
"addons-linter": "^1.3.2",
|
||||||
"ajv": "^6.6.3",
|
"ajv": "^6.6.3",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^6.6.0",
|
||||||
"eslint-plugin-no-unsanitized": "^4.0.0",
|
"eslint-plugin-no-unsanitized": "^2.0.0",
|
||||||
"eslint-plugin-promise": "^5.2.0",
|
"eslint-plugin-promise": "^3.4.0",
|
||||||
"htmllint-cli": "0.0.7",
|
"htmllint-cli": "0.0.7",
|
||||||
"json": ">=10.0.0",
|
"json": ">=10.0.0",
|
||||||
"mocha": "^10.1.0",
|
"mocha": "^6.2.2",
|
||||||
"npm-run-all": "^4.0.0",
|
"npm-run-all": "^4.0.0",
|
||||||
"nyc": "^15.0.0",
|
"nyc": "^15.0.0",
|
||||||
"sinon": "^7.5.0",
|
"sinon": "^7.5.0",
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
"stylelint": "^13.5.0",
|
"stylelint": "^13.5.0",
|
||||||
"stylelint-config-standard": "^20.0.0",
|
"stylelint-config-standard": "^20.0.0",
|
||||||
"stylelint-order": "^4.0.0",
|
"stylelint-order": "^4.0.0",
|
||||||
"web-ext": "^7.5.0",
|
"web-ext": "^5.4.1",
|
||||||
"webextensions-jsdom": "^1.2.1"
|
"webextensions-jsdom": "^1.2.1"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/mozilla/multi-account-containers#readme",
|
"homepage": "https://github.com/mozilla/multi-account-containers#readme",
|
||||||
|
@ -36,16 +36,13 @@
|
||||||
"url": "git+https://github.com/mozilla/multi-account-containers.git"
|
"url": "git+https://github.com/mozilla/multi-account-containers.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "web-ext build -s src/",
|
"webext": "web-ext run -s src/",
|
||||||
"dev": "npm run remove-locales-github && web-ext run -s src/",
|
|
||||||
"lint": "npm-run-all lint:*",
|
"lint": "npm-run-all lint:*",
|
||||||
"lint:addon": "./bin/addons-linter.sh",
|
"lint:addon": "./bin/addons-linter.sh",
|
||||||
"lint:css": "stylelint src/css/*.css",
|
"lint:css": "stylelint src/css/*.css",
|
||||||
"lint:html": "htmllint *.html",
|
"lint:html": "htmllint *.html",
|
||||||
"lint:js": "eslint .",
|
"lint:js": "eslint .",
|
||||||
"package": "rm -rf src/web-ext-artifacts && npm run build && mv src/web-ext-artifacts/firefox_multi-account_containers-*.zip addon.xpi",
|
"package": "rm -rf src/web-ext-artifacts && npm run build && mv src/web-ext-artifacts/firefox_multi-account_containers-*.zip addon.xpi",
|
||||||
"restore-locales-github": "cd src/_locales && git restore .github/",
|
|
||||||
"remove-locales-github": "rm -rf src/_locales/.github",
|
|
||||||
"test": "npm run lint && npm run coverage",
|
"test": "npm run lint && npm run coverage",
|
||||||
"test:once": "mocha test/**/*.test.js",
|
"test:once": "mocha test/**/*.test.js",
|
||||||
"test:watch": "npm run test:once -- --watch",
|
"test:watch": "npm run test:once -- --watch",
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit bdaa01291b7367a5e815470fd263ea36c862fe32
|
Subproject commit f3da295d004b7d6314c5baa321d9a5418ec937d9
|
|
@ -3,7 +3,6 @@
|
||||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
<title data-i18n-message-id="confirmNavigationTitle"></title>
|
<title data-i18n-message-id="confirmNavigationTitle"></title>
|
||||||
<link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="chrome://browser/skin/aboutNetError.css" type="text/css" media="all" />
|
<link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="chrome://browser/skin/aboutNetError.css" type="text/css" media="all" />
|
||||||
<link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="chrome://global/skin/aboutNetError.css" type="text/css" media="all" />
|
|
||||||
<script type="text/javascript" src="./js/i18n.js"></script>
|
<script type="text/javascript" src="./js/i18n.js"></script>
|
||||||
<link rel="stylesheet" href="/css/confirm-page.css" />
|
<link rel="stylesheet" href="/css/confirm-page.css" />
|
||||||
</head>
|
</head>
|
||||||
|
@ -24,21 +23,8 @@
|
||||||
</label>
|
</label>
|
||||||
<br />
|
<br />
|
||||||
<div class="button-container">
|
<div class="button-container">
|
||||||
<button id="deny"
|
<button id="deny" class="button" data-message-id="openInContainer" data-message-arg="current-container-name"></button>
|
||||||
class="button"
|
<button id="confirm" class="button primary" data-message-id="openInContainer" data-message-arg="container-name"></button>
|
||||||
data-message-id="openInContainer"
|
|
||||||
data-message-arg="current-container-name">
|
|
||||||
</button>
|
|
||||||
<button id="deny-no-container"
|
|
||||||
class="button"
|
|
||||||
data-message-id="openInNoContainer">
|
|
||||||
</button>
|
|
||||||
<button id="confirm"
|
|
||||||
class="button primary"
|
|
||||||
autofocus
|
|
||||||
data-message-id="openInContainer"
|
|
||||||
data-message-arg="container-name">
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -1,122 +1,29 @@
|
||||||
body {
|
body {
|
||||||
--grey10: #e7e7e7;
|
|
||||||
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
||||||
background: #fff;
|
background: #fff;
|
||||||
color: rgb(74, 74, 79);
|
color: #202023;
|
||||||
font-size: 13px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h3:first-of-type {
|
h3 {
|
||||||
margin-block-start: 2.5rem;
|
margin-block-start: 2.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h3:first-of-type {
|
||||||
|
margin-block-start: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
label {
|
label {
|
||||||
display: flex;
|
color: rgb(74, 74, 79);
|
||||||
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) {
|
@media (prefers-color-scheme: dark) {
|
||||||
body {
|
body {
|
||||||
background: #23212a;
|
background: #202023;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p,
|
||||||
|
label {
|
||||||
color: rgb(177, 177, 179);
|
color: rgb(177, 177, 179);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1169
src/css/popup.css
BIN
src/img/container-site-d-24.png
Normal file
After Width: | Height: | Size: 342 B |
BIN
src/img/container-site-d-48.png
Normal file
After Width: | Height: | Size: 578 B |
BIN
src/img/container-site-d-96.png
Normal file
After Width: | Height: | Size: 1 KiB |
9
src/img/multiaccountcontainer-16-dark.svg
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<!-- 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/. --><svg data-name="Flat (For Export)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<style>rect,path {fill: rgba(249, 249, 250, 0.8);}</style>
|
||||||
|
<rect x="1" y="1" width="6" height="6" rx="1"/>
|
||||||
|
<path d="M14.75 3H13V1.25A0.25 0.25 0 0 0 12.75 1h-1.5A0.25 0.25 0 0 0 11 1.25V3H9.25A0.25 0.25 0 0 0 9 3.25v1.5A0.25 0.25 0 0 0 9.25 5H11v1.75A0.25 0.25 0 0 0 11.25 7h1.5A0.25 0.25 0 0 0 13 6.75V5h1.75A0.25 0.25 0 0 0 15 4.75v-1.5A0.25 0.25 0 0 0 14.75 3z" fill-rule="evenodd"/>
|
||||||
|
<rect x="1" y="9" width="6" height="6" rx="1"/>
|
||||||
|
<rect x="9" y="9" width="6" height="6" rx="1"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 801 B |
|
@ -1,13 +1,7 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
<svg data-name="Flat (For Export)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
<style>
|
<style>rect,path {fill: rgba(24, 25, 26, 01);}</style>
|
||||||
:root { color-scheme: light dark; }
|
|
||||||
rect, path { fill: rgb(24, 25, 26); }
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
rect, path { fill: rgba(249, 249, 250, 0.8); }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<rect x="1" y="1" width="6" height="6" rx="1"/>
|
<rect x="1" y="1" width="6" height="6" rx="1"/>
|
||||||
<path d="M14.75 3H13V1.25A0.25 0.25 0 0 0 12.75 1h-1.5A0.25 0.25 0 0 0 11 1.25V3H9.25A0.25 0.25 0 0 0 9 3.25v1.5A0.25 0.25 0 0 0 9.25 5H11v1.75A0.25 0.25 0 0 0 11.25 7h1.5A0.25 0.25 0 0 0 13 6.75V5h1.75A0.25 0.25 0 0 0 15 4.75v-1.5A0.25 0.25 0 0 0 14.75 3z" fill-rule="evenodd"/>
|
<path d="M14.75 3H13V1.25A0.25 0.25 0 0 0 12.75 1h-1.5A0.25 0.25 0 0 0 11 1.25V3H9.25A0.25 0.25 0 0 0 9 3.25v1.5A0.25 0.25 0 0 0 9.25 5H11v1.75A0.25 0.25 0 0 0 11.25 7h1.5A0.25 0.25 0 0 0 13 6.75V5h1.75A0.25 0.25 0 0 0 15 4.75v-1.5A0.25 0.25 0 0 0 14.75 3z" fill-rule="evenodd"/>
|
||||||
<rect x="1" y="9" width="6" height="6" rx="1"/>
|
<rect x="1" y="9" width="6" height="6" rx="1"/>
|
||||||
<rect x="9" y="9" width="6" height="6" rx="1"/>
|
<rect x="9" y="9" width="6" height="6" rx="1"/>
|
||||||
</svg>
|
</svg>
|
Before Width: | Height: | Size: 727 B After Width: | Height: | Size: 586 B |
|
@ -4,7 +4,6 @@
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;">
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;">
|
||||||
<style>
|
<style>
|
||||||
:root { color-scheme: light dark; }
|
|
||||||
path, circle, g {
|
path, circle, g {
|
||||||
fill: menutext;
|
fill: menutext;
|
||||||
}
|
}
|
||||||
|
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
@ -1,3 +0,0 @@
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M18.6929 16.0119L13.7399 6.10586C13.5739 5.77358 13.3185 5.49411 13.0025 5.29878C12.6866 5.10346 12.3224 5 11.9509 5C11.5795 5 11.2153 5.10346 10.8993 5.29878C10.5834 5.49411 10.328 5.77358 10.1619 6.10586L5.20894 16.0159C5.0575 16.3206 4.98633 16.659 5.00217 16.999C5.01801 17.3389 5.12033 17.6692 5.29946 17.9586C5.47858 18.2479 5.72858 18.4868 6.0258 18.6526C6.32302 18.8183 6.65762 18.9055 6.99794 18.9059H16.9029C17.2439 18.906 17.5792 18.819 17.8771 18.6531C18.1749 18.4872 18.4255 18.2479 18.6048 17.9579C18.7842 17.668 18.8864 17.337 18.9018 16.9964C18.9172 16.6557 18.8453 16.3169 18.6929 16.0119V16.0119ZM10.9509 8.90586C10.9509 8.64065 11.0563 8.38629 11.2438 8.19876C11.4314 8.01122 11.6857 7.90586 11.9509 7.90586C12.2162 7.90586 12.4705 8.01122 12.658 8.19876C12.8456 8.38629 12.9509 8.64065 12.9509 8.90586V12.9059C12.9509 13.1711 12.8456 13.4254 12.658 13.613C12.4705 13.8005 12.2162 13.9059 11.9509 13.9059C11.6857 13.9059 11.4314 13.8005 11.2438 13.613C11.0563 13.4254 10.9509 13.1711 10.9509 12.9059V8.90586ZM11.9509 17.1559C11.7037 17.1559 11.462 17.0826 11.2565 16.9452C11.0509 16.8078 10.8907 16.6126 10.7961 16.3842C10.7015 16.1558 10.6767 15.9045 10.725 15.662C10.7732 15.4195 10.8922 15.1968 11.0671 15.022C11.2419 14.8472 11.4646 14.7281 11.7071 14.6799C11.9496 14.6317 12.2009 14.6564 12.4293 14.751C12.6577 14.8456 12.8529 15.0058 12.9903 15.2114C13.1276 15.417 13.2009 15.6586 13.2009 15.9059C13.2009 16.2374 13.0692 16.5553 12.8348 16.7897C12.6004 17.0242 12.2825 17.1559 11.9509 17.1559V17.1559Z" fill="#FF4F5E" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.6 KiB |
|
@ -61,9 +61,8 @@ window.assignManager = {
|
||||||
this.area.get([siteStoreKey]).then((storageResponse) => {
|
this.area.get([siteStoreKey]).then((storageResponse) => {
|
||||||
if (storageResponse && siteStoreKey in storageResponse) {
|
if (storageResponse && siteStoreKey in storageResponse) {
|
||||||
resolve(storageResponse[siteStoreKey]);
|
resolve(storageResponse[siteStoreKey]);
|
||||||
} else {
|
|
||||||
resolve(null);
|
|
||||||
}
|
}
|
||||||
|
resolve(null);
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
reject(e);
|
reject(e);
|
||||||
});
|
});
|
||||||
|
@ -103,7 +102,6 @@ window.assignManager = {
|
||||||
async deleteContainer(userContextId) {
|
async deleteContainer(userContextId) {
|
||||||
const sitesByContainer = await this.getAssignedSites(userContextId);
|
const sitesByContainer = await this.getAssignedSites(userContextId);
|
||||||
this.area.remove(Object.keys(sitesByContainer));
|
this.area.remove(Object.keys(sitesByContainer));
|
||||||
identityState.storageArea.remove(backgroundLogic.cookieStoreId(userContextId));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async getAssignedSites(userContextId = null) {
|
async getAssignedSites(userContextId = null) {
|
||||||
|
@ -166,17 +164,11 @@ window.assignManager = {
|
||||||
_neverAsk(m) {
|
_neverAsk(m) {
|
||||||
const pageUrl = m.pageUrl;
|
const pageUrl = m.pageUrl;
|
||||||
if (m.neverAsk === true) {
|
if (m.neverAsk === true) {
|
||||||
if (m.defaultContainer === true) {
|
|
||||||
this.storageArea.remove(pageUrl);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have existing data and for some reason it hasn't been
|
// If we have existing data and for some reason it hasn't been
|
||||||
// deleted etc lets update it
|
// deleted etc lets update it
|
||||||
this.storageArea.get(pageUrl).then((siteSettings) => {
|
this.storageArea.get(pageUrl).then((siteSettings) => {
|
||||||
if (siteSettings) {
|
if (siteSettings) {
|
||||||
siteSettings.neverAsk = true;
|
siteSettings.neverAsk = true;
|
||||||
siteSettings.userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(m.cookieStoreId);
|
|
||||||
this.storageArea.set(pageUrl, siteSettings);
|
this.storageArea.set(pageUrl, siteSettings);
|
||||||
}
|
}
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
|
@ -196,18 +188,13 @@ window.assignManager = {
|
||||||
// The following blocks potentially dangerous requests for privacy that come without a tabId
|
// The following blocks potentially dangerous requests for privacy that come without a tabId
|
||||||
|
|
||||||
if(requestInfo.tabId === -1) {
|
if(requestInfo.tabId === -1) {
|
||||||
return {};
|
return {type: "direct"};
|
||||||
}
|
}
|
||||||
|
|
||||||
const tab = await browser.tabs.get(requestInfo.tabId);
|
const tab = await browser.tabs.get(requestInfo.tabId);
|
||||||
const result = await proxifiedContainers.retrieve(tab.cookieStoreId);
|
const result = await proxifiedContainers.retrieve(tab.cookieStoreId);
|
||||||
if (!result || !result.proxy) {
|
if (!result || !result.proxy) {
|
||||||
return {};
|
return {type: "direct"};
|
||||||
}
|
|
||||||
|
|
||||||
// proxyDNS only works for SOCKS proxies
|
|
||||||
if (["socks", "socks4"].includes(result.proxy.type)) {
|
|
||||||
result.proxy.proxyDNS = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.proxy.mozProxyEnabled) {
|
if (!result.proxy.mozProxyEnabled) {
|
||||||
|
@ -326,8 +313,7 @@ window.assignManager = {
|
||||||
options.url,
|
options.url,
|
||||||
tab.index + 1,
|
tab.index + 1,
|
||||||
tab.active,
|
tab.active,
|
||||||
openTabId,
|
openTabId
|
||||||
tab.groupId
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.reloadPageInContainer(
|
this.reloadPageInContainer(
|
||||||
|
@ -337,8 +323,7 @@ window.assignManager = {
|
||||||
tab.index + 1,
|
tab.index + 1,
|
||||||
tab.active,
|
tab.active,
|
||||||
siteSettings.neverAsk,
|
siteSettings.neverAsk,
|
||||||
openTabId,
|
openTabId
|
||||||
tab.groupId
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.calculateContextMenu(tab);
|
this.calculateContextMenu(tab);
|
||||||
|
@ -397,12 +382,6 @@ window.assignManager = {
|
||||||
return currentContainerState && currentContainerState.isIsolated;
|
return currentContainerState && currentContainerState.isIsolated;
|
||||||
},
|
},
|
||||||
|
|
||||||
maybeAddProxyListeners() {
|
|
||||||
if (browser.proxy) {
|
|
||||||
browser.proxy.onRequest.addListener(this.handleProxifiedRequest, {urls: ["<all_urls>"]});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
browser.contextMenus.onClicked.addListener((info, tab) => {
|
browser.contextMenus.onClicked.addListener((info, tab) => {
|
||||||
info.bookmarkId ?
|
info.bookmarkId ?
|
||||||
|
@ -411,7 +390,7 @@ window.assignManager = {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Before anything happens we decide if the request should be proxified
|
// Before anything happens we decide if the request should be proxified
|
||||||
this.maybeAddProxyListeners();
|
browser.proxy.onRequest.addListener(this.handleProxifiedRequest, {urls: ["<all_urls>"]});
|
||||||
|
|
||||||
// Before a request is handled by the browser we decide if we should
|
// Before a request is handled by the browser we decide if we should
|
||||||
// route through a different container
|
// route through a different container
|
||||||
|
@ -483,7 +462,9 @@ window.assignManager = {
|
||||||
},
|
},
|
||||||
|
|
||||||
contextualIdentityRemoved(changeInfo) {
|
contextualIdentityRemoved(changeInfo) {
|
||||||
this.removeMenuItem(changeInfo.contextualIdentity.cookieStoreId);
|
browser.contextMenus.remove(
|
||||||
|
changeInfo.contextualIdentity.cookieStoreId
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
async _onClickedHandler(info, tab) {
|
async _onClickedHandler(info, tab) {
|
||||||
|
@ -578,16 +559,6 @@ window.assignManager = {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
async _resetCookiesForSite(hostname, cookieStoreId) {
|
|
||||||
const hostNameTruncated = hostname.replace(/^www\./, ""); // Remove "www." from the hostname
|
|
||||||
await browser.browsingData.removeCookies({
|
|
||||||
cookieStoreId: cookieStoreId,
|
|
||||||
hostnames: [hostNameTruncated] // This does not remove cookies from associated domains. To remove all cookies, we have a container storage removal option.
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
async _setOrRemoveAssignment(tabId, pageUrl, userContextId, remove) {
|
async _setOrRemoveAssignment(tabId, pageUrl, userContextId, remove) {
|
||||||
let actionName;
|
let actionName;
|
||||||
// https://github.com/mozilla/testpilot-containers/issues/626
|
// https://github.com/mozilla/testpilot-containers/issues/626
|
||||||
|
@ -639,7 +610,7 @@ window.assignManager = {
|
||||||
},
|
},
|
||||||
|
|
||||||
async _maybeRemoveSiteIsolation(userContextId) {
|
async _maybeRemoveSiteIsolation(userContextId) {
|
||||||
const assignments = await this.storageArea.getAssignedSites(userContextId);
|
const assignments = await this.storageArea.getByContainer(userContextId);
|
||||||
const hasAssignments = assignments && Object.keys(assignments).length > 0;
|
const hasAssignments = assignments && Object.keys(assignments).length > 0;
|
||||||
if (hasAssignments) {
|
if (hasAssignments) {
|
||||||
return;
|
return;
|
||||||
|
@ -670,11 +641,11 @@ window.assignManager = {
|
||||||
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1215376#c16
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1215376#c16
|
||||||
// We also can't change for always private mode
|
// We also can't change for always private mode
|
||||||
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1352102
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1352102
|
||||||
this.removeMenuItem(this.MENU_ASSIGN_ID);
|
browser.contextMenus.remove(this.MENU_ASSIGN_ID);
|
||||||
this.removeMenuItem(this.MENU_REMOVE_ID);
|
browser.contextMenus.remove(this.MENU_REMOVE_ID);
|
||||||
this.removeMenuItem(this.MENU_SEPARATOR_ID);
|
browser.contextMenus.remove(this.MENU_SEPARATOR_ID);
|
||||||
this.removeMenuItem(this.MENU_HIDE_ID);
|
browser.contextMenus.remove(this.MENU_HIDE_ID);
|
||||||
this.removeMenuItem(this.MENU_MOVE_ID);
|
browser.contextMenus.remove(this.MENU_MOVE_ID);
|
||||||
},
|
},
|
||||||
|
|
||||||
async calculateContextMenu(tab) {
|
async calculateContextMenu(tab) {
|
||||||
|
@ -695,7 +666,7 @@ window.assignManager = {
|
||||||
}
|
}
|
||||||
browser.contextMenus.create({
|
browser.contextMenus.create({
|
||||||
id: menuId,
|
id: menuId,
|
||||||
title: browser.i18n.getMessage("alwaysOpenSiteInContainer"),
|
title: "Always Open in This Container",
|
||||||
checked,
|
checked,
|
||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
contexts: ["all"],
|
contexts: ["all"],
|
||||||
|
@ -709,13 +680,13 @@ window.assignManager = {
|
||||||
|
|
||||||
browser.contextMenus.create({
|
browser.contextMenus.create({
|
||||||
id: this.MENU_HIDE_ID,
|
id: this.MENU_HIDE_ID,
|
||||||
title: browser.i18n.getMessage("hideThisContainer"),
|
title: "Hide This Container",
|
||||||
contexts: ["all"],
|
contexts: ["all"],
|
||||||
});
|
});
|
||||||
|
|
||||||
browser.contextMenus.create({
|
browser.contextMenus.create({
|
||||||
id: this.MENU_MOVE_ID,
|
id: this.MENU_MOVE_ID,
|
||||||
title: browser.i18n.getMessage("moveTabsToANewWindow"),
|
title: "Move Tabs to a New Window",
|
||||||
contexts: ["all"],
|
contexts: ["all"],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -727,15 +698,7 @@ window.assignManager = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
reloadPageInDefaultContainer(url, index, active, openerTabId) {
|
||||||
* @param {string} url
|
|
||||||
* @param {number} index
|
|
||||||
* @param {boolean} active
|
|
||||||
* @param {number} [openerTabId]
|
|
||||||
* @param {number} [groupId]
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
reloadPageInDefaultContainer(url, index, active, openerTabId, groupId) {
|
|
||||||
// To create a new tab in the default container, it is easiest just to omit the
|
// To create a new tab in the default container, it is easiest just to omit the
|
||||||
// cookieStoreId entirely.
|
// cookieStoreId entirely.
|
||||||
//
|
//
|
||||||
|
@ -754,58 +717,16 @@ window.assignManager = {
|
||||||
// does not automatically return to the original opener tab. To get this desired behaviour,
|
// does not automatically return to the original opener tab. To get this desired behaviour,
|
||||||
// we MUST specify the openerTabId when creating the new tab.
|
// we MUST specify the openerTabId when creating the new tab.
|
||||||
const cookieStoreId = "firefox-default";
|
const cookieStoreId = "firefox-default";
|
||||||
this.createTabWrapper(url, cookieStoreId, index, active, openerTabId, groupId);
|
browser.tabs.create({url, cookieStoreId, index, active, openerTabId});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
reloadPageInContainer(url, currentUserContextId, userContextId, index, active, neverAsk = false, openerTabId = null) {
|
||||||
/**
|
|
||||||
* Wraps around `browser.tabs.create` and `browser.tabs.group` to create a
|
|
||||||
* tab and ensure that it ends up in the requested tab group, if applicable.
|
|
||||||
*
|
|
||||||
* @param {string} url
|
|
||||||
* @param {string} cookieStoreId
|
|
||||||
* @param {number} index
|
|
||||||
* @param {boolean} active
|
|
||||||
* @param {number} openerTabId
|
|
||||||
* @param {number} [groupId] Tab group ID
|
|
||||||
* @returns {Promise<Tab>}
|
|
||||||
*/
|
|
||||||
async createTabWrapper(url, cookieStoreId, index, active, openerTabId, groupId) {
|
|
||||||
const newTab = await browser.tabs.create({
|
|
||||||
url,
|
|
||||||
cookieStoreId,
|
|
||||||
index,
|
|
||||||
active,
|
|
||||||
openerTabId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (groupId >= 0) {
|
|
||||||
// If the original tab was in a tab group, make sure that the reopened tab
|
|
||||||
// stays in the same tab group.
|
|
||||||
await browser.tabs.group({ groupId, tabIds: newTab.id });
|
|
||||||
}
|
|
||||||
|
|
||||||
return newTab;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} url
|
|
||||||
* @param {string} currentUserContextId
|
|
||||||
* @param {string} userContextId
|
|
||||||
* @param {number} index
|
|
||||||
* @param {boolean} active
|
|
||||||
* @param {boolean} [neverAsk=false]
|
|
||||||
* @param {number} [openerTabId=null]
|
|
||||||
* @param {number} [groupId]
|
|
||||||
* @returns {Promise<Tab>}
|
|
||||||
*/
|
|
||||||
reloadPageInContainer(url, currentUserContextId, userContextId, index, active, neverAsk = false, openerTabId = null, groupId = undefined) {
|
|
||||||
const cookieStoreId = backgroundLogic.cookieStoreId(userContextId);
|
const cookieStoreId = backgroundLogic.cookieStoreId(userContextId);
|
||||||
const loadPage = browser.runtime.getURL("confirm-page.html");
|
const loadPage = browser.runtime.getURL("confirm-page.html");
|
||||||
// False represents assignment is not permitted
|
// 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 the user has explicitly checked "Never Ask Again" on the warning page we will send them straight there
|
||||||
if (neverAsk) {
|
if (neverAsk) {
|
||||||
return this.createTabWrapper(url, cookieStoreId, index, active, openerTabId, groupId);
|
return browser.tabs.create({url, cookieStoreId, index, active, openerTabId});
|
||||||
} else {
|
} else {
|
||||||
let confirmUrl = `${loadPage}?url=${this.encodeURLProperty(url)}&cookieStoreId=${cookieStoreId}`;
|
let confirmUrl = `${loadPage}?url=${this.encodeURLProperty(url)}&cookieStoreId=${cookieStoreId}`;
|
||||||
let currentCookieStoreId;
|
let currentCookieStoreId;
|
||||||
|
@ -813,14 +734,13 @@ window.assignManager = {
|
||||||
currentCookieStoreId = backgroundLogic.cookieStoreId(currentUserContextId);
|
currentCookieStoreId = backgroundLogic.cookieStoreId(currentUserContextId);
|
||||||
confirmUrl += `¤tCookieStoreId=${currentCookieStoreId}`;
|
confirmUrl += `¤tCookieStoreId=${currentCookieStoreId}`;
|
||||||
}
|
}
|
||||||
return this.createTabWrapper(
|
return browser.tabs.create({
|
||||||
confirmUrl,
|
url: confirmUrl,
|
||||||
currentCookieStoreId,
|
cookieStoreId: currentCookieStoreId,
|
||||||
index,
|
|
||||||
active,
|
|
||||||
openerTabId,
|
openerTabId,
|
||||||
groupId
|
index,
|
||||||
).then(() => {
|
active
|
||||||
|
}).then(() => {
|
||||||
// We don't want to sync this URL ever nor clutter the users history
|
// We don't want to sync this URL ever nor clutter the users history
|
||||||
browser.history.deleteUrl({url: confirmUrl});
|
browser.history.deleteUrl({url: confirmUrl});
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
|
@ -832,7 +752,7 @@ window.assignManager = {
|
||||||
async initBookmarksMenu() {
|
async initBookmarksMenu() {
|
||||||
browser.contextMenus.create({
|
browser.contextMenus.create({
|
||||||
id: this.OPEN_IN_CONTAINER,
|
id: this.OPEN_IN_CONTAINER,
|
||||||
title: browser.i18n.getMessage("openBookmarkInContainerTab"),
|
title: "Open Bookmark in Container Tab",
|
||||||
contexts: ["bookmark"],
|
contexts: ["bookmark"],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -848,19 +768,12 @@ window.assignManager = {
|
||||||
},
|
},
|
||||||
|
|
||||||
async removeBookmarksMenu() {
|
async removeBookmarksMenu() {
|
||||||
this.removeMenuItem(this.OPEN_IN_CONTAINER);
|
browser.contextMenus.remove(this.OPEN_IN_CONTAINER);
|
||||||
const identities = await browser.contextualIdentities.query({});
|
const identities = await browser.contextualIdentities.query({});
|
||||||
for (const identity of identities) {
|
for (const identity of identities) {
|
||||||
this.removeMenuItem(identity.cookieStoreId);
|
browser.contextMenus.remove(identity.cookieStoreId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
removeMenuItem(menuItemId) {
|
|
||||||
// Callers do not check whether the menu exists before attempting to remove
|
|
||||||
// it. contextMenus.remove rejects when the menu does not exist, so we need
|
|
||||||
// to catch and swallow the error to avoid logspam.
|
|
||||||
browser.contextMenus.remove(menuItemId).catch(() => {});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assignManager.init();
|
assignManager.init();
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
/* 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 DEFAULT_TAB = "about:newtab";
|
||||||
|
|
||||||
const backgroundLogic = {
|
const backgroundLogic = {
|
||||||
NEW_TAB_PAGES: new Set([
|
NEW_TAB_PAGES: new Set([
|
||||||
"about:startpage",
|
"about:startpage",
|
||||||
|
@ -14,13 +9,7 @@ const backgroundLogic = {
|
||||||
NUMBER_OF_KEYBOARD_SHORTCUTS: 10,
|
NUMBER_OF_KEYBOARD_SHORTCUTS: 10,
|
||||||
unhideQueue: [],
|
unhideQueue: [],
|
||||||
init() {
|
init() {
|
||||||
|
|
||||||
browser.commands.onCommand.addListener(function (command) {
|
browser.commands.onCommand.addListener(function (command) {
|
||||||
if (command === "sort_tabs") {
|
|
||||||
backgroundLogic.sortTabs();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i=0; i < backgroundLogic.NUMBER_OF_KEYBOARD_SHORTCUTS; i++) {
|
for (let i=0; i < backgroundLogic.NUMBER_OF_KEYBOARD_SHORTCUTS; i++) {
|
||||||
const key = "open_container_" + i;
|
const key = "open_container_" + i;
|
||||||
const cookieStoreId = identityState.keyboardShortcut[key];
|
const cookieStoreId = identityState.keyboardShortcut[key];
|
||||||
|
@ -30,71 +19,6 @@ const backgroundLogic = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
browser.permissions.onAdded.addListener(permissions => this.resetPermissions(permissions));
|
|
||||||
browser.permissions.onRemoved.addListener(permissions => this.resetPermissions(permissions));
|
|
||||||
|
|
||||||
// Update Translation in Manifest
|
|
||||||
browser.runtime.onInstalled.addListener((details) => {
|
|
||||||
this.updateTranslationInManifest();
|
|
||||||
this._undoDefault820SortTabsKeyboardShortcut(details);
|
|
||||||
});
|
|
||||||
browser.runtime.onStartup.addListener(this.updateTranslationInManifest);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* One-time migration after updating from v8.2.0:
|
|
||||||
* Unset the default keyboard shortcut (Ctrl+Comma) for the `sort_tabs`
|
|
||||||
* command if it was set in v8.2.0 of this addon. If the user remapped
|
|
||||||
* a different shortcut manually, retain their shortcut. Users who used
|
|
||||||
* the default keyboard shortcut will need to manually set a shortcut.
|
|
||||||
* See https://support.mozilla.org/en-US/kb/manage-extension-shortcuts-firefox
|
|
||||||
*
|
|
||||||
* @param {{reason: runtime.OnInstalledReason, previousVersion?: string}} details
|
|
||||||
*/
|
|
||||||
async _undoDefault820SortTabsKeyboardShortcut(details) {
|
|
||||||
if (details.reason === "update" && details.previousVersion === "8.2.0") {
|
|
||||||
const commands = await browser.commands.getAll();
|
|
||||||
const sortTabsCommand = commands.find(command => command.name === "sort_tabs");
|
|
||||||
if (sortTabsCommand) {
|
|
||||||
const previouslySuggestedKeys = [
|
|
||||||
"Ctrl+Comma", // "default"
|
|
||||||
"MacCtrl+Comma", // "mac"
|
|
||||||
];
|
|
||||||
if (previouslySuggestedKeys.includes(sortTabsCommand.shortcut)) {
|
|
||||||
browser.commands.reset("sort_tabs");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateTranslationInManifest() {
|
|
||||||
for (let index = 0; index < 10; index++) {
|
|
||||||
const ajustedIndex = index + 1; // We want to start from 1 instead of 0 in the UI.
|
|
||||||
browser.commands.update({
|
|
||||||
name: `open_container_${index}`,
|
|
||||||
description: browser.i18n.getMessage("containerShortcut", `${ajustedIndex}`)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
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() {
|
async getExtensionInfo() {
|
||||||
|
@ -104,19 +28,6 @@ const backgroundLogic = {
|
||||||
return extensionInfo;
|
return extensionInfo;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Remove container data (cookies, localStorage and cache)
|
|
||||||
async deleteContainerDataOnly(userContextId) {
|
|
||||||
await browser.browsingData.removeCookies({
|
|
||||||
cookieStoreId: this.cookieStoreId(userContextId)
|
|
||||||
});
|
|
||||||
|
|
||||||
await browser.browsingData.removeLocalStorage({
|
|
||||||
cookieStoreId: this.cookieStoreId(userContextId)
|
|
||||||
});
|
|
||||||
|
|
||||||
return {done: true, userContextId};
|
|
||||||
},
|
|
||||||
|
|
||||||
getUserContextIdFromCookieStoreId(cookieStoreId) {
|
getUserContextIdFromCookieStoreId(cookieStoreId) {
|
||||||
if (!cookieStoreId) {
|
if (!cookieStoreId) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -185,8 +96,7 @@ const backgroundLogic = {
|
||||||
// We can't open these we just have to throw them away
|
// We can't open these we just have to throw them away
|
||||||
if (protocol === "about:"
|
if (protocol === "about:"
|
||||||
|| protocol === "chrome:"
|
|| protocol === "chrome:"
|
||||||
|| protocol === "moz-extension:"
|
|| protocol === "moz-extension:") {
|
||||||
|| protocol === "file:") {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -236,7 +146,7 @@ const backgroundLogic = {
|
||||||
if ("isIsolated" in containerState || remove) {
|
if ("isIsolated" in containerState || remove) {
|
||||||
delete containerState.isIsolated;
|
delete containerState.isIsolated;
|
||||||
} else {
|
} else {
|
||||||
containerState.isIsolated = "locked";
|
containerState.isIsolated = "locked";
|
||||||
}
|
}
|
||||||
return await identityState.storageArea.set(cookieStoreId, containerState);
|
return await identityState.storageArea.set(cookieStoreId, containerState);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -372,13 +282,7 @@ const backgroundLogic = {
|
||||||
let pos = 0;
|
let pos = 0;
|
||||||
|
|
||||||
// Let's collect UCIs/tabs for this window.
|
// Let's collect UCIs/tabs for this window.
|
||||||
/** @type {Map<string, {order: string, tabs: Tab[]}>} */
|
|
||||||
const map = new Map;
|
const map = new Map;
|
||||||
|
|
||||||
const lastTab = tabs.at(-1);
|
|
||||||
/** @type {boolean} */
|
|
||||||
let lastTabIsInTabGroup = !!lastTab && lastTab.groupId >= 0;
|
|
||||||
|
|
||||||
for (const tab of tabs) {
|
for (const tab of tabs) {
|
||||||
if (pinnedTabs && !tab.pinned) {
|
if (pinnedTabs && !tab.pinned) {
|
||||||
// We don't have, or we already handled all the pinned tabs.
|
// We don't have, or we already handled all the pinned tabs.
|
||||||
|
@ -391,51 +295,26 @@ const backgroundLogic = {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tab.groupId >= 0) {
|
const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(tab.cookieStoreId);
|
||||||
// Skip over tabs in tab groups until it's possible to handle them better.
|
if (!map.has(userContextId)) {
|
||||||
continue;
|
map.set(userContextId, []);
|
||||||
}
|
}
|
||||||
|
map.get(userContextId).push(tab);
|
||||||
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.
|
// Let's sort the map.
|
||||||
const sortMap = new Map([...map.entries()].sort((a, b) => a[1].order > b[1].order));
|
const sortMap = new Map([...map.entries()].sort((a, b) => a[0] > b[0]));
|
||||||
|
|
||||||
// Let's move tabs.
|
// Let's move tabs.
|
||||||
for (const { tabs } of sortMap.values()) {
|
sortMap.forEach(tabs => {
|
||||||
for (const tab of tabs) {
|
for (const tab of tabs) {
|
||||||
++pos;
|
++pos;
|
||||||
browser.tabs.move(tab.id, {
|
browser.tabs.move(tab.id, {
|
||||||
windowId: windowObj.id,
|
windowId: windowObj.id,
|
||||||
index: pinnedTabs ? pos : -1
|
index: pos
|
||||||
});
|
});
|
||||||
// Pinned tabs are never grouped and always inserted in the front.
|
|
||||||
if (!pinnedTabs && lastTabIsInTabGroup && browser.tabs.ungroup) {
|
|
||||||
// If the last item in the tab strip is a grouped tab, moving a tab
|
|
||||||
// to its position will also add it to the tab group. Since this code
|
|
||||||
// is only sorting ungrouped tabs, this forcibly ungroups the first
|
|
||||||
// tab to be moved. All subsequent iterations will only be moving
|
|
||||||
// ungrouped tabs to the position of other ungrouped tabs.
|
|
||||||
lastTabIsInTabGroup = false;
|
|
||||||
browser.tabs.ungroup(tab.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async hideTabs(options) {
|
async hideTabs(options) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const MAJOR_VERSIONS = ["2.3.0", "2.4.0", "6.2.0", "8.0.2"];
|
const MAJOR_VERSIONS = ["2.3.0", "2.4.0", "6.2.0", "8.0.0"];
|
||||||
const badge = {
|
const badge = {
|
||||||
async init() {
|
async init() {
|
||||||
const currentWindow = await browser.windows.getCurrent();
|
const currentWindow = await browser.windows.getCurrent();
|
||||||
|
|
|
@ -20,12 +20,12 @@ const messageHandler = {
|
||||||
case "resetSync":
|
case "resetSync":
|
||||||
response = sync.resetSync();
|
response = sync.resetSync();
|
||||||
break;
|
break;
|
||||||
|
case "resetBookmarksContext":
|
||||||
|
response = assignManager.resetBookmarksMenuItem();
|
||||||
|
break;
|
||||||
case "deleteContainer":
|
case "deleteContainer":
|
||||||
response = backgroundLogic.deleteContainer(m.message.userContextId);
|
response = backgroundLogic.deleteContainer(m.message.userContextId);
|
||||||
break;
|
break;
|
||||||
case "deleteContainerDataOnly":
|
|
||||||
response = backgroundLogic.deleteContainerDataOnly(m.message.userContextId);
|
|
||||||
break;
|
|
||||||
case "createOrUpdateContainer":
|
case "createOrUpdateContainer":
|
||||||
response = backgroundLogic.createOrUpdateContainer(m.message);
|
response = backgroundLogic.createOrUpdateContainer(m.message);
|
||||||
break;
|
break;
|
||||||
|
@ -48,9 +48,6 @@ const messageHandler = {
|
||||||
// m.url is the assignment to be removed/added
|
// m.url is the assignment to be removed/added
|
||||||
response = assignManager._setOrRemoveAssignment(m.tabId, m.url, m.userContextId, m.value);
|
response = assignManager._setOrRemoveAssignment(m.tabId, m.url, m.userContextId, m.value);
|
||||||
break;
|
break;
|
||||||
case "resetCookiesForSite":
|
|
||||||
response = assignManager._resetCookiesForSite(m.pageUrl, m.cookieStoreId);
|
|
||||||
break;
|
|
||||||
case "sortTabs":
|
case "sortTabs":
|
||||||
backgroundLogic.sortTabs();
|
backgroundLogic.sortTabs();
|
||||||
break;
|
break;
|
||||||
|
@ -91,21 +88,17 @@ const messageHandler = {
|
||||||
m.newUserContextId,
|
m.newUserContextId,
|
||||||
m.tabIndex,
|
m.tabIndex,
|
||||||
m.active,
|
m.active,
|
||||||
true,
|
true
|
||||||
null,
|
|
||||||
m.groupId
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "assignAndReloadInContainer":
|
case "assignAndReloadInContainer":
|
||||||
tab = await assignManager.reloadPageInContainer(
|
tab = await assignManager.reloadPageInContainer(
|
||||||
m.url,
|
m.url,
|
||||||
m.currentUserContextId,
|
m.currentUserContextId,
|
||||||
m.newUserContextId,
|
m.newUserContextId,
|
||||||
m.tabIndex,
|
m.tabIndex,
|
||||||
m.active,
|
m.active,
|
||||||
true,
|
true
|
||||||
null,
|
|
||||||
m.groupId
|
|
||||||
);
|
);
|
||||||
// m.tabId is used for where to place the in content message
|
// m.tabId is used for where to place the in content message
|
||||||
// m.url is the assignment to be removed/added
|
// m.url is the assignment to be removed/added
|
||||||
|
@ -230,9 +223,7 @@ const messageHandler = {
|
||||||
// if it's a container tab wait for it to complete and
|
// if it's a container tab wait for it to complete and
|
||||||
// unhide other tabs from this container
|
// unhide other tabs from this container
|
||||||
if (tab.cookieStoreId.startsWith("firefox-container")) {
|
if (tab.cookieStoreId.startsWith("firefox-container")) {
|
||||||
browser.tabs.onUpdated.addListener(this.tabUpdateHandler, {
|
browser.tabs.onUpdated.addListener(this.tabUpdateHandler);
|
||||||
properties: ["status"]
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,11 +66,11 @@ const MozillaVPN_Background = {
|
||||||
|
|
||||||
// Handle responses from MozillaVPN client
|
// Handle responses from MozillaVPN client
|
||||||
async handleResponse(response) {
|
async handleResponse(response) {
|
||||||
MozillaVPN_Background._installed = true;
|
|
||||||
if (response.error && response.error === "vpn-client-down") {
|
if (response.error && response.error === "vpn-client-down") {
|
||||||
MozillaVPN_Background._connected = false;
|
MozillaVPN_Background._connected = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
MozillaVPN_Background._installed = true;
|
||||||
if (response.servers) {
|
if (response.servers) {
|
||||||
const servers = response.servers.countries;
|
const servers = response.servers.countries;
|
||||||
browser.storage.local.set({ [MozillaVPN_Background.MOZILLA_VPN_SERVERS_KEY]: servers});
|
browser.storage.local.set({ [MozillaVPN_Background.MOZILLA_VPN_SERVERS_KEY]: servers});
|
||||||
|
@ -100,19 +100,6 @@ const MozillaVPN_Background = {
|
||||||
get isolationKey() {
|
get isolationKey() {
|
||||||
return this._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();
|
MozillaVPN_Background.init();
|
||||||
|
|
|
@ -7,47 +7,27 @@ async function load() {
|
||||||
redirectUrlElement.textContent = redirectUrl;
|
redirectUrlElement.textContent = redirectUrl;
|
||||||
appendFavicon(redirectUrl, redirectUrlElement);
|
appendFavicon(redirectUrl, redirectUrlElement);
|
||||||
|
|
||||||
// Option for staying on the previous container
|
|
||||||
document.getElementById("deny").addEventListener("click", (e) => {
|
document.getElementById("deny").addEventListener("click", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
denySubmit(redirectUrl, currentCookieStoreId);
|
denySubmit(redirectUrl);
|
||||||
});
|
|
||||||
|
|
||||||
// Option for going to the default container (no container)
|
|
||||||
document.getElementById("deny-no-container").addEventListener("click", (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
denySubmit(redirectUrl, currentCookieStoreId);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const container = await browser.contextualIdentities.get(cookieStoreId);
|
const container = await browser.contextualIdentities.get(cookieStoreId);
|
||||||
const currentContainer = currentCookieStoreId ? await browser.contextualIdentities.get(currentCookieStoreId) : null;
|
const currentContainer = currentCookieStoreId ? await browser.contextualIdentities.get(currentCookieStoreId) : null;
|
||||||
const currentContainerName = currentContainer ? setDenyButton(currentContainer.name) : setDenyButton("");
|
const currentContainerName = currentContainer ? currentContainer.name : "";
|
||||||
|
|
||||||
document.querySelectorAll("[data-message-id]").forEach(el => {
|
document.querySelectorAll("[data-message-id]").forEach(el => {
|
||||||
const elementData = el.dataset;
|
const elementData = el.dataset;
|
||||||
const containerName = elementData.messageArg === "container-name" ? container.name : currentContainerName;
|
const containerName = elementData.messageArg === "container-name" ? container.name : currentContainerName;
|
||||||
el.textContent = browser.i18n.getMessage(elementData.messageId, containerName);
|
el.textContent = browser.i18n.getMessage(elementData.messageId, containerName);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Option for going to newly selected container
|
|
||||||
document.getElementById("confirm").addEventListener("click", (e) => {
|
document.getElementById("confirm").addEventListener("click", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
confirmSubmit(redirectUrl, cookieStoreId);
|
confirmSubmit(redirectUrl, cookieStoreId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDenyButton(currentContainerName) {
|
|
||||||
const buttonDeny = document.getElementById("deny");
|
|
||||||
const buttonDenyNoContainer = document.getElementById("deny-no-container");
|
|
||||||
|
|
||||||
if (currentContainerName) {
|
|
||||||
buttonDenyNoContainer.style.display = "none";
|
|
||||||
return currentContainerName;
|
|
||||||
}
|
|
||||||
buttonDeny.style.display = "none";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function appendFavicon(pageUrl, redirectUrlElement) {
|
function appendFavicon(pageUrl, redirectUrlElement) {
|
||||||
const origin = new URL(pageUrl).origin;
|
const origin = new URL(pageUrl).origin;
|
||||||
const favIconElement = Utils.createFavIconElement(`${origin}/favicon.ico`);
|
const favIconElement = Utils.createFavIconElement(`${origin}/favicon.ico`);
|
||||||
|
@ -62,42 +42,24 @@ function confirmSubmit(redirectUrl, cookieStoreId) {
|
||||||
browser.runtime.sendMessage({
|
browser.runtime.sendMessage({
|
||||||
method: "neverAsk",
|
method: "neverAsk",
|
||||||
neverAsk: true,
|
neverAsk: true,
|
||||||
cookieStoreId: cookieStoreId,
|
|
||||||
pageUrl: redirectUrl
|
pageUrl: redirectUrl
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
openInContainer(redirectUrl, cookieStoreId);
|
openInContainer(redirectUrl, cookieStoreId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function getCurrentTab() {
|
||||||
* @returns {Promise<Tab>}
|
return browser.tabs.query({
|
||||||
*/
|
|
||||||
async function getCurrentTab() {
|
|
||||||
const tabs = await browser.tabs.query({
|
|
||||||
active: true,
|
active: true,
|
||||||
windowId: browser.windows.WINDOW_ID_CURRENT
|
windowId: browser.windows.WINDOW_ID_CURRENT
|
||||||
});
|
});
|
||||||
return tabs[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function denySubmit(redirectUrl, currentCookieStoreId) {
|
async function denySubmit(redirectUrl) {
|
||||||
const tab = await getCurrentTab();
|
const tab = await getCurrentTab();
|
||||||
const currentContainer = currentCookieStoreId ? await browser.contextualIdentities.get(currentCookieStoreId) : null;
|
|
||||||
const neverAsk = document.getElementById("never-ask").checked;
|
|
||||||
|
|
||||||
if (neverAsk) {
|
|
||||||
await browser.runtime.sendMessage({
|
|
||||||
method: "neverAsk",
|
|
||||||
neverAsk: true,
|
|
||||||
cookieStoreId: currentCookieStoreId,
|
|
||||||
pageUrl: redirectUrl,
|
|
||||||
defaultContainer: !currentContainer
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await browser.runtime.sendMessage({
|
await browser.runtime.sendMessage({
|
||||||
method: "exemptContainerAssignment",
|
method: "exemptContainerAssignment",
|
||||||
tabId: tab.id,
|
tabId: tab[0].id,
|
||||||
pageUrl: redirectUrl
|
pageUrl: redirectUrl
|
||||||
});
|
});
|
||||||
document.location.replace(redirectUrl);
|
document.location.replace(redirectUrl);
|
||||||
|
@ -107,15 +69,12 @@ load();
|
||||||
|
|
||||||
async function openInContainer(redirectUrl, cookieStoreId) {
|
async function openInContainer(redirectUrl, cookieStoreId) {
|
||||||
const tab = await getCurrentTab();
|
const tab = await getCurrentTab();
|
||||||
const reopenedTab = await browser.tabs.create({
|
await browser.tabs.create({
|
||||||
index: tab.index + 1,
|
index: tab[0].index + 1,
|
||||||
cookieStoreId,
|
cookieStoreId,
|
||||||
url: redirectUrl
|
url: redirectUrl
|
||||||
});
|
});
|
||||||
if (tab.groupId >= 0) {
|
if (tab.length > 0) {
|
||||||
// If the original tab was in a tab group, make sure that the reopened tab
|
browser.tabs.remove(tab[0].id);
|
||||||
// stays in the same tab group.
|
|
||||||
await browser.tabs.group({ groupId: tab.groupId, tabIds: reopenedTab.id });
|
|
||||||
}
|
}
|
||||||
await browser.tabs.remove(tab.id);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,12 +24,11 @@ async function addMessage(message) {
|
||||||
divElement.innerText = message.text;
|
divElement.innerText = message.text;
|
||||||
|
|
||||||
const imageElement = document.createElement("img");
|
const imageElement = document.createElement("img");
|
||||||
const imagePath = browser.runtime.getURL("/img/multiaccountcontainer-16.svg");
|
const imagePath = browser.runtime.getURL("/img/container-site-d-24.png");
|
||||||
const response = await fetch(imagePath);
|
const response = await fetch(imagePath);
|
||||||
const blob = await response.blob();
|
const blob = await response.blob();
|
||||||
const objectUrl = URL.createObjectURL(blob);
|
const objectUrl = URL.createObjectURL(blob);
|
||||||
imageElement.src = objectUrl;
|
imageElement.src = objectUrl;
|
||||||
imageElement.width = imageElement.height = 24;
|
|
||||||
divElement.prepend(imageElement);
|
divElement.prepend(imageElement);
|
||||||
|
|
||||||
document.body.appendChild(divElement);
|
document.body.appendChild(divElement);
|
||||||
|
|
|
@ -5,17 +5,11 @@ const MozillaVPN = {
|
||||||
const mozillaVpnInstalled = await browser.runtime.sendMessage({ method: "MozillaVPN_getInstallationStatus" });
|
const mozillaVpnInstalled = await browser.runtime.sendMessage({ method: "MozillaVPN_getInstallationStatus" });
|
||||||
this.handleStatusIndicatorsInContainerLists(mozillaVpnInstalled);
|
this.handleStatusIndicatorsInContainerLists(mozillaVpnInstalled);
|
||||||
|
|
||||||
const permissionsEnabled = await this.bothPermissionsEnabled();
|
|
||||||
if (!permissionsEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const proxies = await this.getProxies(identities);
|
const proxies = await this.getProxies(identities);
|
||||||
if (Object.keys(proxies).length === 0) {
|
if (Object.keys(proxies).length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tooltipProxyWarning = browser.i18n.getMessage("tooltipWarning");
|
|
||||||
for (const el of document.querySelectorAll("[data-cookie-store-id]")) {
|
for (const el of document.querySelectorAll("[data-cookie-store-id]")) {
|
||||||
const cookieStoreId = el.dataset.cookieStoreId;
|
const cookieStoreId = el.dataset.cookieStoreId;
|
||||||
|
|
||||||
|
@ -35,7 +29,6 @@ const MozillaVPN = {
|
||||||
if (!mozillaVpnConnected && proxy.mozProxyEnabled) {
|
if (!mozillaVpnConnected && proxy.mozProxyEnabled) {
|
||||||
flag.classList.add("proxy-unavailable");
|
flag.classList.add("proxy-unavailable");
|
||||||
const menuItemName = el.querySelector(".menu-item-name");
|
const menuItemName = el.querySelector(".menu-item-name");
|
||||||
menuItemName.setAttribute("title", tooltipProxyWarning);
|
|
||||||
if (menuItemName) {
|
if (menuItemName) {
|
||||||
el.querySelector(".menu-item-name").dataset.mozProxyWarning = "proxy-unavailable";
|
el.querySelector(".menu-item-name").dataset.mozProxyWarning = "proxy-unavailable";
|
||||||
}
|
}
|
||||||
|
@ -65,11 +58,14 @@ const MozillaVPN = {
|
||||||
const mozillaVpnConnected = await browser.runtime.sendMessage({ method: "MozillaVPN_getConnectionStatus" });
|
const mozillaVpnConnected = await browser.runtime.sendMessage({ method: "MozillaVPN_getConnectionStatus" });
|
||||||
const connectionStatusStringId = mozillaVpnConnected ? "moz-vpn-connected" : "moz-vpn-disconnected";
|
const connectionStatusStringId = mozillaVpnConnected ? "moz-vpn-connected" : "moz-vpn-disconnected";
|
||||||
const connectionStatusLocalizedString = browser.i18n.getMessage(connectionStatusStringId);
|
const connectionStatusLocalizedString = browser.i18n.getMessage(connectionStatusStringId);
|
||||||
const connectionStatusTooltip = document.querySelector(".vpn-status-container-list");
|
|
||||||
connectionStatusTooltip.setAttribute("title", connectionStatusLocalizedString);
|
|
||||||
|
|
||||||
statusIconEls.forEach(el => {
|
statusIconEls.forEach(el => {
|
||||||
el.style.backgroundImage = mozillaVpnConnected ? connectedIndicatorSrc : disconnectedIndicatorSrc;
|
el.style.backgroundImage = mozillaVpnConnected ? connectedIndicatorSrc : disconnectedIndicatorSrc;
|
||||||
|
if (el.querySelector(".tooltip")) {
|
||||||
|
el.querySelector(".tooltip").textContent = connectionStatusLocalizedString;
|
||||||
|
} else {
|
||||||
|
el.textContent = connectionStatusLocalizedString;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -155,10 +151,6 @@ const MozillaVPN = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
async bothPermissionsEnabled() {
|
|
||||||
return await browser.permissions.contains({ permissions: ["proxy", "nativeMessaging"] });
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
async getProxyWarnings(proxyObj) {
|
async getProxyWarnings(proxyObj) {
|
||||||
if (!proxyObj) {
|
if (!proxyObj) {
|
||||||
|
@ -248,7 +240,7 @@ const MozillaVPN = {
|
||||||
randomInteger = (randomInteger - server.weight);
|
randomInteger = (randomInteger - server.weight);
|
||||||
}
|
}
|
||||||
return nextServer;
|
return nextServer;
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.MozillaVPN = MozillaVPN;
|
window.MozillaVPN = MozillaVPN;
|
||||||
|
|
|
@ -1,45 +1,17 @@
|
||||||
const NUMBER_OF_KEYBOARD_SHORTCUTS = 10;
|
const NUMBER_OF_KEYBOARD_SHORTCUTS = 10;
|
||||||
|
|
||||||
async function setUpCheckBoxes() {
|
async function requestPermissions() {
|
||||||
document.querySelectorAll("[data-permission-id]").forEach(async(el) => {
|
const checkbox = document.querySelector("#bookmarksPermissions");
|
||||||
const permissionId = el.dataset.permissionId;
|
if (checkbox.checked) {
|
||||||
const permissionEnabled = await browser.permissions.contains({ permissions: [permissionId] });
|
const granted = await browser.permissions.request({permissions: ["bookmarks"]});
|
||||||
el.checked = !!permissionEnabled;
|
if (!granted) {
|
||||||
});
|
checkbox.checked = false;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
await browser.permissions.remove({ permissions: [permissionId] });
|
} else {
|
||||||
});
|
await browser.permissions.remove({permissions: ["bookmarks"]});
|
||||||
});
|
}
|
||||||
|
browser.runtime.sendMessage({ method: "resetBookmarksContext" });
|
||||||
async function maybeShowPermissionsWarningIcon() {
|
|
||||||
const bothMozillaVpnPermissionsEnabled = await MozillaVPN.bothPermissionsEnabled();
|
|
||||||
const permissionsWarningEl = document.querySelector(".warning-icon");
|
|
||||||
permissionsWarningEl.classList.toggle("show-warning", !bothMozillaVpnPermissionsEnabled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function enableDisableSync() {
|
async function enableDisableSync() {
|
||||||
|
@ -53,20 +25,15 @@ async function enableDisableReplaceTab() {
|
||||||
await browser.storage.local.set({replaceTabEnabled: !!checkbox.checked});
|
await browser.storage.local.set({replaceTabEnabled: !!checkbox.checked});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function changeTheme(event) {
|
|
||||||
const theme = event.currentTarget;
|
|
||||||
await browser.storage.local.set({currentTheme: theme.value});
|
|
||||||
await browser.storage.local.set({currentThemeId: theme.selectedIndex});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setupOptions() {
|
async function setupOptions() {
|
||||||
|
const hasPermission = await browser.permissions.contains({permissions: ["bookmarks"]});
|
||||||
const { syncEnabled } = await browser.storage.local.get("syncEnabled");
|
const { syncEnabled } = await browser.storage.local.get("syncEnabled");
|
||||||
const { replaceTabEnabled } = await browser.storage.local.get("replaceTabEnabled");
|
const { replaceTabEnabled } = await browser.storage.local.get("replaceTabEnabled");
|
||||||
const { currentThemeId } = await browser.storage.local.get("currentThemeId");
|
if (hasPermission) {
|
||||||
|
document.querySelector("#bookmarksPermissions").checked = true;
|
||||||
|
}
|
||||||
document.querySelector("#syncCheck").checked = !!syncEnabled;
|
document.querySelector("#syncCheck").checked = !!syncEnabled;
|
||||||
document.querySelector("#replaceTabCheck").checked = !!replaceTabEnabled;
|
document.querySelector("#replaceTabCheck").checked = !!replaceTabEnabled;
|
||||||
document.querySelector("#changeTheme").selectedIndex = currentThemeId;
|
|
||||||
setupContainerShortcutSelects();
|
setupContainerShortcutSelects();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,38 +78,13 @@ function resetOnboarding() {
|
||||||
browser.storage.local.set({"onboarding-stage": 0});
|
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.addEventListener("DOMContentLoaded", setupOptions);
|
||||||
|
document.querySelector("#bookmarksPermissions").addEventListener( "change", requestPermissions);
|
||||||
document.querySelector("#syncCheck").addEventListener( "change", enableDisableSync);
|
document.querySelector("#syncCheck").addEventListener( "change", enableDisableSync);
|
||||||
document.querySelector("#replaceTabCheck").addEventListener( "change", enableDisableReplaceTab);
|
document.querySelector("#replaceTabCheck").addEventListener( "change", enableDisableReplaceTab);
|
||||||
document.querySelector("#changeTheme").addEventListener( "change", changeTheme);
|
document.querySelector("button").addEventListener("click", resetOnboarding);
|
||||||
|
|
||||||
maybeShowPermissionsWarningIcon();
|
|
||||||
for (let i=0; i < NUMBER_OF_KEYBOARD_SHORTCUTS; i++) {
|
for (let i=0; i < NUMBER_OF_KEYBOARD_SHORTCUTS; i++) {
|
||||||
document.querySelector("#open_container_"+i)
|
document.querySelector("#open_container_"+i)
|
||||||
.addEventListener("change", storeShortcutChoice);
|
.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();
|
|
|
@ -32,9 +32,6 @@ async function init() {
|
||||||
list.appendChild(fragment);
|
list.appendChild(fragment);
|
||||||
|
|
||||||
MozillaVPN.handleContainerList(identities);
|
MozillaVPN.handleContainerList(identities);
|
||||||
|
|
||||||
// Set the theme
|
|
||||||
Utils.applyTheme();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
|
481
src/js/popup.js
|
@ -10,6 +10,7 @@ const DEFAULT_ICON = "circle";
|
||||||
const NEW_CONTAINER_ID = "new";
|
const NEW_CONTAINER_ID = "new";
|
||||||
|
|
||||||
const ONBOARDING_STORAGE_KEY = "onboarding-stage";
|
const ONBOARDING_STORAGE_KEY = "onboarding-stage";
|
||||||
|
const CONTAINER_ORDER_STORAGE_KEY = "container-order";
|
||||||
const CONTAINER_DRAG_DATA_TYPE = "firefox-container";
|
const CONTAINER_DRAG_DATA_TYPE = "firefox-container";
|
||||||
|
|
||||||
// List of panels
|
// List of panels
|
||||||
|
@ -32,7 +33,6 @@ const P_CONTAINER_EDIT = "containerEdit";
|
||||||
const P_CONTAINER_DELETE = "containerDelete";
|
const P_CONTAINER_DELETE = "containerDelete";
|
||||||
const P_CONTAINERS_ACHIEVEMENT = "containersAchievement";
|
const P_CONTAINERS_ACHIEVEMENT = "containersAchievement";
|
||||||
const P_CONTAINER_ASSIGNMENTS = "containerAssignments";
|
const P_CONTAINER_ASSIGNMENTS = "containerAssignments";
|
||||||
const P_CLEAR_CONTAINER_STORAGE = "clearContainerStorage";
|
|
||||||
|
|
||||||
const P_MOZILLA_VPN_SERVER_LIST = "moz-vpn-server-list";
|
const P_MOZILLA_VPN_SERVER_LIST = "moz-vpn-server-list";
|
||||||
const P_ADVANCED_PROXY_SETTINGS = "advanced-proxy-settings-panel";
|
const P_ADVANCED_PROXY_SETTINGS = "advanced-proxy-settings-panel";
|
||||||
|
@ -66,9 +66,6 @@ const Logic = {
|
||||||
method: "MozillaVPN_attemptPort"
|
method: "MozillaVPN_attemptPort"
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// Set the theme
|
|
||||||
Utils.applyTheme();
|
|
||||||
|
|
||||||
// Remove browserAction "upgraded" badge when opening panel
|
// Remove browserAction "upgraded" badge when opening panel
|
||||||
this.clearBrowserActionBadge();
|
this.clearBrowserActionBadge();
|
||||||
|
|
||||||
|
@ -123,19 +120,6 @@ const Logic = {
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
notify(i18nOpts) {
|
|
||||||
const notificationCards = document.querySelectorAll(".popup-notification-card");
|
|
||||||
const text = browser.i18n.getMessage(i18nOpts.messageId, i18nOpts.placeholders);
|
|
||||||
notificationCards.forEach(notificationCard => {
|
|
||||||
notificationCard.textContent = text;
|
|
||||||
notificationCard.classList.add("is-shown");
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
notificationCard.classList.remove("is-shown");
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
async showAchievementOrContainersListPanel() {
|
async showAchievementOrContainersListPanel() {
|
||||||
// Do we need to show an achievement panel?
|
// Do we need to show an achievement panel?
|
||||||
let showAchievements = false;
|
let showAchievements = false;
|
||||||
|
@ -225,11 +209,6 @@ const Logic = {
|
||||||
async saveContainerOrder(rows) {
|
async saveContainerOrder(rows) {
|
||||||
const containerOrder = {};
|
const containerOrder = {};
|
||||||
rows.forEach((node, index) => {
|
rows.forEach((node, index) => {
|
||||||
if (typeof browser.contextualIdentities.move === "function") {
|
|
||||||
browser.contextualIdentities.move(
|
|
||||||
node.dataset.containerId, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
return containerOrder[node.dataset.containerId] = index;
|
return containerOrder[node.dataset.containerId] = index;
|
||||||
});
|
});
|
||||||
await browser.storage.local.set({
|
await browser.storage.local.set({
|
||||||
|
@ -415,11 +394,7 @@ const Logic = {
|
||||||
},
|
},
|
||||||
|
|
||||||
shortcutListener(e){
|
shortcutListener(e){
|
||||||
function openTopContainers() {
|
function openNewContainerTab(identity) {
|
||||||
const identities = Logic.identities();
|
|
||||||
const key = e.code.substring(5);
|
|
||||||
const identity = e.code === "Digit0" ? identities[9] : identities[key - 1];
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
browser.tabs.create({
|
browser.tabs.create({
|
||||||
cookieStoreId: identity.cookieStoreId
|
cookieStoreId: identity.cookieStoreId
|
||||||
|
@ -429,34 +404,12 @@ const Logic = {
|
||||||
window.close();
|
window.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const identities = Logic.identities();
|
||||||
// We monitor if the search input is focused so we can disable opening
|
if ((e.keyCode >= 49 && e.keyCode <= 57) &&
|
||||||
// containers by typing a digit between 0-9 while the popup is open.
|
Logic._currentPanel === "containersList") {
|
||||||
const searchInput = document.getElementById("search-terms");
|
const identity = identities[e.keyCode - 49];
|
||||||
let isSearchInputFocused = false;
|
if (identity) {
|
||||||
|
openNewContainerTab(identity);
|
||||||
if (document.activeElement === searchInput) {
|
|
||||||
isSearchInputFocused = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Logic._currentPanel === "containersList" && !isSearchInputFocused) {
|
|
||||||
switch(e.code) {
|
|
||||||
case "Digit0":
|
|
||||||
case "Digit1":
|
|
||||||
case "Digit2":
|
|
||||||
case "Digit3":
|
|
||||||
case "Digit4":
|
|
||||||
case "Digit5":
|
|
||||||
case "Digit6":
|
|
||||||
case "Digit7":
|
|
||||||
case "Digit8":
|
|
||||||
case "Digit9":
|
|
||||||
openTopContainers();
|
|
||||||
break;
|
|
||||||
case "Slash":
|
|
||||||
document.getElementById("search-terms").focus();
|
|
||||||
e.preventDefault();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -509,23 +462,6 @@ const Logic = {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
filterContainerList() {
|
|
||||||
const pattern = /^\s+|\s+$/g;
|
|
||||||
const list = Array.from(document.querySelectorAll("#identities-list tr"));
|
|
||||||
const search = document.querySelector("#search-terms").value.replace(pattern, "").toLowerCase();
|
|
||||||
|
|
||||||
for (const i in list) {
|
|
||||||
const text = list[i].querySelector("td div span");
|
|
||||||
|
|
||||||
if (text.innerText.replace(pattern, "").toLowerCase().includes(search) ||
|
|
||||||
!search) {
|
|
||||||
list[i].style.display = "block";
|
|
||||||
} else {
|
|
||||||
list[i].style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -685,7 +621,7 @@ Logic.registerPanel(P_ONBOARDING_7, {
|
||||||
// Let's move to the containers list panel.
|
// Let's move to the containers list panel.
|
||||||
Utils.addEnterHandler(document.querySelector("#sign-in"), async () => {
|
Utils.addEnterHandler(document.querySelector("#sign-in"), async () => {
|
||||||
browser.tabs.create({
|
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&brand=mozilla",
|
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);
|
await Logic.setOnboardingStage(7);
|
||||||
Logic.showPanel(P_ONBOARDING_8);
|
Logic.showPanel(P_ONBOARDING_8);
|
||||||
|
@ -707,29 +643,14 @@ Logic.registerPanel(P_ONBOARDING_8, {
|
||||||
|
|
||||||
// This method is called when the object is registered.
|
// This method is called when the object is registered.
|
||||||
initialize() {
|
initialize() {
|
||||||
document.querySelectorAll(".onboarding-done").forEach(el => {
|
Utils.addEnterHandler(document.querySelector("#onboarding-done-btn"), async () => {
|
||||||
Utils.addEnterHandler(el, async () => {
|
await Logic.setOnboardingStage(8);
|
||||||
await Logic.setOnboardingStage(8);
|
Logic.showPanel(P_CONTAINERS_LIST);
|
||||||
Logic.showPanel(P_CONTAINERS_LIST);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// This method is called when the panel is shown.
|
// This method is called when the panel is shown.
|
||||||
async prepare() {
|
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);
|
return Promise.resolve(null);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -741,7 +662,24 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
|
||||||
|
|
||||||
// This method is called when the object is registered.
|
// This method is called when the object is registered.
|
||||||
async initialize() {
|
async initialize() {
|
||||||
|
const mozillaVpnToutName = "moz-tout-main-panel";
|
||||||
|
|
||||||
await browser.runtime.sendMessage({ method: "MozillaVPN_queryStatus" });
|
await browser.runtime.sendMessage({ method: "MozillaVPN_queryStatus" });
|
||||||
|
Utils.addEnterHandler(document.querySelector("#moz-vpn-learn-more"), () => {
|
||||||
|
MozillaVPN.handleMozillaCtaClick("mac-main-panel-btn");
|
||||||
|
window.close();
|
||||||
|
});
|
||||||
|
Utils.addEnterHandler(document.querySelector(".dismiss-moz-vpn-tout"), async() => {
|
||||||
|
const { mozillaVpnHiddenToutsList } = await browser.storage.local.get("mozillaVpnHiddenToutsList");
|
||||||
|
if (typeof(mozillaVpnHiddenToutsList) === "undefined") {
|
||||||
|
await browser.storage.local.set({ "mozillaVpnHiddenToutsList": [] });
|
||||||
|
}
|
||||||
|
document.querySelector("#moz-vpn-tout").classList.add("disappear");
|
||||||
|
mozillaVpnHiddenToutsList.push({
|
||||||
|
name: mozillaVpnToutName
|
||||||
|
});
|
||||||
|
await browser.storage.local.set({ mozillaVpnHiddenToutsList });
|
||||||
|
});
|
||||||
Utils.addEnterHandler(document.querySelector("#manage-containers-link"), (e) => {
|
Utils.addEnterHandler(document.querySelector("#manage-containers-link"), (e) => {
|
||||||
if (!e.target.classList.contains("disable-edit-containers")) {
|
if (!e.target.classList.contains("disable-edit-containers")) {
|
||||||
Logic.showPanel(MANAGE_CONTAINERS_PICKER);
|
Logic.showPanel(MANAGE_CONTAINERS_PICKER);
|
||||||
|
@ -756,6 +694,9 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
|
||||||
Utils.addEnterHandler(document.querySelector("#always-open-in"), () => {
|
Utils.addEnterHandler(document.querySelector("#always-open-in"), () => {
|
||||||
Logic.showPanel(ALWAYS_OPEN_IN_PICKER);
|
Logic.showPanel(ALWAYS_OPEN_IN_PICKER);
|
||||||
});
|
});
|
||||||
|
Utils.addEnterHandler(document.querySelector("#info-icon"), () => {
|
||||||
|
browser.runtime.openOptionsPage();
|
||||||
|
});
|
||||||
Utils.addEnterHandler(document.querySelector("#sort-containers-link"), async () => {
|
Utils.addEnterHandler(document.querySelector("#sort-containers-link"), async () => {
|
||||||
try {
|
try {
|
||||||
await browser.runtime.sendMessage({
|
await browser.runtime.sendMessage({
|
||||||
|
@ -767,32 +708,16 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const mozillaVpnPermissionsWarningDotName = "moz-permissions-warning-dot";
|
const mozVpnTout = document.getElementById("moz-vpn-tout");
|
||||||
|
const mozillaVpnInstalled = await browser.runtime.sendMessage({ method: "MozillaVPN_getInstallationStatus" });
|
||||||
let { mozillaVpnHiddenToutsList } = await browser.storage.local.get("mozillaVpnHiddenToutsList");
|
if (mozillaVpnInstalled) {
|
||||||
if (typeof(mozillaVpnHiddenToutsList) === "undefined") {
|
return mozVpnTout.remove();
|
||||||
await browser.storage.local.set({ "mozillaVpnHiddenToutsList": [] });
|
|
||||||
mozillaVpnHiddenToutsList = [];
|
|
||||||
}
|
}
|
||||||
|
const { mozillaVpnHiddenToutsList } = await browser.storage.local.get("mozillaVpnHiddenToutsList");
|
||||||
// Badge Options icon if both nativeMessaging and/or proxy permissions are disabled
|
const mozillaVpnToutShouldBeHidden = mozillaVpnHiddenToutsList && mozillaVpnHiddenToutsList.find(tout => tout.name === mozillaVpnToutName);
|
||||||
const bothMozillaVpnPermissionsEnabled = await MozillaVPN.bothPermissionsEnabled();
|
if (mozillaVpnToutShouldBeHidden) {
|
||||||
const warningDotShouldBeHidden = mozillaVpnHiddenToutsList.find(tout => tout.name === mozillaVpnPermissionsWarningDotName);
|
return mozVpnTout.remove();
|
||||||
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() {
|
unregister() {
|
||||||
|
@ -824,6 +749,7 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="menu-text">${identity.name}</span>
|
<span class="menu-text">${identity.name}</span>
|
||||||
|
<span class="tooltip proxy-unavailable">This container has been configured to use a Mozilla VPN proxy, but Mozilla VPN is not on. Turn Mozilla VPN on to use this proxy.</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="menu-right-float">
|
<span class="menu-right-float">
|
||||||
<img alt="" class="always-open-in-flag flag-img" src="/img/flags/.png"/>
|
<img alt="" class="always-open-in-flag flag-img" src="/img/flags/.png"/>
|
||||||
|
@ -841,6 +767,8 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
|
||||||
tr.appendChild(td);
|
tr.appendChild(td);
|
||||||
|
|
||||||
const openInThisContainer = tr.querySelector(".menu-item-name");
|
const openInThisContainer = tr.querySelector(".menu-item-name");
|
||||||
|
// const mozProxyWarning = await MozillaVPN.getProxyWarnings(proxies[identity.cookieStoreId]);
|
||||||
|
// openInThisContainer.dataset.mozProxyWarning = mozProxyWarning;
|
||||||
Utils.addEnterHandler(openInThisContainer, (e) => {
|
Utils.addEnterHandler(openInThisContainer, (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (openInThisContainer.dataset.mozProxyWarning === "proxy-unavailable") {
|
if (openInThisContainer.dataset.mozProxyWarning === "proxy-unavailable") {
|
||||||
|
@ -873,6 +801,7 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
|
||||||
Utils.addEnterHandler(showPanelButton, () => {
|
Utils.addEnterHandler(showPanelButton, () => {
|
||||||
Logic.showPanel(P_CONTAINER_INFO, identity);
|
Logic.showPanel(P_CONTAINER_INFO, identity);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const list = document.querySelector("#identities-list");
|
const list = document.querySelector("#identities-list");
|
||||||
|
@ -882,7 +811,6 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
|
||||||
|
|
||||||
document.addEventListener("keydown", Logic.keyboardNavListener);
|
document.addEventListener("keydown", Logic.keyboardNavListener);
|
||||||
document.addEventListener("keydown", Logic.shortcutListener);
|
document.addEventListener("keydown", Logic.shortcutListener);
|
||||||
document.addEventListener("input", Logic.filterContainerList);
|
|
||||||
|
|
||||||
MozillaVPN.handleContainerList(identities);
|
MozillaVPN.handleContainerList(identities);
|
||||||
|
|
||||||
|
@ -959,7 +887,6 @@ Logic.registerPanel(P_CONTAINER_INFO, {
|
||||||
Utils.alwaysOpenInContainer(identity);
|
Utils.alwaysOpenInContainer(identity);
|
||||||
window.close();
|
window.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show or not the has-tabs section.
|
// Show or not the has-tabs section.
|
||||||
for (let trHasTabs of document.getElementsByClassName("container-info-has-tabs")) { // eslint-disable-line prefer-const
|
for (let trHasTabs of document.getElementsByClassName("container-info-has-tabs")) { // eslint-disable-line prefer-const
|
||||||
trHasTabs.style.display = !identity.hasHiddenTabs && !identity.hasOpenTabs ? "none" : "";
|
trHasTabs.style.display = !identity.hasHiddenTabs && !identity.hasOpenTabs ? "none" : "";
|
||||||
|
@ -983,13 +910,6 @@ Logic.registerPanel(P_CONTAINER_INFO, {
|
||||||
Utils.addEnterHandler(manageContainer, async () => {
|
Utils.addEnterHandler(manageContainer, async () => {
|
||||||
Logic.showPanel(P_CONTAINER_EDIT, identity);
|
Logic.showPanel(P_CONTAINER_EDIT, identity);
|
||||||
});
|
});
|
||||||
const clearContainerStorageButton = document.getElementById("clear-container-storage-info");
|
|
||||||
Utils.addEnterHandler(clearContainerStorageButton, async () => {
|
|
||||||
const granted = await browser.permissions.request({ permissions: ["browsingData"] });
|
|
||||||
if (granted) {
|
|
||||||
Logic.showPanel(P_CLEAR_CONTAINER_STORAGE, identity);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return this.buildOpenTabTable(tabs);
|
return this.buildOpenTabTable(tabs);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1280,8 +1200,7 @@ Logic.registerPanel(REOPEN_IN_CONTAINER_PICKER, {
|
||||||
false,
|
false,
|
||||||
newUserContextId,
|
newUserContextId,
|
||||||
currentTab.index + 1,
|
currentTab.index + 1,
|
||||||
currentTab.active,
|
currentTab.active
|
||||||
currentTab.groupId
|
|
||||||
);
|
);
|
||||||
window.close();
|
window.close();
|
||||||
};
|
};
|
||||||
|
@ -1291,7 +1210,6 @@ Logic.registerPanel(REOPEN_IN_CONTAINER_PICKER, {
|
||||||
if (currentTab.cookieStoreId !== "firefox-default") {
|
if (currentTab.cookieStoreId !== "firefox-default") {
|
||||||
const tr = document.createElement("tr");
|
const tr = document.createElement("tr");
|
||||||
tr.classList.add("menu-item", "hover-highlight", "keyboard-nav");
|
tr.classList.add("menu-item", "hover-highlight", "keyboard-nav");
|
||||||
tr.setAttribute("tabindex", "0");
|
|
||||||
const td = document.createElement("td");
|
const td = document.createElement("td");
|
||||||
|
|
||||||
td.innerHTML = Utils.escaped`
|
td.innerHTML = Utils.escaped`
|
||||||
|
@ -1311,8 +1229,7 @@ Logic.registerPanel(REOPEN_IN_CONTAINER_PICKER, {
|
||||||
false,
|
false,
|
||||||
0,
|
0,
|
||||||
currentTab.index + 1,
|
currentTab.index + 1,
|
||||||
currentTab.active,
|
currentTab.active
|
||||||
currentTab.groupId
|
|
||||||
);
|
);
|
||||||
window.close();
|
window.close();
|
||||||
});
|
});
|
||||||
|
@ -1453,14 +1370,11 @@ Logic.registerPanel(P_CONTAINER_ASSIGNMENTS, {
|
||||||
/* 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.
|
/* 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 */
|
This is pending a better solution for favicons from web extensions */
|
||||||
const assumedUrl = `https://${site.hostname}/favicon.ico`;
|
const assumedUrl = `https://${site.hostname}/favicon.ico`;
|
||||||
const resetSiteCookiesInfo = browser.i18n.getMessage("clearSiteCookiesTooltipInfo");
|
|
||||||
const deleteSiteInfo = browser.i18n.getMessage("deleteSiteTooltipInfo");
|
|
||||||
trElement.innerHTML = Utils.escaped`
|
trElement.innerHTML = Utils.escaped`
|
||||||
<td>
|
<td>
|
||||||
<div class="favicon"></div>
|
<div class="favicon"></div>
|
||||||
<span title="${site.hostname}" class="menu-text truncate-text">${site.hostname}</span>
|
<span title="${site.hostname}" class="menu-text">${site.hostname}</span>
|
||||||
<img title="${resetSiteCookiesInfo}" class="reset-button reset-assignment" src="/img/refresh-16.svg" />
|
<img class="trash-button delete-assignment" src="/img/container-delete.svg" />
|
||||||
<img title="${deleteSiteInfo}" class="trash-button delete-assignment" src="/img/container-delete.svg" />
|
|
||||||
</td>`;
|
</td>`;
|
||||||
trElement.getElementsByClassName("favicon")[0].appendChild(Utils.createFavIconElement(assumedUrl));
|
trElement.getElementsByClassName("favicon")[0].appendChild(Utils.createFavIconElement(assumedUrl));
|
||||||
const deleteButton = trElement.querySelector(".trash-button");
|
const deleteButton = trElement.querySelector(".trash-button");
|
||||||
|
@ -1472,20 +1386,6 @@ Logic.registerPanel(P_CONTAINER_ASSIGNMENTS, {
|
||||||
delete assignments[siteKey];
|
delete assignments[siteKey];
|
||||||
this.showAssignedContainers(assignments);
|
this.showAssignedContainers(assignments);
|
||||||
});
|
});
|
||||||
const resetButton = trElement.querySelector(".reset-button");
|
|
||||||
Utils.addEnterHandler(resetButton, async () => {
|
|
||||||
const cookieStoreId = Logic.currentCookieStoreId();
|
|
||||||
const granted = await browser.permissions.request({ permissions: ["browsingData"] });
|
|
||||||
if (!granted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const result = await Utils.resetCookiesForSite(site.hostname, cookieStoreId);
|
|
||||||
if (result === true) {
|
|
||||||
Logic.notify({messageId: "cookiesClearedSuccess", placeholders: [site.hostname]});
|
|
||||||
} else {
|
|
||||||
Logic.notify({messageId: "cookiesCouldNotBeCleared", placeholders: [site.hostname]});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
trElement.classList.add("menu-item", "hover-highlight", "keyboard-nav");
|
trElement.classList.add("menu-item", "hover-highlight", "keyboard-nav");
|
||||||
tableElement.appendChild(trElement);
|
tableElement.appendChild(trElement);
|
||||||
});
|
});
|
||||||
|
@ -1540,25 +1440,18 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
|
||||||
async connectedCallback() {
|
async connectedCallback() {
|
||||||
const { mozillaVpnHiddenToutsList } = await browser.storage.local.get("mozillaVpnHiddenToutsList");
|
const { mozillaVpnHiddenToutsList } = await browser.storage.local.get("mozillaVpnHiddenToutsList");
|
||||||
const mozillaVpnCollapseEditContainerTout = mozillaVpnHiddenToutsList && mozillaVpnHiddenToutsList.find(tout => tout.name === this.toutName);
|
const mozillaVpnCollapseEditContainerTout = mozillaVpnHiddenToutsList && mozillaVpnHiddenToutsList.find(tout => tout.name === this.toutName);
|
||||||
const mozillaVpnInstalled = await browser.runtime.sendMessage({ method: "MozillaVPN_getInstallationStatus" });
|
|
||||||
|
|
||||||
this.hideShowButton.addEventListener("click", this);
|
this.hideShowButton.addEventListener("click", this);
|
||||||
|
|
||||||
if (mozillaVpnCollapseEditContainerTout && !mozillaVpnInstalled) {
|
if (mozillaVpnCollapseEditContainerTout) {
|
||||||
this.collapseUi();
|
this.collapseUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add listeners
|
// Add listeners
|
||||||
if (!this.classList.contains("has-attached-listeners")) {
|
if (!this.classList.contains("has-attached-listeners")) {
|
||||||
|
|
||||||
const bothMozillaVpnPermissionsEnabled = await MozillaVPN.bothPermissionsEnabled();
|
this.primaryCta.addEventListener("click", () => {
|
||||||
this.primaryCta.addEventListener("click", async() => {
|
MozillaVPN.handleMozillaCtaClick("mac-edit-container-panel-btn");
|
||||||
if (!bothMozillaVpnPermissionsEnabled && mozillaVpnInstalled) {
|
|
||||||
await browser.permissions.request({ permissions: ["proxy", "nativeMessaging"] });
|
|
||||||
} else {
|
|
||||||
MozillaVPN.handleMozillaCtaClick("mac-edit-container-panel-btn");
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.switch.addEventListener("click", async() => {
|
this.switch.addEventListener("click", async() => {
|
||||||
|
@ -1611,9 +1504,9 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
|
||||||
await proxifiedContainers.set(id.cookieStoreId, proxy);
|
await proxifiedContainers.set(id.cookieStoreId, proxy);
|
||||||
this.switch.checked = true;
|
this.switch.checked = true;
|
||||||
this.updateProxyDependentUi(proxy);
|
this.updateProxyDependentUi(proxy);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.switch.checked = false;
|
this.switch.checked = false;
|
||||||
this.updateProxyDependentUi({});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1627,35 +1520,24 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
|
||||||
const mozillaVpnInstalled = await browser.runtime.sendMessage({ method: "MozillaVPN_getInstallationStatus" });
|
const mozillaVpnInstalled = await browser.runtime.sendMessage({ method: "MozillaVPN_getInstallationStatus" });
|
||||||
const mozillaVpnConnected = await browser.runtime.sendMessage({ method: "MozillaVPN_getConnectionStatus" });
|
const mozillaVpnConnected = await browser.runtime.sendMessage({ method: "MozillaVPN_getConnectionStatus" });
|
||||||
|
|
||||||
this.subtitle.textContent = browser.i18n.getMessage("integrateContainers");
|
if (!mozillaVpnInstalled) {
|
||||||
|
|
||||||
const bothMozillaVpnPermissionsEnabled = await MozillaVPN.bothPermissionsEnabled();
|
this.hideEls(this.switch, this.switchLabel, this.currentServerButton);
|
||||||
|
this.subtitle.textContent = browser.i18n.getMessage("protectThisContainer");
|
||||||
|
this.primaryCta.addEventListener("click", this);
|
||||||
|
|
||||||
if (mozillaVpnInstalled && !bothMozillaVpnPermissionsEnabled) {
|
} else {
|
||||||
this.subtitle.style.flex = "1 1 100%";
|
|
||||||
this.classList.remove("show-server-button");
|
// Mozilla VPN installed...
|
||||||
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
|
// Hide cta and hide/show button
|
||||||
this.hideEls(this.primaryCta, this.hideShowButton);
|
this.hideEls(this.primaryCta, this.hideShowButton);
|
||||||
|
|
||||||
// Update subtitle
|
// Update subtitle
|
||||||
this.subtitle.textContent = mozillaVpnConnected ? browser.i18n.getMessage("useCustomLocation") : browser.i18n.getMessage("mozillaVpnMustBeOn");
|
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) {
|
if (!mozillaVpnConnected) {
|
||||||
[this.switchLabel, this.switch].forEach(el => {
|
|
||||||
el.style.display = "inline-block";
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.hideEls(this.switch, this.switchLabel, this.currentServerButton);
|
this.hideEls(this.switch, this.switchLabel, this.currentServerButton);
|
||||||
this.switch.checked = false;
|
this.switch.checked = false;
|
||||||
}
|
}
|
||||||
|
@ -1702,15 +1584,17 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
|
||||||
this.mozProxyEnabledInput.value = proxyInfo.mozProxyEnabled;
|
this.mozProxyEnabledInput.value = proxyInfo.mozProxyEnabled;
|
||||||
this.proxyAddressInput.value = `${proxyInfo.type}://${proxyInfo.host}:${proxyInfo.port}`;
|
this.proxyAddressInput.value = `${proxyInfo.type}://${proxyInfo.host}:${proxyInfo.port}`;
|
||||||
|
|
||||||
if (typeof(proxyInfo.countryCode) === "undefined" && proxyInfo.type) {
|
if (typeof(proxyInfo.countryCode) === "undefined" && proxyInfo.type !== "direct") {
|
||||||
// Set custom proxy URL below 'Advanced proxy settings' button label
|
// Set custom proxy URL below 'Advanced proxy settings' button label
|
||||||
this.advancedProxyAddress.textContent = `${proxyInfo.type}://${proxyInfo.host}:${proxyInfo.port}`;
|
this.advancedProxyAddress.textContent = `${proxyInfo.type}://${proxyInfo.host}:${proxyInfo.port}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateProxyDependentUi(proxyInfo) {
|
async updateProxyDependentUi(proxyInfo) {
|
||||||
|
const containerHasProxy = typeof(proxyInfo) !== "undefined";
|
||||||
|
|
||||||
const mozillaVpnProxyLocationAvailable = (proxy) => {
|
const mozillaVpnProxyLocationAvailable = (proxy) => {
|
||||||
return typeof(proxy) !== "undefined" && typeof(proxy.countryCode) !== "undefined" && typeof(proxy.cityName) !== "undefined";
|
return typeof(proxy.countryCode) !== "undefined" && typeof(proxyInfo.cityName) !== "undefined";
|
||||||
};
|
};
|
||||||
|
|
||||||
const mozillaVpnProxyIsEnabled = (proxy) => {
|
const mozillaVpnProxyIsEnabled = (proxy) => {
|
||||||
|
@ -1723,7 +1607,7 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
|
||||||
|
|
||||||
const mozillaVpnConnected = await browser.runtime.sendMessage({ method: "MozillaVPN_getConnectionStatus" });
|
const mozillaVpnConnected = await browser.runtime.sendMessage({ method: "MozillaVPN_getConnectionStatus" });
|
||||||
if (
|
if (
|
||||||
!proxyInfo ||
|
!containerHasProxy ||
|
||||||
!mozillaVpnProxyLocationAvailable(proxyInfo) ||
|
!mozillaVpnProxyLocationAvailable(proxyInfo) ||
|
||||||
!mozillaVpnConnected
|
!mozillaVpnConnected
|
||||||
) {
|
) {
|
||||||
|
@ -1738,20 +1622,23 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate inputs and server button with current or previously stored mozilla vpn proxy
|
// Populate inputs and server button with current or previously stored mozilla vpn proxy
|
||||||
if(proxyInfo && mozillaVpnProxyLocationAvailable(proxyInfo)) {
|
if(containerHasProxy && mozillaVpnProxyLocationAvailable(proxyInfo)) {
|
||||||
this.currentCountryFlag.style.backgroundImage = `url("./img/flags/${proxyInfo.countryCode.toUpperCase()}.png")`;
|
this.currentCountryFlag.style.backgroundImage = `url("./img/flags/${proxyInfo.countryCode.toUpperCase()}.png")`;
|
||||||
this.currentCountryFlag.style.backgroundImage = proxyInfo.countryCode + ".png";
|
this.currentCountryFlag.style.backgroundImage = proxyInfo.countryCode + ".png";
|
||||||
this.currentCityName.textContent = proxyInfo.cityName;
|
this.currentCityName.textContent = proxyInfo.cityName;
|
||||||
this.countryCode = proxyInfo.countryCode;
|
this.countryCode = proxyInfo.countryCode;
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
expandUi() {
|
expandUi() {
|
||||||
this.classList.add("expanded");
|
this.classList.add("expanded");
|
||||||
|
this.style.maxHeight = 500 + "px";
|
||||||
}
|
}
|
||||||
|
|
||||||
collapseUi() {
|
collapseUi() {
|
||||||
this.classList.remove("expanded");
|
this.classList.remove("expanded");
|
||||||
|
this.style.maxHeight = 56 + "px";
|
||||||
}
|
}
|
||||||
|
|
||||||
hideEls(...els) {
|
hideEls(...els) {
|
||||||
|
@ -1788,10 +1675,6 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
|
||||||
customElements.define("moz-vpn-container-ui", MozVpnContainerUi);
|
customElements.define("moz-vpn-container-ui", MozVpnContainerUi);
|
||||||
const mozillaVpnUi = document.querySelector("moz-vpn-container-ui");
|
const mozillaVpnUi = document.querySelector("moz-vpn-container-ui");
|
||||||
mozillaVpnUi.updateMozVpnStatusDependentUi();
|
mozillaVpnUi.updateMozVpnStatusDependentUi();
|
||||||
|
|
||||||
browser.permissions.onAdded.addListener(() => { mozillaVpnUi.updateMozVpnStatusDependentUi(); });
|
|
||||||
browser.permissions.onRemoved.addListener(() => { mozillaVpnUi.updateMozVpnStatusDependentUi(); });
|
|
||||||
|
|
||||||
const advancedProxySettingsButton = document.querySelector(".advanced-proxy-settings-btn");
|
const advancedProxySettingsButton = document.querySelector(".advanced-proxy-settings-btn");
|
||||||
Utils.addEnterHandler(advancedProxySettingsButton, () => {
|
Utils.addEnterHandler(advancedProxySettingsButton, () => {
|
||||||
Logic.showPanel(P_ADVANCED_PROXY_SETTINGS, this.getEditInProgressIdentity(), false, false);
|
Logic.showPanel(P_ADVANCED_PROXY_SETTINGS, this.getEditInProgressIdentity(), false, false);
|
||||||
|
@ -1954,65 +1837,60 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const proxyPermissionEnabled = await browser.permissions.contains({ permissions: ["proxy"] });
|
const proxyData = await proxifiedContainers.retrieve(identity.cookieStoreId);
|
||||||
if (proxyPermissionEnabled) {
|
if (proxyData) {
|
||||||
const proxyData = await proxifiedContainers.retrieve(identity.cookieStoreId);
|
if (proxyData.proxy && proxyData.proxy.mozProxyEnabled && !mozillaVpnConnected) {
|
||||||
if (proxyData && proxyData.proxy.mozProxyEnabled && !mozillaVpnConnected) {
|
|
||||||
mozillaVpnUi.updateProxyDependentUi({});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const proxy = proxyData ? proxyData.proxy : {};
|
mozillaVpnUi.updateProxyDependentUi(proxyData.proxy);
|
||||||
mozillaVpnUi.updateProxyDependentUi(proxy);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mozillaVpnUi.updateProxyDependentUi({});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Logic.registerPanel(P_ADVANCED_PROXY_SETTINGS, {
|
Logic.registerPanel(P_ADVANCED_PROXY_SETTINGS, {
|
||||||
panelSelector: "#advanced-proxy-settings-panel",
|
panelSelector: "#advanced-proxy-settings-panel",
|
||||||
|
|
||||||
async initialize() {
|
initialize(){
|
||||||
this._proxyForm = document.querySelector(".advanced-proxy-panel-content");
|
this._proxyForm = document.querySelector(".advanced-proxy-panel-content");
|
||||||
this._advancedProxyInput = this._proxyForm.querySelector("#edit-advanced-proxy-input");
|
const advancedProxyInput = this._proxyForm.querySelector("#edit-advanced-proxy-input");
|
||||||
const clearAdvancedProxyInput = this._proxyForm.querySelector("#clear-advanced-proxy-input");
|
const clearadvancedProxyInput = this._proxyForm.querySelector("#clear-advanced-proxy-input");
|
||||||
this._submitadvancedProxy = this._proxyForm.querySelector("#submit-advanced-proxy");
|
this._submitadvancedProxy = this._proxyForm.querySelector("#submit-advanced-proxy");
|
||||||
|
advancedProxyInput.addEventListener("keydown", () => {
|
||||||
this._advancedProxyInput.addEventListener("keydown", (e) => {
|
advancedProxyInput.dataset.editedStatus = "edited";
|
||||||
if (e.key !== "Enter") {
|
|
||||||
this.hideInvalidEntryWarning();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this._submitadvancedProxy.addEventListener("click", (e) => {
|
this._submitadvancedProxy.addEventListener("click", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.submitProxyForm();
|
const parsedProxy = proxifiedContainers.parseProxy(advancedProxyInput.value);
|
||||||
|
if (advancedProxyInput.value.length > 0 && !parsedProxy) {
|
||||||
|
this._proxyForm.classList.add("invalid");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const identity = Logic.currentIdentity();
|
||||||
|
proxifiedContainers.set(identity.cookieStoreId, parsedProxy);
|
||||||
|
Logic.showPanel(P_CONTAINER_EDIT, Logic.currentIdentity(), false, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
clearAdvancedProxyInput.addEventListener("click", (e) => {
|
clearadvancedProxyInput.addEventListener("click", (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
this._proxyForm.classList.remove("invalid");
|
||||||
const activeEl = document.activeElement;
|
advancedProxyInput.value = "";
|
||||||
if (activeEl === this._advancedProxyInput) {
|
|
||||||
return this.submitProxyForm();
|
|
||||||
}
|
|
||||||
if (activeEl !== clearAdvancedProxyInput) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.hideInvalidEntryWarning();
|
|
||||||
this._advancedProxyInput.value = "";
|
|
||||||
this._advancedProxyInput.focus();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this._advancedProxyInput.addEventListener("blur", () => {
|
advancedProxyInput.addEventListener("blur", () => {
|
||||||
if (this._advancedProxyInput.value.length === 0) {
|
if (advancedProxyInput.value.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(!proxifiedContainers.parseProxy(this._advancedProxyInput.value)) {
|
if(!proxifiedContainers.parseProxy(advancedProxyInput.value)) {
|
||||||
this.showInvalidEntryWarning();
|
this._proxyForm.classList.add("invalid");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
advancedProxyInput.addEventListener("focus", () => {
|
||||||
this._advancedProxyInput.addEventListener("focus", () => {
|
this._proxyForm.classList.remove("invalid");
|
||||||
this.hideInvalidEntryWarning();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const returnButton = document.getElementById("advanced-proxy-settings-return");
|
const returnButton = document.getElementById("advanced-proxy-settings-return");
|
||||||
|
@ -2026,94 +1904,31 @@ Logic.registerPanel(P_ADVANCED_PROXY_SETTINGS, {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
showInvalidEntryWarning() {
|
|
||||||
this._proxyForm.classList.add("invalid");
|
|
||||||
},
|
|
||||||
|
|
||||||
hideInvalidEntryWarning() {
|
|
||||||
this._proxyForm.classList.remove("invalid");
|
|
||||||
},
|
|
||||||
|
|
||||||
async submitProxyForm() {
|
|
||||||
const parsedProxy = proxifiedContainers.parseProxy(this._advancedProxyInput.value);
|
|
||||||
if (this._advancedProxyInput.value.length > 0 && !parsedProxy) {
|
|
||||||
this.showInvalidEntryWarning();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const identity = Logic.currentIdentity();
|
|
||||||
proxifiedContainers.set(identity.cookieStoreId, parsedProxy);
|
|
||||||
|
|
||||||
Logic.showPanel(P_CONTAINER_EDIT, Logic.currentIdentity(), false, false);
|
|
||||||
},
|
|
||||||
|
|
||||||
async prepare() {
|
async prepare() {
|
||||||
const identity = Logic.currentIdentity();
|
const identity = Logic.currentIdentity();
|
||||||
|
|
||||||
const advancedProxyInput = document.getElementById("edit-advanced-proxy-input");
|
const advancedProxyInput = document.getElementById("edit-advanced-proxy-input");
|
||||||
|
|
||||||
const proxyPermissionEnabled = await browser.permissions.contains({ permissions: ["proxy"] });
|
// Clear the proxy field, reset form validity and edited classes
|
||||||
if (!proxyPermissionEnabled) {
|
advancedProxyInput.value = "";
|
||||||
|
advancedProxyInput.dataset.editedStatus = "";
|
||||||
// Restrict tabbing inside advanced proxy panel to proxy permissions ui
|
this._proxyForm.classList.remove("invalid");
|
||||||
const panel = document.getElementById("advanced-proxy-settings-panel");
|
|
||||||
const clickableEls = panel.querySelectorAll("button, a, input");
|
|
||||||
clickableEls.forEach(el => {
|
|
||||||
if (!el.dataset.tabGroup && el.id !== "advanced-proxy-settings-return") {
|
|
||||||
el.setAttribute("tabindex", "-1");
|
|
||||||
el.disabled = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Show proxy permission overlay
|
|
||||||
const permissionsOverlay = document.getElementById("permissions-overlay");
|
|
||||||
permissionsOverlay.style.display = "flex";
|
|
||||||
|
|
||||||
// Add "enable" button handling
|
|
||||||
const enableProxyPermissionsButton = document.getElementById("enable-proxy-permissions");
|
|
||||||
|
|
||||||
enableProxyPermissionsButton.addEventListener("click", async() => {
|
|
||||||
const granted = await browser.permissions.request({ permissions: ["proxy"] });
|
|
||||||
if (granted) {
|
|
||||||
permissionsOverlay.style.display = "none";
|
|
||||||
// restore normal panel tabbing
|
|
||||||
clickableEls.forEach(el => {
|
|
||||||
el.tabindex = "0";
|
|
||||||
el.disabled = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// reset input
|
|
||||||
const resetProxyInput = () => {
|
|
||||||
if (!advancedProxyInput) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
advancedProxyInput.value = "";
|
|
||||||
};
|
|
||||||
|
|
||||||
resetProxyInput();
|
|
||||||
|
|
||||||
this.hideInvalidEntryWarning();
|
|
||||||
|
|
||||||
const setProxyInputPlaceholder = (proxy) => {
|
|
||||||
this._advancedProxyInput.value = `${proxy.type}://${proxy.host}:${proxy.port}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const edit_proxy_dom = function(proxy) {
|
const edit_proxy_dom = function(proxy) {
|
||||||
if (!proxy.type || MozillaVPN.proxyIsDisabled(proxy)) {
|
if (proxy.type === "direct" || typeof proxy.type === "undefined" || MozillaVPN.proxyIsDisabled(proxy)) {
|
||||||
resetProxyInput();
|
advancedProxyInput.value = "";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return setProxyInputPlaceholder(proxy);
|
advancedProxyInput.value = `${proxy.type}://${proxy.host}:${proxy.port}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const proxyData = await proxifiedContainers.retrieve(identity.cookieStoreId);
|
const proxyData = await proxifiedContainers.retrieve(identity.cookieStoreId);
|
||||||
if (proxyData) {
|
if (proxyData) {
|
||||||
edit_proxy_dom(proxyData.proxy);
|
edit_proxy_dom(proxyData.proxy);
|
||||||
} else {
|
} else {
|
||||||
resetProxyInput();
|
advancedProxyInput.value = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const containerColor = document.querySelector(".proxy-title-container-color");
|
const containerColor = document.querySelector(".proxy-title-container-color");
|
||||||
containerColor.dataset.identityColor = identity.color;
|
containerColor.dataset.identityColor = identity.color;
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
|
@ -2128,6 +1943,17 @@ Logic.registerPanel(P_MOZILLA_VPN_SERVER_LIST, {
|
||||||
|
|
||||||
Utils.addEnterHandler(document.getElementById("moz-vpn-return"), async () => {
|
Utils.addEnterHandler(document.getElementById("moz-vpn-return"), async () => {
|
||||||
const identity = Logic.currentIdentity();
|
const identity = Logic.currentIdentity();
|
||||||
|
const { mozillaVpnServers } = await browser.storage.local.get("mozillaVpnServers");
|
||||||
|
|
||||||
|
const selectedServer = document.querySelector(".server-radio-btn:checked");
|
||||||
|
const proxy = MozillaVPN.getProxy(
|
||||||
|
selectedServer.dataset.countryCode,
|
||||||
|
selectedServer.dataset.cityName,
|
||||||
|
true,
|
||||||
|
mozillaVpnServers
|
||||||
|
);
|
||||||
|
|
||||||
|
await proxifiedContainers.set(identity.cookieStoreId, proxy);
|
||||||
Logic.showPanel(P_CONTAINER_EDIT, identity, false, false);
|
Logic.showPanel(P_CONTAINER_EDIT, identity, false, false);
|
||||||
Logic.showPreviousPanel();
|
Logic.showPreviousPanel();
|
||||||
});
|
});
|
||||||
|
@ -2171,21 +1997,6 @@ Logic.registerPanel(P_MOZILLA_VPN_SERVER_LIST, {
|
||||||
radioBtn.dataset.cityName = city.name;
|
radioBtn.dataset.cityName = city.name;
|
||||||
radioBtn.name = "server-city";
|
radioBtn.name = "server-city";
|
||||||
|
|
||||||
const cityListItem = cityTemplateClone.querySelector(".server-city-list-item");
|
|
||||||
Utils.addEnterHandler((cityListItem), async(e) => {
|
|
||||||
if (e.key === "Enter") {
|
|
||||||
radioBtn.checked = true;
|
|
||||||
}
|
|
||||||
const identity = Logic.currentIdentity();
|
|
||||||
const proxy = MozillaVPN.getProxy(
|
|
||||||
radioBtn.dataset.countryCode,
|
|
||||||
radioBtn.dataset.cityName,
|
|
||||||
true,
|
|
||||||
mozillaVpnServers
|
|
||||||
);
|
|
||||||
await proxifiedContainers.set(identity.cookieStoreId, proxy);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set city name
|
// Set city name
|
||||||
cityName.textContent = city.name;
|
cityName.textContent = city.name;
|
||||||
cityList.appendChild(cityTemplateClone);
|
cityList.appendChild(cityTemplateClone);
|
||||||
|
@ -2261,47 +2072,6 @@ Logic.registerPanel(P_MOZILLA_VPN_SERVER_LIST, {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// P_CLEAR_CONTAINER_STORAGE: Page for confirming container storage removal.
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
Logic.registerPanel(P_CLEAR_CONTAINER_STORAGE, {
|
|
||||||
panelSelector: "#clear-container-storage-panel",
|
|
||||||
|
|
||||||
// This method is called when the object is registered.
|
|
||||||
initialize() {
|
|
||||||
|
|
||||||
Utils.addEnterHandler(document.querySelector("#clear-container-storage-cancel-link"), () => {
|
|
||||||
const identity = Logic.currentIdentity();
|
|
||||||
Logic.showPanel(P_CONTAINER_INFO, identity, false, false);
|
|
||||||
});
|
|
||||||
Utils.addEnterHandler(document.querySelector("#close-clear-container-storage-panel"), () => {
|
|
||||||
const identity = Logic.currentIdentity();
|
|
||||||
Logic.showPanel(P_CONTAINER_INFO, identity, false, false);
|
|
||||||
});
|
|
||||||
Utils.addEnterHandler(document.querySelector("#clear-container-storage-ok-link"), async () => {
|
|
||||||
const identity = Logic.currentIdentity();
|
|
||||||
const userContextId = Utils.userContextId(identity.cookieStoreId);
|
|
||||||
const result = await browser.runtime.sendMessage({
|
|
||||||
method: "deleteContainerDataOnly",
|
|
||||||
message: { userContextId }
|
|
||||||
});
|
|
||||||
if (result.done === true) {
|
|
||||||
Logic.notify({messageId: "storageWasClearedConfirmation", placeholders: [identity.name]});
|
|
||||||
}
|
|
||||||
Logic.showPanel(P_CONTAINER_INFO, identity, false, false);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// This method is called when the panel is shown.
|
|
||||||
prepare() {
|
|
||||||
const identity = Logic.currentIdentity();
|
|
||||||
|
|
||||||
// Populating the panel: name, icon, and warning message
|
|
||||||
document.getElementById("container-clear-storage-title").textContent = identity.name;
|
|
||||||
return Promise.resolve(null);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// P_CONTAINER_DELETE: Delete a container.
|
// P_CONTAINER_DELETE: Delete a container.
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -2351,6 +2121,15 @@ Logic.registerPanel(P_CONTAINER_DELETE, {
|
||||||
|
|
||||||
// Populating the panel: name, icon, and warning message
|
// Populating the panel: name, icon, and warning message
|
||||||
document.getElementById("container-delete-title").textContent = identity.name;
|
document.getElementById("container-delete-title").textContent = identity.name;
|
||||||
|
|
||||||
|
const totalNumberOfTabs = identity.numberOfHiddenTabs + identity.numberOfOpenTabs;
|
||||||
|
let warningMessage = "";
|
||||||
|
if (totalNumberOfTabs > 0) {
|
||||||
|
const grammaticalNumTabs = totalNumberOfTabs > 1 ? "tabs" : "tab";
|
||||||
|
warningMessage = `If you remove this container now, ${totalNumberOfTabs} container ${grammaticalNumTabs} will be closed.`;
|
||||||
|
}
|
||||||
|
document.getElementById("delete-container-tab-warning").textContent = warningMessage;
|
||||||
|
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -44,14 +44,14 @@ proxifiedContainers = {
|
||||||
|
|
||||||
// Parses a proxy description string of the format type://host[:port] or type://username:password@host[:port] (port is optional)
|
// Parses a proxy description string of the format type://host[:port] or type://username:password@host[:port] (port is optional)
|
||||||
parseProxy(proxy_str, mozillaVpnData = null) {
|
parseProxy(proxy_str, mozillaVpnData = null) {
|
||||||
const proxyRegexp = /(?<type>(https?)|(socks4?)):\/\/(\b(?<username>[\w-]+):(?<password>[\w-]+)@)?(?<host>((?:\d{1,3}\.){3}\d{1,3}\b)|(\b([\w.-]+)+))(:(?<port>\d+))?/;
|
const proxyRegexp = /(?<type>(https?)|(socks4?)):\/\/(\b(?<username>\w+):(?<password>\w+)@)?(?<host>((?:\d{1,3}\.){3}\d{1,3}\b)|(\b([\w.-]+)+))(:(?<port>\d+))?/;
|
||||||
const matches = proxyRegexp.exec(proxy_str);
|
const matches = proxyRegexp.exec(proxy_str);
|
||||||
if (!matches) {
|
if (!matches) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mozillaVpnData && mozillaVpnData.mozProxyEnabled === undefined) {
|
if (mozillaVpnData && mozillaVpnData.mozProxyEnabled === undefined) {
|
||||||
matches.groups.type = null;
|
matches.groups.type = "direct";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mozillaVpnData) {
|
if (!mozillaVpnData) {
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
|
|
||||||
const DEFAULT_FAVICON = "/img/blank-favicon.svg";
|
const DEFAULT_FAVICON = "/img/blank-favicon.svg";
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
const CONTAINER_ORDER_STORAGE_KEY = "container-order";
|
|
||||||
|
|
||||||
// TODO use export here instead of globals
|
// TODO use export here instead of globals
|
||||||
const Utils = {
|
const Utils = {
|
||||||
|
|
||||||
|
@ -94,9 +91,6 @@ const Utils = {
|
||||||
return result.join("");
|
return result.join("");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Promise<Tab|false>}
|
|
||||||
*/
|
|
||||||
async currentTab() {
|
async currentTab() {
|
||||||
const activeTabs = await browser.tabs.query({ active: true, windowId: browser.windows.WINDOW_ID_CURRENT });
|
const activeTabs = await browser.tabs.query({ active: true, windowId: browser.windows.WINDOW_ID_CURRENT });
|
||||||
if (activeTabs.length > 0) {
|
if (activeTabs.length > 0) {
|
||||||
|
@ -141,32 +135,14 @@ const Utils = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
resetCookiesForSite(pageUrl, cookieStoreId) {
|
async reloadInContainer(url, currentUserContextId, newUserContextId, tabIndex, active) {
|
||||||
return browser.runtime.sendMessage({
|
|
||||||
method: "resetCookiesForSite",
|
|
||||||
pageUrl,
|
|
||||||
cookieStoreId,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} url
|
|
||||||
* @param {string} currentUserContextId
|
|
||||||
* @param {string} newUserContextId
|
|
||||||
* @param {number} tabIndex
|
|
||||||
* @param {boolean} active
|
|
||||||
* @param {number} [groupId]
|
|
||||||
* @returns {Promise<any>}
|
|
||||||
*/
|
|
||||||
async reloadInContainer(url, currentUserContextId, newUserContextId, tabIndex, active, groupId = undefined) {
|
|
||||||
return await browser.runtime.sendMessage({
|
return await browser.runtime.sendMessage({
|
||||||
method: "reloadInContainer",
|
method: "reloadInContainer",
|
||||||
url,
|
url,
|
||||||
currentUserContextId,
|
currentUserContextId,
|
||||||
newUserContextId,
|
newUserContextId,
|
||||||
tabIndex,
|
tabIndex,
|
||||||
active,
|
active
|
||||||
groupId
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -180,8 +156,7 @@ const Utils = {
|
||||||
currentUserContextId: false,
|
currentUserContextId: false,
|
||||||
newUserContextId: assignedUserContextId,
|
newUserContextId: assignedUserContextId,
|
||||||
tabIndex: currentTab.index +1,
|
tabIndex: currentTab.index +1,
|
||||||
active: currentTab.active,
|
active:currentTab.active
|
||||||
groupId: currentTab.groupId
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await Utils.setOrRemoveAssignment(
|
await Utils.setOrRemoveAssignment(
|
||||||
|
@ -191,26 +166,6 @@ const Utils = {
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
/* Theme helper
|
|
||||||
*
|
|
||||||
* First, we look if there's a theme already set in the local storage. If
|
|
||||||
* there isn't one, we set the theme based on `prefers-color-scheme`.
|
|
||||||
* */
|
|
||||||
getTheme(currentTheme, window) {
|
|
||||||
if (typeof currentTheme !== "undefined" && currentTheme !== "auto") {
|
|
||||||
return currentTheme;
|
|
||||||
}
|
|
||||||
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
|
||||||
return "dark";
|
|
||||||
}
|
|
||||||
return "light";
|
|
||||||
},
|
|
||||||
async applyTheme() {
|
|
||||||
const { currentTheme } = await browser.storage.local.get("currentTheme");
|
|
||||||
const popup = document.getElementsByTagName("html")[0];
|
|
||||||
const theme = Utils.getTheme(currentTheme, window);
|
|
||||||
popup.setAttribute("data-theme", theme);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
window.Utils = Utils;
|
window.Utils = Utils;
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
{
|
{
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"name": "Firefox Multi-Account Containers",
|
"name": "Firefox Multi-Account Containers",
|
||||||
"version": "8.3.0",
|
"version": "8.0.0",
|
||||||
"incognito": "not_allowed",
|
"incognito": "not_allowed",
|
||||||
"description": "__MSG_extensionDescription__",
|
"description": "__MSG_extensionDescription__",
|
||||||
"icons": {
|
"icons": {
|
||||||
"48": "img/multiaccountcontainer-16.svg",
|
"48": "img/container-site-d-48.png",
|
||||||
"96": "img/multiaccountcontainer-16.svg"
|
"96": "img/container-site-d-96.png"
|
||||||
|
},
|
||||||
|
"applications": {
|
||||||
|
"gecko": {
|
||||||
|
"id": "@testpilot-containers",
|
||||||
|
"strict_min_version": "67.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"homepage_url": "https://github.com/mozilla/multi-account-containers#readme",
|
"homepage_url": "https://github.com/mozilla/multi-account-containers#readme",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
|
@ -18,22 +24,20 @@
|
||||||
"history",
|
"history",
|
||||||
"idle",
|
"idle",
|
||||||
"management",
|
"management",
|
||||||
|
"nativeMessaging",
|
||||||
"storage",
|
"storage",
|
||||||
"unlimitedStorage",
|
"unlimitedStorage",
|
||||||
"tabs",
|
"tabs",
|
||||||
"webRequestBlocking",
|
"webRequestBlocking",
|
||||||
"webRequest"
|
"webRequest",
|
||||||
|
"proxy"
|
||||||
],
|
],
|
||||||
"optional_permissions": [
|
"optional_permissions": [
|
||||||
"bookmarks",
|
"bookmarks"
|
||||||
"browsingData",
|
|
||||||
"nativeMessaging",
|
|
||||||
"proxy"
|
|
||||||
],
|
],
|
||||||
"browser_specific_settings": {
|
"browser_specific_settings": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
"id": "@testpilot-containers",
|
"id": "@testpilot-containers"
|
||||||
"strict_min_version": "91.1.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"commands": {
|
"commands": {
|
||||||
|
@ -42,81 +46,77 @@
|
||||||
"default": "Ctrl+Period",
|
"default": "Ctrl+Period",
|
||||||
"mac": "MacCtrl+Period"
|
"mac": "MacCtrl+Period"
|
||||||
},
|
},
|
||||||
"description": "__MSG_openContainerPanel__"
|
"description": "Open containers panel"
|
||||||
},
|
|
||||||
"sort_tabs": {
|
|
||||||
"description": "__MSG_sortTabsByContainer__"
|
|
||||||
},
|
},
|
||||||
"open_container_0": {
|
"open_container_0": {
|
||||||
"suggested_key": {
|
"suggested_key": {
|
||||||
"default": "Ctrl+Shift+1"
|
"default": "Ctrl+Shift+1"
|
||||||
},
|
},
|
||||||
"description": "__MSG_containerShortcut__"
|
"description": "Container Shortcut 1"
|
||||||
},
|
},
|
||||||
"open_container_1": {
|
"open_container_1": {
|
||||||
"suggested_key": {
|
"suggested_key": {
|
||||||
"default": "Ctrl+Shift+2"
|
"default": "Ctrl+Shift+2"
|
||||||
},
|
},
|
||||||
"description": "__MSG_containerShortcut__"
|
"description": "Container Shortcut 2"
|
||||||
},
|
},
|
||||||
"open_container_2": {
|
"open_container_2": {
|
||||||
"suggested_key": {
|
"suggested_key": {
|
||||||
"default": "Ctrl+Shift+3"
|
"default": "Ctrl+Shift+3"
|
||||||
},
|
},
|
||||||
"description": "__MSG_containerShortcut__"
|
"description": "Container Shortcut 3"
|
||||||
},
|
},
|
||||||
"open_container_3": {
|
"open_container_3": {
|
||||||
"suggested_key": {
|
"suggested_key": {
|
||||||
"default": "Ctrl+Shift+4"
|
"default": "Ctrl+Shift+4"
|
||||||
},
|
},
|
||||||
"description": "__MSG_containerShortcut__"
|
"description": "Container Shortcut 4"
|
||||||
},
|
},
|
||||||
"open_container_4": {
|
"open_container_4": {
|
||||||
"suggested_key": {
|
"suggested_key": {
|
||||||
"default": "Ctrl+Shift+5"
|
"default": "Ctrl+Shift+5"
|
||||||
},
|
},
|
||||||
"description": "__MSG_containerShortcut__"
|
"description": "Container Shortcut 5"
|
||||||
},
|
},
|
||||||
"open_container_5": {
|
"open_container_5": {
|
||||||
"suggested_key": {
|
"suggested_key": {
|
||||||
"default": "Ctrl+Shift+6"
|
"default": "Ctrl+Shift+6"
|
||||||
},
|
},
|
||||||
"description": "__MSG_containerShortcut__"
|
"description": "Container Shortcut 6"
|
||||||
},
|
},
|
||||||
"open_container_6": {
|
"open_container_6": {
|
||||||
"suggested_key": {
|
"suggested_key": {
|
||||||
"default": "Ctrl+Shift+7"
|
"default": "Ctrl+Shift+7"
|
||||||
},
|
},
|
||||||
"description": "__MSG_containerShortcut__"
|
"description": "Container Shortcut 7"
|
||||||
},
|
},
|
||||||
"open_container_7": {
|
"open_container_7": {
|
||||||
"suggested_key": {
|
"suggested_key": {
|
||||||
"default": "Ctrl+Shift+8"
|
"default": "Ctrl+Shift+8"
|
||||||
},
|
},
|
||||||
"description": "__MSG_containerShortcut__"
|
"description": "Container Shortcut 8"
|
||||||
},
|
},
|
||||||
"open_container_8": {
|
"open_container_8": {
|
||||||
"suggested_key": {
|
"suggested_key": {
|
||||||
"default": "Ctrl+Shift+9"
|
"default": "Ctrl+Shift+9"
|
||||||
},
|
},
|
||||||
"description": "__MSG_containerShortcut__"
|
"description": "Container Shortcut 9"
|
||||||
},
|
},
|
||||||
"open_container_9": {
|
"open_container_9": {
|
||||||
"suggested_key": {
|
"suggested_key": {
|
||||||
"default": "Ctrl+Shift+0"
|
"default": "Ctrl+Shift+0"
|
||||||
},
|
},
|
||||||
"description": "__MSG_containerShortcut__"
|
"description": "Container Shortcut 10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"browser_action": {
|
"browser_action": {
|
||||||
"browser_style": true,
|
"browser_style": true,
|
||||||
"default_icon": "img/multiaccountcontainer-16.svg",
|
"default_icon": "img/multiaccountcontainer-16.svg",
|
||||||
"default_title": "Firefox Multi-Account Containers",
|
"default_title": "Multi-Account Containers",
|
||||||
"default_popup": "popup.html",
|
"default_popup": "popup.html",
|
||||||
"default_area": "navbar",
|
|
||||||
"theme_icons": [
|
"theme_icons": [
|
||||||
{
|
{
|
||||||
"light": "img/multiaccountcontainer-16.svg",
|
"light": "img/multiaccountcontainer-16-dark.svg",
|
||||||
"dark": "img/multiaccountcontainer-16.svg",
|
"dark": "img/multiaccountcontainer-16.svg",
|
||||||
"size": 32
|
"size": 32
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@
|
||||||
"page_action": {
|
"page_action": {
|
||||||
"browser_style": true,
|
"browser_style": true,
|
||||||
"default_icon": "img/container-openin-16.svg",
|
"default_icon": "img/container-openin-16.svg",
|
||||||
"default_title": "__MSG_alwaysOpenSiteInContainer__",
|
"default_title": "Always open this in a Container",
|
||||||
"default_popup": "pageActionPopup.html",
|
"default_popup": "pageActionPopup.html",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"show_matches": ["*://*/*"]
|
"show_matches": ["*://*/*"]
|
||||||
|
@ -149,7 +149,7 @@
|
||||||
],
|
],
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"web_accessible_resources": [
|
"web_accessible_resources": [
|
||||||
"/img/multiaccountcontainer-16.svg"
|
"/img/container-site-d-24.png"
|
||||||
],
|
],
|
||||||
"options_ui": {
|
"options_ui": {
|
||||||
"page": "options.html",
|
"page": "options.html",
|
||||||
|
|
106
src/options.html
|
@ -4,137 +4,85 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<script type="text/javascript" src="./js/i18n.js"></script>
|
<script type="text/javascript" src="./js/i18n.js"></script>
|
||||||
<script type="text/javascript" src="./js/mozillaVpn.js"></script>
|
|
||||||
<script type="text/javascript" src="./js/proxified-containers.js"></script>
|
|
||||||
<link rel="stylesheet" href="css/options.css">
|
<link rel="stylesheet" href="css/options.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<form>
|
<form>
|
||||||
<h3 data-i18n-message-id="optionalPermissions"></h3>
|
<h3 data-i18n-message-id="optionalPermissions"></h3>
|
||||||
<div class="settings-group">
|
<label >
|
||||||
<label class="permission">
|
<input type="checkbox" id="bookmarksPermissions">
|
||||||
<input type="checkbox" data-permission-id="bookmarks" id="bookmarksPermissions">
|
<span data-i18n-message-id="enableBookMarkMenus"></span>
|
||||||
<span class="bold" data-i18n-message-id="enableBookMarkMenus"></span>
|
|
||||||
</label>
|
|
||||||
<p><em data-i18n-message-id="enableBookMarkMenusDescription"></em></p>
|
|
||||||
</div>
|
|
||||||
<div id="moz-vpn-proxy-permissions" class="moz-vpn-proxy-permissions">
|
|
||||||
<h3 class="moz-vpn-proxy-permissions-title">
|
|
||||||
<span data-i18n-message-id="mozillaVpnAndProxyPermissionsTitle" class="warning-icon"></span>
|
|
||||||
</h3>
|
|
||||||
<div class="moz-vpn-proxy-permissions-content">
|
|
||||||
<div class="settings-group">
|
|
||||||
<label class="permission">
|
|
||||||
<input type="checkbox" data-permission-id="nativeMessaging">
|
|
||||||
<span class="bold" data-i18n-message-id="nativeMessagingPermissionTitle"></span>
|
|
||||||
</label>
|
|
||||||
<p><em data-i18n-message-id="nativeMessagingPermissionDescription"></em></p>
|
|
||||||
</div>
|
|
||||||
<div class="settings-group">
|
|
||||||
<label class="permission">
|
|
||||||
<input type="checkbox" data-permission-id="proxy">
|
|
||||||
<span class="bold" data-i18n-message-id="proxyPermissionTitle"></span>
|
|
||||||
</label>
|
|
||||||
<p><em data-i18n-message-id="proxyPermissionDescription"></em></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<h3 data-i18n-message-id="sync"></h3>
|
|
||||||
<div class="settings-group">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" id="syncCheck">
|
|
||||||
<span class="bold" data-i18n-message-id="enableSync"></span>
|
|
||||||
</label>
|
|
||||||
<p><em data-i18n-message-id="enableSyncDescription"></em></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
</label>
|
||||||
|
<p><em data-i18n-message-id="enableBookMarkMenusDescription"></em></p>
|
||||||
|
<h3 data-i18n-message-id="firefoxAccountsSync"></h3>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="syncCheck">
|
||||||
|
<span data-i18n-message-id="enableSync"></span>
|
||||||
|
</label>
|
||||||
|
<p><em data-i18n-message-id="enableSyncDescription"></em></p>
|
||||||
<h3 data-i18n-message-id="tabBehavior"></h3>
|
<h3 data-i18n-message-id="tabBehavior"></h3>
|
||||||
|
<label>
|
||||||
<div class="settings-group">
|
<input type="checkbox" id="replaceTabCheck">
|
||||||
<label>
|
<span data-i18n-message-id="replaceTab"></span>
|
||||||
<input type="checkbox" id="replaceTabCheck">
|
</label>
|
||||||
<span class="bold" data-i18n-message-id="replaceTab"></span>
|
<p><em data-i18n-message-id="replaceTabDescription"></em></p>
|
||||||
</label>
|
|
||||||
<p><em data-i18n-message-id="replaceTabDescription"></em></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
TODO
|
|
||||||
- Add data-i18n
|
|
||||||
-->
|
|
||||||
<h3 data-i18n-message-id="theme"></h3>
|
|
||||||
|
|
||||||
<p><label class="keyboard-shortcut">
|
|
||||||
<span data-i18n-message-id="chooseTheme"></span>
|
|
||||||
<select id="changeTheme" name="changeTheme">
|
|
||||||
<option value="auto" selected data-i18n-message-id="themeAuto">
|
|
||||||
</option>
|
|
||||||
<option value="light" data-i18n-message-id="themeLight">
|
|
||||||
</option>
|
|
||||||
<option value="dark" data-i18n-message-id="themeDark">
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</label></p>
|
|
||||||
|
|
||||||
<h3 data-i18n-message-id="keyboardShortCuts"></h3>
|
<h3 data-i18n-message-id="keyboardShortCuts"></h3>
|
||||||
<p><em data-i18n-message-id="editWhichContainer"></em></p>
|
<p><em data-i18n-message-id="editWhichContainer"></em></p>
|
||||||
|
<p><label>
|
||||||
<p><label class="keyboard-shortcut">
|
|
||||||
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="1"></span>
|
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="1"></span>
|
||||||
<select id="open_container_0">
|
<select id="open_container_0">
|
||||||
</select>
|
</select>
|
||||||
</label></p>
|
</label></p>
|
||||||
<p><label class="keyboard-shortcut">
|
<p><label>
|
||||||
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="2"></span>
|
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="2"></span>
|
||||||
<select id="open_container_1">
|
<select id="open_container_1">
|
||||||
</select>
|
</select>
|
||||||
</label></p>
|
</label></p>
|
||||||
<p><label class="keyboard-shortcut">
|
<p><label>
|
||||||
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="3"></span>
|
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="3"></span>
|
||||||
<select id="open_container_2">
|
<select id="open_container_2">
|
||||||
</select>
|
</select>
|
||||||
</label></p>
|
</label></p>
|
||||||
<p><label class="keyboard-shortcut">
|
<p><label>
|
||||||
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="4"></span>
|
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="4"></span>
|
||||||
<select id="open_container_3">
|
<select id="open_container_3">
|
||||||
</select>
|
</select>
|
||||||
</label></p>
|
</label></p>
|
||||||
<p><label class="keyboard-shortcut">
|
<p><label>
|
||||||
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="5"></span>
|
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="5"></span>
|
||||||
<select id="open_container_4">
|
<select id="open_container_4">
|
||||||
</select>
|
</select>
|
||||||
</label></p>
|
</label></p>
|
||||||
<p><label class="keyboard-shortcut">
|
<p><label>
|
||||||
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="6"></span>
|
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="6"></span>
|
||||||
<select id="open_container_5">
|
<select id="open_container_5">
|
||||||
</select>
|
</select>
|
||||||
</label></p>
|
</label></p>
|
||||||
<p><label class="keyboard-shortcut">
|
<p><label>
|
||||||
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="7"></span>
|
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="7"></span>
|
||||||
<select id="open_container_6">
|
<select id="open_container_6">
|
||||||
</select>
|
</select>
|
||||||
</label></p>
|
</label></p>
|
||||||
<p><label class="keyboard-shortcut">
|
<p><label>
|
||||||
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="8"></span>
|
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="8"></span>
|
||||||
<select id="open_container_7">
|
<select id="open_container_7">
|
||||||
</select>
|
</select>
|
||||||
</label></p>
|
</label></p>
|
||||||
<p><label class="keyboard-shortcut">
|
<p><label>
|
||||||
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="9"></span>
|
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="9"></span>
|
||||||
<select id="open_container_8">
|
<select id="open_container_8">
|
||||||
</select>
|
</select>
|
||||||
</label></p>
|
</label></p>
|
||||||
<p><label class="keyboard-shortcut">
|
<p><label>
|
||||||
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="10"></span>
|
<span data-i18n-message-id="keyboardShortCut" data-i18n-placeholder="10"></span>
|
||||||
<select id="open_container_9">
|
<select id="open_container_9">
|
||||||
</select>
|
</select>
|
||||||
</label></p>
|
</label></p>
|
||||||
<h3 data-i18n-message-id="onboarding"></h3>
|
<h3 data-i18n-message-id="onboarding"></h3>
|
||||||
<button data-btn-id="reset-onboarding" data-i18n-message-id="resetOnboardingPanels"></button>
|
<button data-i18n-message-id="resetOnboardingPanels"></button>
|
||||||
<p><em data-i18n-message-id="onboardingToggle"></em></p>
|
<p><em data-i18n-message-id="onboardingToggle"></em></p>
|
||||||
<h3>Mozilla VPN</h3>
|
|
||||||
<button data-btn-id="moz-vpn-learn-more" data-i18n-message-id="learnMore"></button>
|
|
||||||
</form>
|
</form>
|
||||||
<script src="js/options.js"></script>
|
<script src="js/options.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
<title>Firefox Multi-Account Containers</title>
|
<title>Multi-Account Containers</title>
|
||||||
<script type="text/javascript" src="./js/i18n.js"></script>
|
<script type="text/javascript" src="./js/i18n.js"></script>
|
||||||
<link rel="stylesheet" type="text/css" href="css/popup.css">
|
<link rel="stylesheet" type="text/css" href="css/popup.css">
|
||||||
|
|
||||||
|
|
114
src/popup.html
|
@ -1,7 +1,7 @@
|
||||||
<html data-theme="auto">
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
<title>Firefox Multi-Account Containers</title>
|
<title>Multi-Account Containers</title>
|
||||||
<script type="text/javascript" src="./js/i18n.js"></script>
|
<script type="text/javascript" src="./js/i18n.js"></script>
|
||||||
<link rel="stylesheet" href="./css/popup.css">
|
<link rel="stylesheet" href="./css/popup.css">
|
||||||
</head>
|
</head>
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
<div class="panel onboarding onboarding-panel-6 hide" id="onboarding-panel-6">
|
<div class="panel onboarding onboarding-panel-6 hide" id="onboarding-panel-6">
|
||||||
<img class="onboarding-img" alt="" src="/img/Sync.svg" />
|
<img class="onboarding-img" alt="" src="/img/Sync.svg" />
|
||||||
<h3 class="onboarding-title" data-i18n-message-id="onboarding-6-header"></h3>
|
<h3 class="onboarding-title" data-i18n-message-id="onboarding-6-header"></h3>
|
||||||
<p data-i18n-message-id="onboarding-6-description-2"></p>
|
<p data-i18n-message-id="onboarding-6-description"></p>
|
||||||
<div class="half-button-wrapper">
|
<div class="half-button-wrapper">
|
||||||
<a href="#" id="no-sync" class="half-onboarding-button grey-button keyboard-nav" tabindex="0" data-i18n-message-id="notNow"></a>
|
<a href="#" id="no-sync" class="half-onboarding-button grey-button keyboard-nav" tabindex="0" data-i18n-message-id="notNow"></a>
|
||||||
<a href="#" id="start-sync-button" class="half-onboarding-button keyboard-nav" tabindex="0" data-i18n-message-id="startSyncing"></a>
|
<a href="#" id="start-sync-button" class="half-onboarding-button keyboard-nav" tabindex="0" data-i18n-message-id="startSyncing"></a>
|
||||||
|
@ -53,8 +53,8 @@
|
||||||
|
|
||||||
<div class="panel onboarding onboarding-panel-7 hide" id="onboarding-panel-7">
|
<div class="panel onboarding onboarding-panel-7 hide" id="onboarding-panel-7">
|
||||||
<img class="onboarding-img" alt="" src="/img/Account.svg" />
|
<img class="onboarding-img" alt="" src="/img/Account.svg" />
|
||||||
<h3 class="onboarding-title" data-i18n-message-id="onboarding-7-header-2"></h3>
|
<h3 class="onboarding-title" data-i18n-message-id="onboarding-7-header"></h3>
|
||||||
<p data-i18n-message-id="onboarding-7-description-2"></p>
|
<p data-i18n-message-id="onboarding-7-description"></p>
|
||||||
<div class="half-button-wrapper">
|
<div class="half-button-wrapper">
|
||||||
<a href="#" id="no-sign-in" class="half-onboarding-button grey-button keyboard-nav" tabindex="0" data-i18n-message-id="notNow"></a>
|
<a href="#" id="no-sign-in" class="half-onboarding-button grey-button keyboard-nav" tabindex="0" data-i18n-message-id="notNow"></a>
|
||||||
<a href="#" id="sign-in" class="half-onboarding-button keyboard-nav" tabindex="0" data-i18n-message-id="signIn"></a>
|
<a href="#" id="sign-in" class="half-onboarding-button keyboard-nav" tabindex="0" data-i18n-message-id="signIn"></a>
|
||||||
|
@ -62,22 +62,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel onboarding onboarding-panel-8 hide" id="onboarding-panel-8">
|
<div class="panel onboarding onboarding-panel-8 hide" id="onboarding-panel-8">
|
||||||
<div class="moz-vpn-onboarding-content">
|
<img class="onboarding-img" alt="" src="/img/moz-vpn-onboarding.svg" />
|
||||||
<img class="onboarding-img" alt="" src="/img/moz-vpn-onboarding.svg" />
|
<h3 class="onboarding-title" data-i18n-message-id="proxyNowAvailable"></h3>
|
||||||
<h3 class="onboarding-title" data-i18n-message-id="proxyNowAvailable"></h3>
|
<p data-i18n-message-id="onboarding-8-description"></p>
|
||||||
<p data-i18n-message-id="onboarding-8-description"></p>
|
<div class="half-button-wrapper">
|
||||||
</div>
|
<a href="#" id="onboarding-done-btn" class="half-onboarding-button keyboard-nav" tabindex="0" data-i18n-message-id="done"></a>
|
||||||
<div id="moz-vpn-fw-onboarding-done" class="half-button-wrapper">
|
|
||||||
<a id="moz-vpn-fw-onboarding-done" href="#" class="half-onboarding-button keyboard-nav onboarding-done" tabindex="0" data-i18n-message-id="done"></a>
|
|
||||||
</div>
|
|
||||||
<div class="moz-vpn-permissions">
|
|
||||||
<div class="moz-vpn-permissions-copy">
|
|
||||||
<span data-i18n-message-id="mozillaVpnRequiresAdditionalPermissions"></span>
|
|
||||||
</div>
|
|
||||||
<div class="half-button-wrapper">
|
|
||||||
<a href="#" id="permissions-not-now" class="half-onboarding-button grey-button keyboard-nav onboarding-done" tabindex="0" data-i18n-message-id="notNow"></a>
|
|
||||||
<a href="#" id="onboarding-enable-permissions" class="half-onboarding-button keyboard-nav" tabindex="0" data-i18n-message-id="enable"></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -107,10 +96,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel menu-panel container-panel hide" id="container-panel">
|
<div class="panel menu-panel container-panel hide" id="container-panel">
|
||||||
<span class="popup-notification-card"></span>
|
<h3 class="title">Multi-Account Containers</h3>
|
||||||
<h3 class="title">Firefox Multi-Account Containers</h3>
|
|
||||||
<a href="#" class="info-icon" id="info-icon" tabindex="10">
|
<a href="#" class="info-icon" id="info-icon" tabindex="10">
|
||||||
<img data-i18n-attribute-message-id="info" data-i18n-attribute="alt" alt="" src="/img/info.svg" / >
|
<img data-i18n-attribute-message-id="info" data-i18n-attribute="alt" alt="" ="info" src="/img/info.svg" / >
|
||||||
</a>
|
</a>
|
||||||
<hr>
|
<hr>
|
||||||
<table class="menu">
|
<table class="menu">
|
||||||
|
@ -155,22 +143,11 @@
|
||||||
<div class="sub-header" data-i18n-message-id="containers"></div>
|
<div class="sub-header" data-i18n-message-id="containers"></div>
|
||||||
<h4 class="moz-vpn-logotype vpn-status-container-list display-none">Mozilla VPN
|
<h4 class="moz-vpn-logotype vpn-status-container-list display-none">Mozilla VPN
|
||||||
<span class="moz-vpn-connection-status-indicator container-list-status-icon">
|
<span class="moz-vpn-connection-status-indicator container-list-status-icon">
|
||||||
|
<span class="tooltip"></span>
|
||||||
</span>
|
</span>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="scrollable identities-list">
|
<div class="scrollable identities-list">
|
||||||
<div class="searchbar">
|
|
||||||
<label for="search-terms"
|
|
||||||
class="hide-label"
|
|
||||||
data-i18n-message-id="filterInputLabel">
|
|
||||||
</label>
|
|
||||||
<input type="text"
|
|
||||||
id="search-terms"
|
|
||||||
name="search-terms"
|
|
||||||
placeholder="Search container name"
|
|
||||||
data-i18n-attribute="placeholder"
|
|
||||||
data-i18n-attribute-message-id="filterInputPlaceholder">
|
|
||||||
</div>
|
|
||||||
<table class="menu" id="identities-list">
|
<table class="menu" id="identities-list">
|
||||||
<tr class="menu-item hover-highlight">
|
<tr class="menu-item hover-highlight">
|
||||||
<td>
|
<td>
|
||||||
|
@ -192,14 +169,24 @@
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="moz-vpn-tout" class="moz-vpn-content expanded">
|
||||||
<v-padding-hack-footer></v-padding-hack-footer> <!--prevents last container from getting covered up by the 'manage containers button' when list is long-->
|
<div class="flx-row button-wrapper">
|
||||||
|
<h4 class="moz-vpn-logo">Mozilla VPN</h4>
|
||||||
|
<button class="controller dismiss-moz-vpn-tout" tab-index="0"></button>
|
||||||
|
</div>
|
||||||
|
<div class="collapsible-content flx-col controller-collapsible-content">
|
||||||
|
<div class="flx-row flx-space-between">
|
||||||
|
<span class="moz-vpn-subtitle" data-i18n-message-id="protectYourContainers"></span>
|
||||||
|
</div>
|
||||||
|
<button id="moz-vpn-learn-more" class="moz-vpn-cta primary-cta" data-i18n-message-id="learnMore"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<v-padding-hack-footer></v-padding-hack-footer> <!--presents last container from getting covered up by the 'manage containers button' when list is long-->
|
||||||
<div class="bottom-btn keyboard-nav controller" id="manage-containers-link" tabindex="0" data-i18n-message-id="manageContainers"></div>
|
<div class="bottom-btn keyboard-nav controller" id="manage-containers-link" tabindex="0" data-i18n-message-id="manageContainers"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="hide panel menu-panel container-info-panel" id="container-info-panel" tabindex="-1">
|
<div class="hide panel menu-panel container-info-panel" id="container-info-panel" tabindex="-1">
|
||||||
<span class="popup-notification-card"></span>
|
|
||||||
<h3 class="title" id="container-info-title" data-i18n-attribute-message-id="personal"></h3>
|
<h3 class="title" id="container-info-title" data-i18n-attribute-message-id="personal"></h3>
|
||||||
<button class="btn-return arrow-left controller keyboard-nav-back" id="close-container-info-panel" tabindex="0"></button>
|
<button class="btn-return arrow-left controller keyboard-nav-back" id="close-container-info-panel" tabindex="0"></button>
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -236,14 +223,6 @@
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="menu-item hover-highlight keyboard-nav" id="clear-container-storage" tabindex="0">
|
|
||||||
<td>
|
|
||||||
<img class="menu-icon clear-storage-icon" alt="" src="img/container-delete.svg" />
|
|
||||||
<span class="menu-text" id="clear-container-storage-info" data-i18n-message-id="clearContainerStorage"></span>
|
|
||||||
<span class="menu-arrow">
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
</table>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="sub-header-wrapper">
|
<div class="sub-header-wrapper">
|
||||||
|
@ -261,14 +240,13 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<v-padding-hack-footer></v-padding-hack-footer>
|
<v-padding-hack-footer></v-padding-hack-footer>
|
||||||
<div class="bottom-btn keyboard-nav hover-highlight controller" id="manage-container-link" tabindex="0" data-i18n-message-id="manageThisContainer"></div>
|
<div class="bottom-btn keyboard-nav hover-highlight" id="manage-container-link" tabindex="0" data-i18n-message-id="manageThisContainer"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="panel menu-panel container-picker-panel hide" id="container-picker-panel">
|
<div class="panel menu-panel container-picker-panel hide" id="container-picker-panel">
|
||||||
<span class="popup-notification-card"></span>
|
|
||||||
<h3 class="title" id="picker-title">
|
<h3 class="title" id="picker-title">
|
||||||
Firefox Multi-Account Containers
|
Multi-Account Containers
|
||||||
</h3>
|
</h3>
|
||||||
<button class="btn-return arrow-left controller keyboard-nav-back" id="close-container-picker-panel" tabindex="0"></button>
|
<button class="btn-return arrow-left controller keyboard-nav-back" id="close-container-picker-panel" tabindex="0"></button>
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -291,7 +269,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel menu-panel edit-container-panel hide" id="edit-container-panel">
|
<div class="panel menu-panel edit-container-panel hide" id="edit-container-panel">
|
||||||
<span class="popup-notification-card"></span>
|
|
||||||
<h3 class="title" id="container-edit-title" data-i18n-message-id="default"></h3>
|
<h3 class="title" id="container-edit-title" data-i18n-message-id="default"></h3>
|
||||||
<button class="btn-return arrow-left controller" id="close-container-edit-panel"></button>
|
<button class="btn-return arrow-left controller" id="close-container-edit-panel"></button>
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -311,7 +288,7 @@
|
||||||
<legend class="form-header" data-i18n-message-id="icon"></legend>
|
<legend class="form-header" data-i18n-message-id="icon"></legend>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="proxies"> <!---- PROXIES -->
|
<fieldset class="proxies"> <!---- PROXIES -->
|
||||||
<input type="text" class="proxies" name="container-proxy" id="edit-container-panel-proxy" placeholder="type://host:port" hidden/>
|
<input type="text" class="proxies" name="container-proxy" id="edit-container-panel-proxy" maxlength="50" placeholder="type://host:port" hidden/>
|
||||||
<input type="text" class="proxies" name="moz-proxy-enabled" id="moz-proxy-enabled" maxlength="5" hidden/>
|
<input type="text" class="proxies" name="moz-proxy-enabled" id="moz-proxy-enabled" maxlength="5" hidden/>
|
||||||
<input type="text" class="proxies" name="country-code" id="country-code-input" maxlength="5" hidden/>
|
<input type="text" class="proxies" name="country-code" id="country-code-input" maxlength="5" hidden/>
|
||||||
<input type="text" class="proxies" name="city-name" id="city-name-input" maxlength="5" hidden/>
|
<input type="text" class="proxies" name="city-name" id="city-name-input" maxlength="5" hidden/>
|
||||||
|
@ -347,18 +324,17 @@
|
||||||
<span class="slider round"></span>
|
<span class="slider round"></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<button id="get-mozilla-vpn" class="moz-vpn-cta primary-cta" data-i18n-message-id="getMozillaVpn"></button>
|
<button id="get-mozilla-vpn" class="moz-vpn-cta primary-cta" data-i18n-message-id="learnMore"></button>
|
||||||
<button id="moz-vpn-current-server" class="controller">
|
<button id="moz-vpn-current-server" class="controller">
|
||||||
<span class="current-country-flag"></span>
|
<span class="current-country-flag"></span>
|
||||||
<span class="current-city-name"></span>
|
<span class="current-city-name"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</moz-vpn-container-ui>
|
</moz-vpn-container-ui>
|
||||||
<button id="advanced-proxy-settings-btn" class="proxy-section advanced-proxy-settings-btn">
|
<button id="advanced-proxy-settings-btn" class="proxy-section advanced-proxy-settings-btn controller">
|
||||||
<span class="advanced-proxy-settings-btn-label" data-i18n-message-id="advancedProxySettings"></span>
|
<span class="advanced-proxy-settings-btn-label" data-i18n-message-id="advancedProxySettings"></span>
|
||||||
<span id="advanced-proxy-address"></span>
|
<span id="advanced-proxy-address"></span>
|
||||||
</button>
|
</button>
|
||||||
<hr>
|
|
||||||
<button class="delete-container delete-btn alert-text" id="delete-container-button" data-i18n-message-id="deleteThisContainer"></button>
|
<button class="delete-container delete-btn alert-text" id="delete-container-button" data-i18n-message-id="deleteThisContainer"></button>
|
||||||
|
|
||||||
<!-- TODO get UX / CONTENT on how to message about unavailable proxies -->
|
<!-- TODO get UX / CONTENT on how to message about unavailable proxies -->
|
||||||
|
@ -382,7 +358,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel menu-panel edit-container-assignments hide" id="edit-container-assignments">
|
<div class="panel menu-panel edit-container-assignments hide" id="edit-container-assignments">
|
||||||
<span class="popup-notification-card"></span>
|
|
||||||
<h3 class="title" id="edit-assignments-title" data-i18n-message-id="default"></h3>
|
<h3 class="title" id="edit-assignments-title" data-i18n-message-id="default"></h3>
|
||||||
<button class="btn-return arrow-left controller" id="close-container-assignment-panel"></button>
|
<button class="btn-return arrow-left controller" id="close-container-assignment-panel"></button>
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -408,27 +383,12 @@
|
||||||
<hr>
|
<hr>
|
||||||
<div class="panel-content delete-container-confirm">
|
<div class="panel-content delete-container-confirm">
|
||||||
<h4 class="delete-container-confirm-title" data-i18n-message-id="removeThisContainer"></h4>
|
<h4 class="delete-container-confirm-title" data-i18n-message-id="removeThisContainer"></h4>
|
||||||
|
<p class="delete-warning" id="delete-container-tab-warning"></p>
|
||||||
<p class="delete-warning" data-i18n-message-id="removeThisContainerConfirmation"></p>
|
<p class="delete-warning" data-i18n-message-id="removeThisContainerConfirmation"></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-footer">
|
<div class="panel-footer">
|
||||||
<a href="#" class="button expanded secondary footer-button cancel-button" data-i18n-message-id="cancel" id="delete-container-cancel-link"></a>
|
<a href="#" class="button expanded secondary footer-button cancel-button" data-i18n-message-id="cancel" id="delete-container-cancel-link"></a>
|
||||||
<a href="#" class="button expanded confirmation-destructive-ok-btn footer-button alert-text" data-i18n-message-id="ok" id="delete-container-ok-link"></a>
|
<a href="#" class="button expanded primary footer-button" data-i18n-message-id="ok" id="delete-container-ok-link"></a>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="hide panel clear-container-storage-panel" id="clear-container-storage-panel">
|
|
||||||
<h3 class="title" id="container-clear-storage-title" data-i18n-message-id="default">
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<button class="btn-return arrow-left controller" id="close-clear-container-storage-panel"></button>
|
|
||||||
<hr>
|
|
||||||
<div class="panel-content clear-container-storage-confirm">
|
|
||||||
<h4 class="clear-container-storage-confirm-title" data-i18n-message-id="clearContainerStoragePanelTitle"></h4>
|
|
||||||
<p class="clear-container-storage-warning" data-i18n-message-id="clearContainerStorageConfirmation"></p>
|
|
||||||
</div>
|
|
||||||
<div class="panel-footer">
|
|
||||||
<a href="#" class="button expanded secondary footer-button cancel-button" data-i18n-message-id="cancel" id="clear-container-storage-cancel-link"></a>
|
|
||||||
<a href="#" class="button expanded confirmation-destructive-ok-btn footer-button alert-text" data-i18n-message-id="ok" id="clear-container-storage-ok-link"></a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -448,7 +408,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template id="server-city-list-items">
|
<template id="server-city-list-items">
|
||||||
<li>
|
<li>
|
||||||
<label class="server-city-list-item" tabindex="0">
|
<label class="server-city-list-item">
|
||||||
<input class="server-radio-btn" type="radio" data-country-code="" data-city-name="" checked=""/>
|
<input class="server-radio-btn" type="radio" data-country-code="" data-city-name="" checked=""/>
|
||||||
<div class="server-radio-control"></div>
|
<div class="server-radio-control"></div>
|
||||||
<span class="server-city-name"></span>
|
<span class="server-city-name"></span>
|
||||||
|
@ -470,17 +430,13 @@
|
||||||
<form class="advanced-proxy-panel-content">
|
<form class="advanced-proxy-panel-content">
|
||||||
<label class="advanced-proxy-input-label" for="container-proxy" data-i18n-message-id="advancedProxySettings"></label>
|
<label class="advanced-proxy-input-label" for="container-proxy" data-i18n-message-id="advancedProxySettings"></label>
|
||||||
<div class="advanced-proxy-input-wrapper">
|
<div class="advanced-proxy-input-wrapper">
|
||||||
<input id="edit-advanced-proxy-input" class="proxy-host primary-input" name="container-proxy" type="text" placeholder="type://host:port" />
|
<input id="edit-advanced-proxy-input" class="proxy-host primary-input" name="container-proxy" type="text" maxlength="50" placeholder="type://host:port" />
|
||||||
<button id="clear-advanced-proxy-input" class="controller" data-i18n-attribute="value" data-i18n-attribute-message-id="clearproxylabel"></button>
|
<button id="clear-advanced-proxy-input" class="controller" data-i18n-attribute="value" data-i18n-attribute-message-id="clearproxylabel"></button>
|
||||||
<span class="proxy-validity" data-i18n-message-id="invalidProxyAlert"></span>
|
<span class="proxy-validity" data-i18n-message-id="invalidProxyAlert"></span>
|
||||||
</div>
|
</div>
|
||||||
<button id="submit-advanced-proxy" class="primary-cta apply-to-container" data-i18n-message-id="applyToContainer"></button>
|
<button id="submit-advanced-proxy" class="primary-cta apply-to-container" data-i18n-message-id="applyToContainer"></button>
|
||||||
<a id="advanced-proxy-settings-learn-more" href="" class="blue-link" data-i18n-message-id="learnMore"></a>
|
<a id="advanced-proxy-settings-learn-more" href="" class="blue-link" data-i18n-message-id="learnMore"></a>
|
||||||
</form>
|
</form>
|
||||||
<div id="permissions-overlay" class="permissions-overlay" data-tab-group="proxy-disabled">
|
|
||||||
<p data-tab-group="proxy-disabled" data-i18n-message-id="additionalPermissionNeeded"></p>
|
|
||||||
<button id="enable-proxy-permissions" class="primary-cta" data-tab-group="proxy-disabled" data-i18n-message-id="enable"></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<script src="js/utils.js"></script>
|
<script src="js/utils.js"></script>
|
||||||
<script src="js/popup.js"></script>
|
<script src="js/popup.js"></script>
|
||||||
|
|
|
@ -32,10 +32,6 @@ const buildDom = async ({background = {}, popup = {}}) => {
|
||||||
window.crypto = {
|
window.crypto = {
|
||||||
getRandomValues: arr => crypto.randomBytes(arr.length),
|
getRandomValues: arr => crypto.randomBytes(arr.length),
|
||||||
};
|
};
|
||||||
// By default, the mock contextMenus.remove() returns undefined;
|
|
||||||
// Let it return a Promise instead, so that .then() calls chained to
|
|
||||||
// it (in src/js/background/assignManager.js) do not fail.
|
|
||||||
window.browser.contextMenus.remove.resolves();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
const { sinon, nextTick, buildBackgroundDom } = require("../common");
|
|
||||||
|
|
||||||
describe("#1140", () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
this.background = await buildBackgroundDom();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("removing containers", () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
this.background.browser.contextualIdentities.onRemoved.addListener = sinon.stub();
|
|
||||||
const [promise] = this.background.browser.runtime.onMessage.addListener.yield({
|
|
||||||
method: "deleteContainer",
|
|
||||||
message: {
|
|
||||||
userContextId: "1"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await promise;
|
|
||||||
await nextTick();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should remove the identitystate from storage as well", async () => {
|
|
||||||
this.background.browser.storage.local.remove.should.have.been.calledWith([
|
|
||||||
"identitiesState@@_firefox-container-1"
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|