├── .babelrc
├── .circleci
└── config.yml
├── .dir-locals.el
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .github
└── ISSUE_TEMPLATE
│ └── config.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── bin
├── deploy-addon.sh
└── install-test-dependencies.sh
├── docs
├── CODEOWNERS
├── README.md
├── SECURITY.md
├── code_of_conduct.md
├── contributing.md
├── css
│ └── extra.css
├── developer
│ ├── directory-structure.md
│ ├── install.md
│ ├── releases.md
│ ├── test-plan-accessibility.md
│ ├── test-plan-telemetry.md
│ └── test-plan.md
├── index.md
├── metrics.md
├── pull_request_template.md
├── release-notes.md
└── user-guide.md
├── karma.conf.js
├── l10n.toml
├── mkdocs.yml
├── package-lock.json
├── package.json
├── src
├── .dir-locals.el
├── background
│ ├── README.md
│ ├── browser-action.js
│ ├── clipboard.js
│ ├── datastore.js
│ ├── environment.js
│ ├── index.js
│ ├── message-ports.js
│ ├── profile.js
│ ├── telemetry.js
│ └── views.js
├── common.js
├── experiments
│ ├── logins
│ │ ├── api.js
│ │ └── schema.json
│ ├── sync
│ │ ├── api.js
│ │ └── schema.json
│ └── temptelemetry
│ │ ├── api.js
│ │ └── schema.json
├── fonts
│ └── fira-mono-regular.woff
├── icons
│ ├── add-icon.svg
│ ├── arrow-dropdown.svg
│ ├── arrowhead-left-16.svg
│ ├── chevron-right.svg
│ ├── clear.svg
│ ├── close.svg
│ ├── copy.svg
│ ├── delete.svg
│ ├── edit.svg
│ ├── external-link.svg
│ ├── hide.svg
│ ├── icon-account.svg
│ ├── icon-desktop.svg
│ ├── icon-download.svg
│ ├── icon-faq.svg
│ ├── icon-feedback.svg
│ ├── icon-lockwise.svg
│ ├── icon-mobile.svg
│ ├── icon-sync.svg
│ ├── icon-toolbar.svg
│ ├── info-dark.svg
│ ├── info.svg
│ ├── localized
│ │ ├── LICENSE
│ │ └── en
│ │ │ ├── icon-app-store.svg
│ │ │ └── icon-play-store.png
│ ├── search-focused.svg
│ ├── search.svg
│ ├── show.svg
│ ├── warning-light.svg
│ └── warning.svg
├── images
│ ├── intro-step-1.png
│ ├── intro-step-2.png
│ ├── intro-step-3.png
│ ├── logged-out-avatar.png
│ ├── logo-lockwise.svg
│ └── nessie_v2.svg
├── l10n.js
├── list
│ ├── README.md
│ ├── actions.js
│ ├── common.js
│ ├── components
│ │ ├── duplicate-notification.js
│ │ ├── error-notification.js
│ │ ├── item-fields.css
│ │ ├── item-fields.js
│ │ ├── item-list.css
│ │ ├── item-list.js
│ │ ├── item-summary.css
│ │ ├── item-summary.js
│ │ ├── notification.css
│ │ └── sync-notification.js
│ ├── containers
│ │ ├── connected-sync-notification.js
│ │ ├── item-filter.js
│ │ └── no-matching-placeholder.js
│ ├── filter.js
│ ├── manage
│ │ ├── components
│ │ │ ├── app.css
│ │ │ ├── app.js
│ │ │ ├── edit-item-details.js
│ │ │ ├── homepage.css
│ │ │ ├── homepage.js
│ │ │ ├── intro-page.css
│ │ │ ├── intro-page.js
│ │ │ ├── item-details.css
│ │ │ ├── item-details.js
│ │ │ ├── item-list-panel.css
│ │ │ ├── item-list-panel.js
│ │ │ ├── list-counter.js
│ │ │ ├── list-sort.css
│ │ │ ├── list-sort.js
│ │ │ ├── promote-banner.css
│ │ │ └── promote-banner.js
│ │ ├── containers
│ │ │ ├── add-item.css
│ │ │ ├── add-item.js
│ │ │ ├── all-items.js
│ │ │ ├── app-header.css
│ │ │ ├── app-header.js
│ │ │ ├── app-panes.js
│ │ │ ├── connected-promote-banner.js
│ │ │ ├── current-selection.js
│ │ │ ├── list-sort.js
│ │ │ ├── modals.css
│ │ │ └── modals.js
│ │ ├── index.js
│ │ ├── reducers.js
│ │ ├── sort-middleware.js
│ │ └── telemetry.js
│ ├── message-ports.js
│ ├── open-link-middleware.js
│ ├── popup
│ │ ├── components
│ │ │ ├── app.css
│ │ │ ├── app.js
│ │ │ ├── item-details-panel.css
│ │ │ ├── item-details-panel.js
│ │ │ ├── item-list-panel.css
│ │ │ └── item-list-panel.js
│ │ ├── containers
│ │ │ ├── all-items-panel.js
│ │ │ ├── current-selection.js
│ │ │ └── no-entries-placeholder.js
│ │ ├── index.js
│ │ ├── reducers.js
│ │ └── telemetry.js
│ ├── reducers.js
│ ├── sort.js
│ └── telemetry.js
├── locales
│ ├── README.md
│ ├── cak
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── cs
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── cy
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── de
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── el
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── en-CA
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── en-US
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── fr
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── fy-NL
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── hu
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── ia
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── id
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── it
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── kab
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── locales.json
│ ├── nl
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── nn-NO
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── pt-BR
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── ro
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── sl
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── sv-SE
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── vi
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ ├── zh-CN
│ │ ├── list.ftl
│ │ └── widgets.ftl
│ └── zh-TW
│ │ ├── list.ftl
│ │ └── widgets.ftl
├── manifest.json.tpl
├── telemetry.js
├── template.ejs
└── widgets
│ ├── README.md
│ ├── banner.css
│ ├── banner.js
│ ├── breadcrumbs.css
│ ├── breadcrumbs.js
│ ├── button.css
│ ├── button.js
│ ├── copy-to-clipboard-button.css
│ ├── copy-to-clipboard-button.js
│ ├── dialog-box.css
│ ├── dialog-box.js
│ ├── field-text.js
│ ├── filter-input.js
│ ├── input.css
│ ├── input.js
│ ├── label-text.js
│ ├── link.css
│ ├── link.js
│ ├── modal-root.css
│ ├── modal-root.js
│ ├── panel.css
│ ├── panel.js
│ ├── password-input.js
│ ├── password-text.js
│ ├── scrolling-list.css
│ ├── scrolling-list.js
│ ├── stack.css
│ ├── stack.js
│ ├── text-area.css
│ ├── text-area.js
│ ├── toolbar.css
│ └── toolbar.js
├── stylelint.config.js
├── test
├── .dir-locals.el
├── .eslintrc.js
├── integration
│ ├── a11y-test.js
│ ├── chai-axe.js
│ ├── driver.js
│ ├── functional-test.js
│ ├── helper.js
│ ├── logins-api-test.js
│ ├── sync-api-test.js
│ └── test-pages
│ │ ├── logins-api.html
│ │ ├── logins-api.js
│ │ ├── sync-api.html
│ │ └── sync-api.js
└── unit
│ ├── background
│ ├── browser-action-test.js
│ ├── clipboard-test.js
│ ├── datastore-test.js
│ ├── environment-test.js
│ ├── message-ports-test.js
│ ├── profile-test.js
│ ├── telemetry-test.js
│ └── views-test.js
│ ├── chai-focus.js
│ ├── common-test.js
│ ├── common.js
│ ├── constants.js
│ ├── enzyme.js
│ ├── l10n-test.js
│ ├── list
│ ├── actions-test.js
│ ├── components
│ │ ├── item-list-test.js
│ │ ├── item-summary-test.js
│ │ └── sync-notification-test.js
│ ├── containers
│ │ ├── item-filter-test.js
│ │ └── no-matching-placeholder-test.js
│ ├── filter-test.js
│ ├── manage
│ │ ├── components
│ │ │ ├── app-test.js
│ │ │ ├── edit-item-details-test.js
│ │ │ ├── homepage-test.js
│ │ │ ├── intro-page-test.js
│ │ │ ├── item-details-test.js
│ │ │ ├── item-list-panel-test.js
│ │ │ ├── list-counter-test.js
│ │ │ ├── list-sort-test.js
│ │ │ └── promote-banner-test.js
│ │ ├── containers
│ │ │ ├── add-item-test.js
│ │ │ ├── all-items-test.js
│ │ │ ├── app-header-test.js
│ │ │ ├── connected-promote-banner-test.js
│ │ │ ├── current-selection-test.js
│ │ │ ├── list-sort-test.js
│ │ │ └── modals-test.js
│ │ ├── mock-redux-state.js
│ │ ├── reducers-test.js
│ │ └── telemetry-test.js
│ ├── message-ports-test.js
│ ├── open-link-middleware-test.js
│ ├── popup
│ │ ├── components
│ │ │ ├── app-test.js
│ │ │ ├── item-details-panel-test.js
│ │ │ └── item-list-panel-test.js
│ │ ├── containers
│ │ │ ├── all-items-panel-test.js
│ │ │ ├── current-selection-test.js
│ │ │ └── no-entries-placeholder-test.js
│ │ ├── mock-redux-state.js
│ │ └── telemetry-test.js
│ ├── reducers-test.js
│ ├── sort-test.js
│ └── telemetry-test.js
│ ├── mocks
│ ├── browser.js
│ └── l10n.js
│ ├── telemetry-test.js
│ └── widgets
│ ├── banner-test.js
│ ├── breadcrumbs-test.js
│ ├── button-test.js
│ ├── copy-to-clipboard-button-test.js
│ ├── dialog-box-test.js
│ ├── field-text-test.js
│ ├── filter-input-test.js
│ ├── input-test.js
│ ├── label-text-test.js
│ ├── link-test.js
│ ├── panel-test.js
│ ├── password-input-test.js
│ ├── scrolling-list-test.js
│ ├── stack-test.js
│ ├── text-area-test.js
│ └── toolbar-test.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-react",
4 | [
5 | "@babel/preset-env",
6 | {
7 | "targets": {
8 | "firefox": "63",
9 | "node": "10",
10 | }
11 | }
12 | ]
13 | ],
14 | "plugins": [
15 | "@babel/plugin-syntax-object-rest-spread",
16 | "@babel/plugin-syntax-async-generators"
17 | ],
18 | "env": {
19 | "test": {
20 | "plugins": [
21 | "istanbul",
22 | "rewire"
23 | ]
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/.dir-locals.el:
--------------------------------------------------------------------------------
1 | ; Use rjsx-mode instead of js2-mode for files in this directory.
2 | ((js2-mode . ((mode . rjsx))))
3 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | root = true
6 |
7 | [*]
8 | charset = utf-8
9 | end_of_line = lf
10 | indent_style = space
11 | insert_final_newline = true
12 |
13 | [.{babelrc,editorconfig,eslintrc.js}]
14 | indent_size = 2
15 |
16 | [**.{js,json,yml}]
17 | indent_size = 2
18 |
19 | [docs/**.md]
20 | indent_size = 2
21 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | !.eslintrc.js
2 | coverage
3 | dist
4 | node_modules
5 | site
6 | temp
7 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 | */
6 |
7 | let extraPlugins = [];
8 | if (process.env.STRICT_LINT !== "1") {
9 | extraPlugins.push("only-warn");
10 | }
11 |
12 | module.exports = {
13 | "parserOptions": {
14 | "ecmaVersion": 2018,
15 | "sourceType": "module",
16 | "ecmaFeatures": {
17 | "jsx": true,
18 | },
19 | },
20 | "extends": [
21 | "eslint:recommended",
22 | "plugin:mozilla/recommended",
23 | "plugin:react/recommended",
24 | ],
25 | "env": {
26 | "browser": true,
27 | "node": true,
28 | "webextensions": true,
29 | "es6": true,
30 | },
31 | "plugins": [
32 | "mozilla",
33 | "react",
34 | ...extraPlugins,
35 | ],
36 | "rules": {
37 | "comma-dangle": ["error", "always-multiline"],
38 | "curly": "error",
39 | "no-console": "warn",
40 | "semi": "error",
41 | "mozilla/no-define-cc-etc": "off",
42 | "mozilla/use-chromeutils-import": "off",
43 | },
44 | "settings": {
45 | "react": {
46 | "version": "16",
47 | },
48 | },
49 | };
50 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | contact_links:
2 | - name: Firefox Desktop Password Management UI Bugs
3 | url: https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox&component=about:logins
4 | about: Issues or enhancements managing Logins and Passwords (about:logins) in Firefox Desktop
5 | - name: Firefox Password Manager Website Compatibility Bugs
6 | url: https://bugzilla.mozilla.org/enter_bug.cgi?product=Toolkit&component=Password%20Manager:%20Site%20Compatibility
7 | about: Issues with the Firefox password manager working on a specific website. One website per bug please.
8 | - name: Firefox Password Manager Bugs
9 | url: https://bugzilla.mozilla.org/enter_bug.cgi?product=Toolkit&component=Password%20Manager
10 | about: Issues or enhancements filling or saving logins, not specific to a certain website.
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | /coverage/
3 | /dist/
4 | /docs/api.md
5 | /node_modules/
6 | /site/
7 | /temp/
8 | /addons/
9 | /.web-ext-artifacts/
10 | .DS_Store
11 | .cache
12 | .tox
13 | *.log
14 | *.pyc
15 | .pytest_cache
16 | geckodriver.exe
17 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Community Participation Guidelines
2 |
3 | This repository is governed by Mozilla's code of conduct and etiquette guidelines.
4 | For more details, please read the
5 | [Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).
6 |
7 | ## How to Report
8 | For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page.
9 |
10 |
16 |
--------------------------------------------------------------------------------
/bin/deploy-addon.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # Deploys a signed extension as a GitHub Release
4 |
5 | VERSION=$(cat package.json | grep version | head -1 | awk -F: '{ print $2 }' | sed 's/[",]//g' | tr -d '[[:space:]]')
6 | echo "Publish ${VERSION}"
7 | ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -recreate ${VERSION} ./addons/lockwise-signed.xpi
8 |
--------------------------------------------------------------------------------
/bin/install-test-dependencies.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -ex
3 |
4 | ## Copied from https://github.com/mozilla/testpilot/blob/master/bin/circleci/install-test-dependencies.sh
5 |
6 | GECKODRIVER_URL=$(
7 | curl -s 'https://api.github.com/repos/mozilla/geckodriver/releases/latest' |
8 | python -c "import sys, json; r = json.load(sys.stdin); print([a for a in r['assets'] if 'linux64' in a['name']][0]['browser_download_url']);"
9 | );
10 |
11 | curl -L -o geckodriver.tar.gz $GECKODRIVER_URL
12 | gunzip -c geckodriver.tar.gz | tar xopf -
13 | chmod +x geckodriver
14 | sudo mv geckodriver /bin
15 | geckodriver --version
16 | # Install pip
17 | sudo apt-get install python-pip python-dev build-essential
18 | sudo pip install --upgrade pip
19 |
20 | sudo pip install tox mozdownload mozinstall==1.15
21 |
22 | mkdir -p ~/project/firefox-downloads/
23 | find ~/project/firefox-downloads/ -type f -mtime +90 -delete
24 | mozdownload --version latest --destination ~/project/firefox-downloads/firefox/
25 | mozdownload --version latest-beta --destination ~/project/firefox-downloads/firefox_dev/
26 | mozdownload --version latest --type daily --destination ~/project/firefox-downloads/firefox_nightly/
27 |
28 | # Dependencies for firefox
29 | sudo apt-get update && sudo apt-get install -y libgtk3.0-cil-dev libasound2 libasound2 libdbus-glib-1-2 libdbus-1-3
30 |
31 | # install firefox-dev
32 | mozinstall $(ls -t ~/project/firefox-downloads/firefox_dev/*.tar.bz2 | head -1)
33 | alias firefox=~/project/firefox/firefox
34 | firefox --version
35 |
--------------------------------------------------------------------------------
/docs/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # lockwise-extension code owners
2 | # https://help.github.com/articles/about-codeowners/
3 |
4 | # test plans are maintained by PI
5 | /docs/developer/test-plan* @mozilla-lockwise/product-integrity
6 |
7 | # metrics docs are maintained by Leif
8 | /docs/metrics.md @irrationalagent
9 |
10 | # tests and related config are maintained by engineering
11 | /test/ @mozilla-lockwise/desktop-engineering
12 |
13 | # source code and all other docs are maintained by engineering
14 | /src/ @mozilla-lockwise/desktop-engineering
15 | /docs/ @mozilla-lockwise/desktop-engineering
16 |
17 | # release notes are maintained by the product owners
18 | /docs/release-notes.md @mozilla-lockwise/product
19 |
20 | # include Flod for l10n string changes
21 | /src/locales/en-US/*.ftl @flodolo
22 |
23 | # otherwise, everything is to be reviewed by the desktop team
24 | * @mozilla-lockwise/desktop-engineering
25 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to Lockwise's extension documentation!
2 |
3 | You can read this documentation [online][online-docs-link] or by browsing the
4 | [`docs` directory on GitHub][repo-docs-link].
5 |
6 | [online-docs-link]: https://mozilla-lockwise.github.io/lockwise-addon/
7 | [repo-docs-link]: https://github.com/mozilla-lockwise/lockwise-addon/tree/master/docs
8 |
--------------------------------------------------------------------------------
/docs/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Mozilla Security #
2 |
3 | - Mozilla cares about privacy and security. For more information please see: [https://www.mozilla.org/security/](https://www.mozilla.org/security/)
4 |
5 | - If you believe that you've found a security vulnerability, please report it by sending email to the addresses: [security@mozilla.org](mailto:security@mozilla.org) and [lockbox-dev@mozilla.com](mailto:lockbox-dev@mozilla.com)
6 |
--------------------------------------------------------------------------------
/docs/code_of_conduct.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | This repository is governed by Mozilla's code of conduct and etiquette guidelines. For more details please see the [Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/) and [Developer Etiquette Guidelines](https://bugzilla.mozilla.org/page.cgi?id=etiquette.html).
4 |
--------------------------------------------------------------------------------
/docs/css/extra.css:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | h1 {
6 | margin-bottom: 1em;
7 | }
8 |
9 | a.button-link {
10 | display: block;
11 | margin: .5em auto;
12 | padding: 0.5em;
13 | width: 12em;
14 | color: white;
15 | background-color: #717171;
16 | border: 1px solid #717171;
17 | border-radius: 4px;
18 | text-align: center;
19 | }
20 |
21 | div.right {
22 | float: right;
23 | clear: right;
24 | width: 40%;
25 | padding-left: 2em;
26 | }
27 |
28 | .rst-content .admonition-title::before {
29 | display: none !important;
30 | }
31 |
--------------------------------------------------------------------------------
/docs/developer/directory-structure.md:
--------------------------------------------------------------------------------
1 | # Directory Structure
2 |
3 | The following provides a brief outline of the directory structure of this project.
4 |
5 | ## `docs/`
6 |
7 | Documentation that gets built and deploy to GitHub pages (you're reading it!).
8 |
9 | ## `src/`
10 |
11 | This is where all of the source code for this project lives.
12 |
13 | #### `src/background/`
14 |
15 | Any code meant to run in a background page lives here. For more information, see the [`README.md`][background-readme] in this directory.
16 |
17 | #### `src/list/`
18 |
19 | Common code for all the list views in the extension, as well as the specific views themselves (in subdirectories). For more information, see the [`README.md`][list-readme] in this directory.
20 |
21 | ##### `src/list/manage/`
22 |
23 | The full-tab management UI.
24 |
25 | ##### `src/list/popup/`
26 |
27 | The "doorhanger" UI.
28 |
29 | #### `src/locales/`
30 |
31 | All the strings for each locale, in subdirectories with the locale name. For more information, see the [`README.md`][locales-readme] in this directory.
32 |
33 | #### `src/widgets/`
34 |
35 | All the common UI widgets used throughout the source. For more information, see the [`README.md`][widgets-readme] in this directory.
36 |
37 | ## `test/`
38 |
39 | This is where all of the tests for this project live.
40 |
41 | ### `test/integration/`
42 |
43 | End-to-end integration tests operating on a compiled version of the add-on running in Firefox. These tests are written in Javascript using Mocha and `webextensions-geckodriver`.
44 |
45 | ### `test/unit/`
46 |
47 | Unit tests testing individual components of the source code. These are writen in Javascript using Mocha/Chai and run via Karma. The subdirectories of this directory match the subdirectories of `src/webextension`.
48 |
49 | [background-readme]: https://github.com/mozilla-lockwise/lockwise-addon/blob/master/src/background/README.md
50 | [list-readme]: https://github.com/mozilla-lockwise/lockwise-addon/blob/master/src/list/README.md
51 | [locales-readme]: https://github.com/mozilla-lockwise/lockwise-addon/blob/master/src/locales/README.md
52 | [widgets-readme]: https://github.com/mozilla-lockwise/lockwise-addon/blob/master/src/widgets/README.md
53 |
--------------------------------------------------------------------------------
/docs/developer/test-plan-telemetry.md:
--------------------------------------------------------------------------------
1 | # Telemetry Test Plan: Lockwise-extension
2 |
3 | Given the importance the Lockwise team places on actionable user telemetry, ensuring confidence in the fidelity of that data is vital. Coverage aspects are broken into two distinct areas: test automation and manual testing.
4 |
5 | ## Unit tests
6 |
7 | When a new feature is developed or an additional user action needs to be recorded, the developer implementing the new telemetry event will add a unit test to ensure the action creates a ping.
8 |
9 | ## Manual tests
10 |
11 | ### New telemetry events
12 |
13 | Once a new telemetry event is added to a feature and that event has a unit test, the telemetry ping will be manually inspected by either Leif Oines, Product Data Scientist or Product Integrity. The goal of this exercise it to ensure the new event and accompanying unit test are capturing the correct user behavior.
14 |
15 | ### Regression testing
16 |
17 | A small handful of smoke tests will manually be run prior to each release. Note, the team will rely on the underlying unit tests for comprehensive testing of event verification.
18 |
19 | Product Integrity will investigate options for automating these tests into the integration test suite.
20 |
21 | ## Metrics being gathered
22 |
23 | For a comprehensive list of metrics being gathered review the [Lockwise Telemetry Plan](/metrics/).
24 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # Lockbox for desktop
2 |
3 | Welcome to the Lockbox desktop addon documentation!
4 |
--------------------------------------------------------------------------------
/docs/pull_request_template.md:
--------------------------------------------------------------------------------
1 | Fixes #???
2 |
3 | _(**Required**: this reference (one or many) will be closed upon merge. Ideally it has the acceptance criteria and designs for features or fixes related to the work in this Pull Request.)_
4 |
5 | Connected to #???
6 |
7 | _(Optional: other issues or pull requests related to this, but merging should not close it)_
8 |
9 | ## Testing and Review Notes
10 |
11 | _(**Required**: steps to take to confirm this works as expected or other guidance for code, UX, and any other reviewers)_
12 |
13 |
14 | ## Screenshots or Videos
15 |
16 | _(Optional: to clearly demonstrate the feature or fix to help with testing and reviews)_
17 |
18 |
19 | ## To Do
20 |
21 | - add “WIP” to the PR title if pushing up but not complete nor ready for review
22 | - [ ] double check the original issue to confirm it is fully satisfied
23 | - [ ] add testing notes and screenshots in PR description to help guide reviewers
24 | - [ ] add unit tests
25 | - optional: consider adding integration tests
26 | - [ ] request the "UX" team perform a design review (if/when applicable)
27 | - [ ] make sure CI builds are passing (e.g.: fix lint and other errors)
28 | - [ ] check on the [accessibility](https://mozilla-lockbox.github.io/lockbox-addon/developer/test-plan-accessibility/) of any added UI
29 | - [ ] make sure any new UI has telemetry events wired up and documented in docs/metrics.md, and verify that the events are recording properly (visit `about:telemetry#events-tab`, then choose 'dynamic' from the dropdown menu at top right, to view events fired by the addon
30 |
--------------------------------------------------------------------------------
/docs/user-guide.md:
--------------------------------------------------------------------------------
1 | # Lockbox User Guide
2 |
3 | First things first, [install the extension](developer/install.md).
4 |
5 | ## Commands
6 |
7 | * `Ctrl-Shift-L`: open Lockbox editor (item management)
8 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 | */
6 |
7 | module.exports = (config) => {
8 | config.set({
9 | browsers: ["FirefoxHeadless"],
10 | customLaunchers: {
11 | FirefoxHeadless: {
12 | base: "Firefox",
13 | flags: ["-headless"],
14 | },
15 | },
16 | files: [
17 | "test/unit/**/*-test.js",
18 | ],
19 | preprocessors: {
20 | "**/*.js": ["webpack", "sourcemap"],
21 | },
22 |
23 | frameworks: ["mocha"],
24 | reporters: ["mocha", "coverage"],
25 | coverageReporter: {
26 | dir: "coverage/",
27 | reporters: [
28 | { type: "text" },
29 | { type: "lcov" },
30 | { type: "html", subdir: "html" },
31 | ],
32 | },
33 | mochaReporter: {
34 | showDiff: true,
35 | },
36 |
37 | webpack: require("./webpack.config"),
38 | webpackMiddleware: {
39 | stats: "errors-only",
40 | },
41 | });
42 | };
43 |
--------------------------------------------------------------------------------
/l10n.toml:
--------------------------------------------------------------------------------
1 | basepath = "."
2 |
3 | [[paths]]
4 | reference = "src/locales/en-US/*.ftl"
5 | l10n = "src/locales/{locale}/*.ftl"
6 |
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: Lockbox Extension
2 | site_description: Documentation for the Firefox extension
3 | theme: readthedocs
4 | repo_url: https://github.com/mozilla-lockbox/lockbox-addon
5 |
6 | extra_css:
7 | - css/extra.css
8 |
9 | markdown_extensions:
10 | - attr_list
11 | - admonition
12 |
13 | pages:
14 | - 'Introduction': 'index.md'
15 | - 'Release Notes': 'release-notes.md'
16 | - 'Contribute': 'contributing.md'
17 | - 'Telemetry and Metrics': 'metrics.md'
18 | - 'Mozilla Security': 'SECURITY.md'
19 | - 'Developer Guides':
20 | - 'Build and Install': 'developer/install.md'
21 | - 'Release Instructions': 'developer/releases.md'
22 | - 'Directory Structure': 'developer/directory-structure.md'
23 | - 'Test Plan': 'developer/test-plan.md'
24 | - 'Test Plan - Accessibility': 'developer/test-plan-accessibility.md'
25 | - 'Test Plan - Telemetry': 'developer/test-plan-telemetry.md'
26 |
--------------------------------------------------------------------------------
/src/.dir-locals.el:
--------------------------------------------------------------------------------
1 | ; Use rjsx-mode instead of js2-mode for files in this directory.
2 | ((js2-mode . ((mode . rjsx))))
3 |
--------------------------------------------------------------------------------
/src/background/README.md:
--------------------------------------------------------------------------------
1 | # Background Scripts
2 |
3 | All of the logic working with the datastore occurs in the background scripts. Front-end pages communicate with these scripts via message ports.
4 |
5 | ## `open_view`
6 |
7 | Open the view named `name` in a new tab. Returns an empty object.
8 |
9 | ## `close_view`
10 |
11 | Close the view named `name` if it's open. Returns an empty object.
12 |
13 | ## `open_site`
14 |
15 | Open website passed as `url` in a new tab. Returns an empty object.
16 |
17 | ## `list_items`
18 |
19 | List all the items in the datastore. Returns an array of summaries of the items in the `items` field.
20 |
21 | ## `add_item`
22 |
23 | Add an item (in the `item` attribute) to the datastore. Returns the updated item in the `item` field with its `id` field filled out.
24 |
25 | ## `update_item`
26 |
27 | Update an existing item (in the `item` attribute) in the datastore. Returns the updated item in the `item` field.
28 |
29 | ## `remove_item`
30 |
31 | Remove the item with ID `id` from the datastore. Returns an empty object.
32 |
33 | ## `get_item`
34 |
35 | Fetches the item with ID `id` from the datastore. Returns the item object in the `item` field.
36 |
37 | ## `telemetry_event`
38 |
39 | Record a telemetry event with method `method`, object `object`, and extra `extra` (value is `null`).
40 |
41 | ## `telemetry_add`
42 |
43 | Increment a telemetry counter with `name` by some `value`.
44 |
--------------------------------------------------------------------------------
/src/background/browser-action.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | let popup;
6 |
7 | function installPopup(path) {
8 | popup = browser.extension.getURL(path);
9 | browser.browserAction.setPopup({
10 | popup,
11 | });
12 | }
13 |
14 | function uninstallPopup() {
15 | if (popup) {
16 | browser.browserAction.setPopup({ popup: "" });
17 | }
18 | popup = null;
19 | }
20 |
21 | function installEntriesAction() {
22 | return installPopup("list/popup.html");
23 | }
24 |
25 | export default async function updateBrowserAction() {
26 | // clear listener
27 | // XXXX: be more efficient with this?
28 | uninstallPopup();
29 |
30 | return installEntriesAction();
31 | }
32 |
--------------------------------------------------------------------------------
/src/background/clipboard.js:
--------------------------------------------------------------------------------
1 | const CLIPBOARD_CLEAR_DELAY = 1 * 60 * 1000; // 1 minute
2 |
3 | let clearClipboardTimer = null;
4 |
5 | export async function copyToClipboard(field, toCopy, navigatorClipboard = navigator.clipboard) {
6 | if (clearClipboardTimer) {
7 | // Clear any existing timer before scheduling a new one.
8 | window.clearTimeout(clearClipboardTimer);
9 | }
10 | await navigatorClipboard.writeText(toCopy);
11 | clearClipboardTimer = window.setTimeout(
12 | async () => {
13 | clearClipboardTimer = null;
14 | // Only clear the clipboard if it still holds the value set earlier.
15 | const inClipboard = await navigatorClipboard.readText();
16 | if (inClipboard === toCopy) {
17 | navigatorClipboard.writeText("");
18 | }
19 | },
20 | CLIPBOARD_CLEAR_DELAY
21 | );
22 | }
23 |
24 | export default { copyToClipboard };
25 |
--------------------------------------------------------------------------------
/src/background/environment.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | export async function initializeEnvironment() {
6 | // Issue #57: Disable login saving for extension pages.
7 | const origin = browser.extension.getURL("").slice(0, -1);
8 | const savingEnabled = await browser.experiments.logins
9 | .getLoginSavingEnabled(origin);
10 | if (savingEnabled) {
11 | await browser.experiments.logins
12 | .setLoginSavingEnabled(origin, false);
13 | }
14 |
15 | // NOTE: %DOMAIN% is replaced by the browser with the context's location:
16 | // * context menu -> "Fill Password" / "Fill Login" -> "Saved Logins"
17 | // * autofill dropdown -> "Saved logins"
18 | // %DOMAIN* is "" if there is no context (e.g., from main menu -> "Passwords and Logins")
19 | const mgmtURI = browser.extension.getURL("/list/manage.html?filter=%DOMAIN%");
20 | await browser.experiments.logins.setManagementURI(mgmtURI);
21 | }
22 |
--------------------------------------------------------------------------------
/src/background/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 | */
6 |
7 | import updateBrowserAction from "./browser-action";
8 | import initializeMessagePorts from "./message-ports";
9 | import { initializeTelemetry } from "./telemetry";
10 | import { initializeDataStore } from "./datastore";
11 | import { initializeEnvironment } from "./environment";
12 | import { initializeProfileInfo } from "./profile";
13 |
14 | Promise.resolve().then(async () => {
15 | await initializeEnvironment();
16 |
17 | // The `shouldUseEmbedded()` check tells us if FF version is less than 68. It
18 | // turns out there is another bug (1555734) that has nothing to do with whether
19 | // or not we need to use the embedded `recordEvent`, but does require that we
20 | // only enable telemetry for 68+. Hence, despite the misleading name, it's the
21 | // right function to call here.
22 | const enableTelemetry = !(await browser.experiments.temptelemetry.shouldUseEmbedded());
23 | initializeTelemetry(enableTelemetry);
24 | initializeMessagePorts();
25 | await initializeDataStore();
26 | await initializeProfileInfo();
27 | await updateBrowserAction();
28 | });
29 |
--------------------------------------------------------------------------------
/src/background/profile.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | import { broadcast } from "./message-ports";
6 |
7 | let profileInfo = null;
8 |
9 | export function updateProfileInfo(update) {
10 | profileInfo = update;
11 | broadcast({ type: "profile_info", profile: profileInfo });
12 | }
13 |
14 | export async function initializeProfileInfo() {
15 | const { sync } = browser.experiments;
16 | updateProfileInfo(await sync.getUserProfileInfo());
17 | sync.onUserProfileChanged.addListener(updateProfileInfo);
18 | }
19 |
20 | export function getProfileInfo() {
21 | return profileInfo;
22 | }
23 |
24 | export default initializeProfileInfo;
25 |
--------------------------------------------------------------------------------
/src/background/views.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | export class SingletonView {
6 | constructor(path) {
7 | this.path = path;
8 | this.id = undefined;
9 | }
10 |
11 | async open() {
12 | let windowId,
13 | offPath = false,
14 | url = browser.extension.getURL(this.path);
15 | if (this.id !== undefined) {
16 | // verify tab still exists
17 | try {
18 | let tabInfo = await browser.tabs.get(this.id);
19 | windowId = tabInfo.windowId;
20 | offPath = !tabInfo.url.startsWith(url);
21 | } catch (err) {
22 | // does not exist, forget existing info
23 | this.id = undefined;
24 | }
25 | }
26 |
27 | if (!offPath && this.id !== undefined) {
28 | // focus owning window and activate tab
29 | await browser.windows.update(windowId, {
30 | focused: true,
31 | });
32 | await browser.tabs.update(this.id, {
33 | active: true,
34 | });
35 | } else {
36 | // create a tab in the current window
37 | let tabInfo = await browser.tabs.create({
38 | url,
39 | });
40 | this.id = tabInfo.id;
41 | }
42 | return this;
43 | }
44 |
45 | async close() {
46 | try {
47 | (this.id !== undefined) && await browser.tabs.remove(this.id);
48 | } catch (err) {
49 | // Q: loggit?
50 | // eslint-disable-next-line no-console
51 | console.error(`could not close ${this.path} view: ${err.message}`);
52 | }
53 | this.id = undefined;
54 | return this;
55 | }
56 | }
57 |
58 | const views = {
59 | manage: new SingletonView("/list/manage.html"),
60 | };
61 |
62 | export async function openView(name) {
63 | const v = views[name];
64 | return v.open();
65 | }
66 |
67 | export async function closeView(name) {
68 | if (!name) {
69 | let pending = Object.keys(views).map((n) => views[n].close());
70 | await Promise.all(pending);
71 | return {};
72 | }
73 | const v = views[name];
74 | return v.close();
75 | }
76 |
--------------------------------------------------------------------------------
/src/common.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | export function makeItemSummary(item) {
6 | return {
7 | title: item.title,
8 | id: item.id,
9 | origins: item.origins,
10 | username: item.entry.username,
11 | timeLastUsed: item.timeLastUsed,
12 | timePasswordChanged: item.timePasswordChanged,
13 | };
14 | }
15 |
16 | export function classNames(classNames) {
17 | return classNames.filter((i) => i).join(" ");
18 | }
19 |
--------------------------------------------------------------------------------
/src/fonts/fira-mono-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mozilla-lockwise/lockwise-addon/56b75ccb8c9b7b36230c0e3ea51913e40086a30f/src/fonts/fira-mono-regular.woff
--------------------------------------------------------------------------------
/src/icons/add-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/icons/arrow-dropdown.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/icons/arrowhead-left-16.svg:
--------------------------------------------------------------------------------
1 |
4 |
7 |
--------------------------------------------------------------------------------
/src/icons/chevron-right.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/icons/clear.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/icons/close.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/icons/copy.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/icons/delete.svg:
--------------------------------------------------------------------------------
1 |
4 |
10 |
--------------------------------------------------------------------------------
/src/icons/edit.svg:
--------------------------------------------------------------------------------
1 |
4 |
7 |
--------------------------------------------------------------------------------
/src/icons/external-link.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/icons/hide.svg:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------
/src/icons/icon-account.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/icons/icon-desktop.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/icons/icon-download.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/icons/icon-faq.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/icons/icon-feedback.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/icons/icon-mobile.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/icons/icon-sync.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/icons/icon-toolbar.svg:
--------------------------------------------------------------------------------
1 |
4 |
7 |
--------------------------------------------------------------------------------
/src/icons/info-dark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/icons/info.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/icons/localized/LICENSE:
--------------------------------------------------------------------------------
1 | Icons in these subdirectories are not MPL licensed, and product names, logos, and brands are property of their respective owners:
2 |
3 | Google Play and the Google Play logo are trademarks of Google LLC.
4 |
5 | App Store and the App Store logo are trademarks of Apple Inc.
6 |
--------------------------------------------------------------------------------
/src/icons/localized/en/icon-play-store.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mozilla-lockwise/lockwise-addon/56b75ccb8c9b7b36230c0e3ea51913e40086a30f/src/icons/localized/en/icon-play-store.png
--------------------------------------------------------------------------------
/src/icons/search-focused.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/icons/search.svg:
--------------------------------------------------------------------------------
1 |
4 |
7 |
--------------------------------------------------------------------------------
/src/icons/show.svg:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------
/src/icons/warning-light.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
--------------------------------------------------------------------------------
/src/icons/warning.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/images/intro-step-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mozilla-lockwise/lockwise-addon/56b75ccb8c9b7b36230c0e3ea51913e40086a30f/src/images/intro-step-1.png
--------------------------------------------------------------------------------
/src/images/intro-step-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mozilla-lockwise/lockwise-addon/56b75ccb8c9b7b36230c0e3ea51913e40086a30f/src/images/intro-step-2.png
--------------------------------------------------------------------------------
/src/images/intro-step-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mozilla-lockwise/lockwise-addon/56b75ccb8c9b7b36230c0e3ea51913e40086a30f/src/images/intro-step-3.png
--------------------------------------------------------------------------------
/src/images/logged-out-avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mozilla-lockwise/lockwise-addon/56b75ccb8c9b7b36230c0e3ea51913e40086a30f/src/images/logged-out-avatar.png
--------------------------------------------------------------------------------
/src/list/README.md:
--------------------------------------------------------------------------------
1 | # List Views
2 |
3 | This directory contains the list views for the extension (the doorhanger - in `popup/` - and the full management UI - in `manage/`). Code in other directories is shared between both views.
4 |
5 | ## Redux storage
6 |
7 | The list views are the primary users of Redux in the extension. This section describes how the state is stored at a high level.
8 |
9 | ### Cache reducer (common)
10 |
11 | This reducer contains a local cache of the necessary portions of the Lockwise datastore. In particular, it contains a summary of all the items in the store, plus full details of the currently-selected item. Item summaries have the following fields:
12 |
13 | ```
14 | title
15 | id
16 | origins
17 | username
18 | ```
19 |
20 | ### List reducer (common)
21 |
22 | This reducer contains the logic for the actual item list. It stores the ID of the currently-selected item as well as any filter to be applied to the list.
23 |
24 | ### Editor reducer (manager)
25 |
26 | This reducer tracks the state of the editor components. It stores whether the editor is active and if the user has changed any of the fields. `hideHome` is a bit more subtle; it's used when selecting another item when the editor is open, and prevents the home view from appearing while that item is loading.
27 |
28 | ### Modal reducer (manager)
29 |
30 | This reducer contains the state for any modal dialogs to be shown in the manager. It contains the ID of the modal and any props that should be passed to the modal when it's displayed.
31 |
--------------------------------------------------------------------------------
/src/list/common.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | // This is a string instead of a symbol since we need to be able to use it as a
6 | // React `key` inside a list.
7 | export const NEW_ITEM_ID = "new-item";
8 |
9 | export function unflattenItem(item, id) {
10 | let origins = [];
11 | if (item.origin) {
12 | origins.push(item.origin);
13 | }
14 | if (item.formURL) {
15 | origins.push(item.formURL);
16 | }
17 | return {
18 | id,
19 | title: item.title,
20 | origins,
21 | realm: item.realm || null,
22 | timeCreated: item.timeCreated,
23 | timeLastUsed: item.timeLastUsed,
24 | timePasswordChanged: item.timePasswordChanged,
25 | entry: {
26 | kind: "login",
27 | username: item.username,
28 | password: item.password,
29 | },
30 | };
31 | }
32 |
33 | export function flattenItem(item) {
34 | return {
35 | title: item.title,
36 | origin: item.origins[0] || "",
37 | formURL: item.origins[1] || "",
38 | realm: item.realm || "",
39 | timeCreated: item.timeCreated,
40 | timeLastUsed: item.timeLastUsed,
41 | timePasswordChanged: item.timePasswordChanged,
42 | username: item.entry.username,
43 | password: item.entry.password,
44 | };
45 | }
46 |
47 | export const openWebsite = (url, closeWindow = true) => {
48 | browser.runtime.sendMessage({
49 | type: "open_site",
50 | url,
51 | });
52 | if (closeWindow) {
53 | window.close();
54 | }
55 | };
56 |
--------------------------------------------------------------------------------
/src/list/components/duplicate-notification.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | import { Localized } from "fluent-react";
6 | import PropTypes from "prop-types";
7 | import React from "react";
8 |
9 | import ErrorNotification from "./error-notification";
10 |
11 | import styles from "./notification.css";
12 |
13 | export default class DuplicateNotification extends React.Component {
14 | static get propTypes() {
15 | return {
16 | title: PropTypes.string.isRequired,
17 | };
18 | }
19 |
20 | static get defaultProps() {
21 | return {
22 | title: "",
23 | };
24 | }
25 |
26 | render() {
27 | const { title } = this.props;
28 |
29 | return (
30 |
31 |
}>
32 |
33 | aN eNTRy fOr {title} wITh tHAt uSERNAMe aLREADy eXISTs.
34 |
35 |
36 |
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/list/components/error-notification.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | import PropTypes from "prop-types";
6 | import React from "react";
7 |
8 | import styles from "./notification.css";
9 | import { classNames } from "../../common";
10 |
11 | export default function ErrorNotification({ className, children }) {
12 | const fullClassName = classNames([ styles.errorNotification, className ]);
13 |
14 | return
{children}
;
15 | }
16 |
17 | ErrorNotification.propTypes = {
18 | className: PropTypes.string,
19 | children: PropTypes.node,
20 | };
21 |
--------------------------------------------------------------------------------
/src/list/components/item-list.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | import PropTypes from "prop-types";
6 | import React from "react";
7 |
8 | import { classNames } from "../../common";
9 | import ItemSummary from "./item-summary";
10 | import ScrollingList from "../../widgets/scrolling-list";
11 |
12 | import styles from "./item-list.css";
13 |
14 | export default function ItemList({items, itemClassName, panel, ...props}) {
15 | return (
16 |
20 | {({id, title, username}) => {
21 | return (
22 |
24 | );
25 | }}
26 |
27 | );
28 | }
29 |
30 | ItemList.propTypes = {
31 | items: PropTypes.arrayOf(
32 | PropTypes.shape({
33 | id: PropTypes.string.isRequired,
34 | title: PropTypes.string.isRequired,
35 | username: PropTypes.string.isRequired,
36 | }).isRequired
37 | ).isRequired,
38 | itemClassName: PropTypes.string,
39 | panel: PropTypes.bool,
40 | sort: PropTypes.string,
41 | };
42 |
43 | ItemList.defaultProps = {
44 | itemClassName: "",
45 | sort: "name",
46 | };
47 |
48 | export function ItemListPlaceholder({className, children}) {
49 | const fullClassName = classNames(["itemListEmpty", styles.empty, className]);
50 | return (
51 |
52 | {children}
53 |
54 | );
55 | }
56 |
57 | ItemListPlaceholder.propTypes = {
58 | className: PropTypes.string,
59 | children: PropTypes.node,
60 | };
61 |
62 | ItemListPlaceholder.defaultProps = {
63 | className: "",
64 | };
65 |
--------------------------------------------------------------------------------
/src/list/components/item-summary.css:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | .item-summary-container {
6 | cursor: pointer;
7 | }
8 |
9 | .item-summary {
10 | padding: 9px 15px;
11 | position: relative;
12 | }
13 |
14 | .info {
15 | background: url(/icons/chevron-right.svg) no-repeat;
16 | position: absolute;
17 | right: 12px;
18 | top: 25px;
19 | height: 9px;
20 | width: 9px;
21 | }
22 |
23 | li[data-selected] .info {
24 | background-image: none;
25 | }
26 |
27 | .info.panel {
28 | right: 0px;
29 | top: -3px;
30 | width: 18px;
31 | height: 18px;
32 | margin: 20px;
33 | margin-right: 12px;
34 | background-image: url(/icons/info.svg);
35 | background-repeat: no-repeat;
36 | background-size: 18px 18px;
37 | background-position: 50%;
38 | padding: 7px;
39 | border-radius: 2px;
40 | }
41 |
42 | .info.panel:hover {
43 | background-color: rgba(12, 12, 13, 0.1);
44 | }
45 |
46 | .item-summary:hover .info.panel {
47 | background-image: url(/icons/info-dark.svg);
48 | }
49 |
50 | .title,
51 | .subtitle {
52 | line-height: 1.5;
53 | letter-spacing: 0.2px;
54 | overflow: hidden;
55 | white-space: nowrap;
56 | text-overflow: ellipsis;
57 | }
58 |
59 | .title {
60 | font-size: 14px;
61 | font-weight: 500;
62 | color: #0c0c0d;
63 | }
64 |
65 | .subtitle {
66 | font-size: 12px;
67 | font-weight: 300;
68 | color: #737373;
69 | }
70 |
--------------------------------------------------------------------------------
/src/list/components/item-summary.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | import { Localized } from "fluent-react";
6 | import PropTypes from "prop-types";
7 | import React from "react";
8 |
9 | import { classNames } from "../../common";
10 | import { NEW_ITEM_ID } from "../common";
11 |
12 | import styles from "./item-summary.css";
13 |
14 | export default function ItemSummary({className, id, title, username, panel}) {
15 | const trimmedTitle = title.trim();
16 | const trimmedUsername = username.trim();
17 |
18 | const isNew = id === NEW_ITEM_ID;
19 | const idModifier = (subject) => {
20 | if (isNew) {
21 | return "new-";
22 | }
23 | if (!subject.length) {
24 | return "no-";
25 | }
26 | return "";
27 | };
28 |
29 | const titleId = `item-summary-${idModifier(trimmedTitle)}title`;
30 | const usernameId = `item-summary-${idModifier(trimmedUsername)}username`;
31 | return (
32 |
33 |
34 |
35 |
{title || "nO tITLe"}
36 |
37 |
38 |
{username || "nO uSERNAMe"}
39 |
40 | {!isNew && panel &&
41 |
42 | }
43 |
44 |
45 | );
46 | }
47 |
48 | ItemSummary.propTypes = {
49 | className: PropTypes.string,
50 | id: PropTypes.string,
51 | title: PropTypes.string,
52 | username: PropTypes.string,
53 | panel: PropTypes.bool,
54 | };
55 |
56 | ItemSummary.defaultProps = {
57 | className: "",
58 | id: null,
59 | title: "",
60 | username: "",
61 | showInfo: false,
62 | };
63 |
--------------------------------------------------------------------------------
/src/list/components/notification.css:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | .error-notification {
6 | background: #ffe900;
7 | padding: 8px 5px;
8 | }
9 |
10 | .error-notification button {
11 | border-radius: 10px;
12 | border: 1px solid #989898;
13 | color: black;
14 | cursor: pointer;
15 | float: right;
16 | font-size: 11px;
17 | font-weight: 500;
18 | height: 19px;
19 | margin: 0px 10px;
20 | }
21 |
22 | .warning-notification {
23 | background: #a4000f;
24 | margin-bottom: 40px;
25 | color: #ffffff;
26 | }
27 |
28 | .warning-message,
29 | .warning-message-light {
30 | display: inline-block;
31 | margin: 0 10px;
32 | font-size: 12px;
33 | }
34 |
35 | .warning-message:before,
36 | .warning-message-light:before {
37 | background-image: url(/icons/warning.svg);
38 | content: '';
39 | display: inline-block;
40 | height: 16px;
41 | margin-right: 5px;
42 | margin-bottom: -2px;
43 | width: 16px;
44 | }
45 |
46 | .warning-message-light:before {
47 | background-image: url(/icons/warning-light.svg);
48 | }
49 |
50 | .close-icon {
51 | background-image: url(/icons/close.svg);
52 | cursor: pointer;
53 | display: inline-block;
54 | float: right;
55 | height: 16px;
56 | width: 16px;
57 | }
58 |
--------------------------------------------------------------------------------
/src/list/components/sync-notification.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | import { Localized } from "fluent-react";
6 | import PropTypes from "prop-types";
7 | import React from "react";
8 |
9 | import ErrorNotification from "./error-notification";
10 |
11 | import styles from "./notification.css";
12 |
13 | export default class SyncNotification extends React.Component {
14 | static get propTypes() {
15 | return {
16 | // profile not set as required, since the default is null
17 | // which tells us that the user has never signed in.
18 | hasProfileNeedsAttn: PropTypes.bool.isRequired,
19 | isPanel: PropTypes.bool,
20 | reconnectToSync: PropTypes.func.isRequired,
21 | };
22 | }
23 |
24 | static get defaultProps() {
25 | return {
26 | hasProfileNeedsAttn: false,
27 | isPanel: false,
28 | reconnectToSync: () => {},
29 | };
30 | }
31 |
32 | constructor(props) {
33 | super(props);
34 | this.remove = this.remove.bind(this);
35 | this.state = {hideNotification: false};
36 | }
37 |
38 | remove() {
39 | this.setState({
40 | hideNotification: true,
41 | });
42 | }
43 |
44 | render() {
45 | const { hideNotification } = this.state;
46 | const { isPanel, reconnectToSync, hasProfileNeedsAttn } = this.props;
47 | const shouldShow = hasProfileNeedsAttn && !hideNotification;
48 |
49 | return (<>
50 | {shouldShow &&
51 |
52 |
Unable to sync logins.
53 |
54 | {!isPanel && }
55 |
56 |
57 |
58 | }
59 | >);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/list/containers/connected-sync-notification.js:
--------------------------------------------------------------------------------
1 | import { connect } from "react-redux";
2 | import { openSyncPrefs } from "../actions";
3 |
4 | import SyncNotification from "../components/sync-notification";
5 |
6 | export default connect(({
7 | app: {
8 | profileWrap: {
9 | hasProfileNeedsAttn,
10 | },
11 | },
12 | }) => ({
13 | hasProfileNeedsAttn,
14 | }),
15 | dispatch => ({
16 | reconnectToSync: () => dispatch(openSyncPrefs()),
17 | })
18 | )(SyncNotification);
19 |
--------------------------------------------------------------------------------
/src/list/containers/item-filter.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | import { Localized } from "fluent-react";
6 | import PropTypes from "prop-types";
7 | import React from "react";
8 | import { connect } from "react-redux";
9 |
10 | import { filterItems } from "../actions";
11 | import FilterInput from "../../widgets/filter-input";
12 |
13 | function ItemFilter({inputRef, ...props}) {
14 | return (
15 |
18 |
20 |
21 | );
22 | }
23 |
24 | ItemFilter.propTypes = {
25 | inputRef: PropTypes.func,
26 | };
27 |
28 | export default connect(
29 | (state) => ({
30 | value: state.list.filter.query,
31 | disabled: state.cache.items.length === 0,
32 | }),
33 | (dispatch) => ({
34 | onChange: (value) => { dispatch(filterItems(value)); },
35 | })
36 | )(ItemFilter);
37 |
--------------------------------------------------------------------------------
/src/list/containers/no-matching-placeholder.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 | */
6 |
7 | import { Localized } from "fluent-react";
8 | import PropTypes from "prop-types";
9 | import React from "react";
10 | import { connect } from "react-redux";
11 |
12 | import { ItemListPlaceholder } from "../components/item-list";
13 | import { Link } from "../../widgets/link";
14 | import { openFAQ } from "../actions";
15 |
16 | export function BaseNoMatchingPlaceholder({
17 | withTitle,
18 | className,
19 | onLearnMore,
20 | }) {
21 | const header = (withTitle) ?
22 |
23 |
27 |
28 |
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/list/popup/components/item-details-panel.css:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | .panel-body {
6 | padding: 1em;
7 | background: #F9F9FA;
8 | }
9 |
10 | .panel-header {
11 | background: #fff;
12 | text-align: center;
13 | }
14 |
15 | .panel-header .edit-btn {
16 | display: block;
17 | background-image: url(/icons/edit.svg);
18 | background-repeat: no-repeat;
19 | background-size: 16px 16px;
20 | height: 16px;
21 | width: 16px;
22 | position: absolute;
23 | top: 16px;
24 | right: 8px;
25 | }
26 |
27 | .panel-footer-button {
28 | background: #2a2a2e !important;
29 | color: #f9f9fa;
30 | }
31 |
--------------------------------------------------------------------------------
/src/list/popup/components/item-details-panel.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | import { Localized } from "fluent-react";
6 | import React from "react";
7 | import PropTypes from "prop-types";
8 |
9 | import Panel, { PanelHeader, PanelBody, PanelFooter,
10 | PanelFooterButton } from "../../../widgets/panel";
11 | import { ItemFields } from "../../components/item-fields";
12 |
13 | import styles from "./item-details-panel.css";
14 |
15 | export default function ItemDetailsPanel({fields, showPassword, onCopy, onBack, onReveal, onOpenWebsite}) {
16 | return (
17 |
18 |
19 |
20 | lOGIn dETAILs
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
36 | oPEn wEBSITe
37 |
38 |
39 |
40 |
41 | );
42 | }
43 |
44 | ItemDetailsPanel.propTypes = {
45 | ...ItemFields.propTypes,
46 | onCopy: PropTypes.func.isRequired,
47 | onBack: PropTypes.func.isRequired,
48 | onReveal: PropTypes.func.isRequired,
49 | onOpenWebsite: PropTypes.func.isRequired,
50 | };
51 |
--------------------------------------------------------------------------------
/src/list/popup/components/item-list-panel.css:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | .empty {
6 | padding: 20px;
7 | }
8 |
9 | .panel-footer-button {
10 | color: #f9f9fa;
11 | cursor: pointer;
12 | background: #4a4a4f;
13 | font-size: 13px;
14 | }
15 |
16 | .panel-footer-button:hover {
17 | color: #f9f9fa;
18 | background: #2a2a2e !important;
19 | }
20 |
21 | .panel-header {
22 | background: #ffffff;
23 | padding: 12px;
24 | }
25 |
26 | .panel-banner {
27 | background: #d7d7db;
28 | color: #2a2a2e;
29 | font-size: 12px;
30 | font-weight: normal;
31 | }
32 |
33 | .filter-panel {
34 | background: #ffffff;
35 | border: 0;
36 | }
37 |
38 | .filter-panel::before,
39 | .filter-panel:focus-within::before {
40 | background-image: url(/icons/icon-lockwise.svg);
41 | }
42 |
43 | .filter-panel .input-wrapper {
44 | border: none;
45 | }
46 |
47 | .filter-panel input {
48 | background: #ffffff;
49 | font-size: 15px;
50 | font-weight: normal;
51 | color: #171718;
52 | border: none;
53 | }
54 |
55 | input::placeholder {
56 | color: #0C0C0D;
57 | }
58 |
--------------------------------------------------------------------------------
/src/list/popup/containers/all-items-panel.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | import { connect } from "react-redux";
6 |
7 | import ItemListPanel from "../components/item-list-panel";
8 | import { selectItem } from "../../actions";
9 | import { parseFilterString, filterItem } from "../../filter";
10 | import { byLastUsed } from "../../sort";
11 |
12 | export default connect(
13 | (state, ownProps) => {
14 | const totalItemCount = state.cache.items.length;
15 | const filter = parseFilterString(state.list.filter.query);
16 | const items = state.cache.items
17 | .filter((i) => filterItem(filter, i))
18 | .sort(byLastUsed);
19 | const isFiltering = state.list.filter.query.length;
20 | const currProps = {...ownProps, totalItemCount};
21 | if (items.length || state.list.filter.userEntered) {
22 | return {...currProps, items, noResultsBanner: false, isFiltering};
23 | }
24 | // An autogenerated filter that produced no results; show everything, plus
25 | // a banner informing users.
26 | return {...currProps, items: state.cache.items, noResultsBanner: true};
27 | },
28 | (dispatch) => ({
29 | onClick: (id) => { dispatch(selectItem(id)); },
30 | }),
31 | )(ItemListPanel);
32 |
--------------------------------------------------------------------------------
/src/list/popup/containers/current-selection.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | import PropTypes from "prop-types";
6 | import React from "react";
7 | import { connect } from "react-redux";
8 |
9 | import { flattenItem } from "../../common";
10 | import { selectItem, copiedField, openWebsite } from "../../actions";
11 | import AllItemsPanel from "./all-items-panel";
12 | import ItemDetailsPanel from "../components/item-details-panel";
13 | import { concealPassword, revealPassword } from "../../actions";
14 |
15 | const ConnectedItemDetailsPanel = connect(
16 | (state, ownProps) => ({
17 | fields: flattenItem(ownProps.item),
18 | showPassword: state.list.showPassword,
19 | }),
20 | (dispatch, ownProps) => ({
21 | onCopy: (field, toCopy) => { dispatch(copiedField(field, toCopy, ownProps.item)); },
22 | onBack: () => { dispatch(selectItem(null)); },
23 | onReveal: (show) => {
24 | const id = ownProps.item && ownProps.item.id;
25 | return dispatch(show ? revealPassword(id) : concealPassword(id));
26 | },
27 | onOpenWebsite: () => { dispatch(openWebsite(ownProps.item, true)); },
28 | })
29 | )(ItemDetailsPanel);
30 |
31 | function CurrentSelection({item, inputRef, onReveal}) {
32 | if (item) {
33 | return ;
34 | }
35 | return ;
36 | }
37 |
38 | CurrentSelection.propTypes = {
39 | item: PropTypes.object,
40 | inputRef: PropTypes.func,
41 | onReveal: PropTypes.func,
42 | };
43 |
44 | export default connect(
45 | (state) => ({
46 | item: state.cache.currentItem,
47 | }),
48 | )(CurrentSelection);
49 |
--------------------------------------------------------------------------------
/src/list/popup/containers/no-entries-placeholder.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 | */
6 |
7 | import { Localized } from "fluent-react";
8 | import PropTypes from "prop-types";
9 | import React from "react";
10 | import { connect } from "react-redux";
11 |
12 | import { ItemListPlaceholder } from "../../components/item-list";
13 | import { Link } from "../../../widgets/link";
14 | import { openFAQ } from "../../actions";
15 |
16 | export function BaseNoEntriesPlaceholder({className, onLearnMore}) {
17 | return
18 |
19 |
wHEn yOu cREATe an eNTRy...
20 |
21 |
24 | }>
25 |
fINd oUt wHy
26 |
27 | ;
28 | }
29 |
30 | BaseNoEntriesPlaceholder.propTypes = {
31 | className: PropTypes.string,
32 | onLearnMore: PropTypes.func.isRequired,
33 | };
34 |
35 | BaseNoEntriesPlaceholder.defaultProps = {
36 | className: "",
37 | };
38 |
39 | export default connect(
40 | (state, ownProps) => ({
41 | ...ownProps,
42 | }),
43 | (dispatch) => ({
44 | onLearnMore: () => dispatch(openFAQ("how-do-i-get-my-saved-logins-into-firefox-lockbox", true)),
45 | }),
46 | )(BaseNoEntriesPlaceholder);
47 |
--------------------------------------------------------------------------------
/src/list/popup/index.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | import React from "react";
6 | import ReactDOM from "react-dom";
7 | import { Provider } from "react-redux";
8 | import { createStore, applyMiddleware } from "redux";
9 | import thunk from "redux-thunk";
10 |
11 | import AppLocalizationProvider from "../../l10n";
12 | import App from "./components/app";
13 | import { filterItems, listItems, getProfile } from "../actions";
14 | import reducer from "./reducers";
15 | import initializeMessagePorts from "../message-ports";
16 | import telemetryLogger from "./telemetry";
17 | import openLink from "../open-link-middleware";
18 |
19 | const store = createStore(reducer, undefined, applyMiddleware(
20 | thunk, telemetryLogger, openLink,
21 | ));
22 |
23 | // Pre-fill the URL of the current tab.
24 | // XXX: Eventually, we'll probably want to put this in another file.
25 | browser.tabs.query({ active: true, currentWindow: true }).then((result) => {
26 | const validProtocols = ["http:", "https:"];
27 | const url = new URL(result[0].url);
28 | if (validProtocols.includes(url.protocol)) {
29 | store.dispatch(filterItems(url.hostname, false));
30 | }
31 | });
32 |
33 | store.dispatch(listItems());
34 | initializeMessagePorts(store);
35 | store.dispatch(getProfile());
36 |
37 | ReactDOM.render(
38 |
39 |
41 |
42 |
43 | ,
44 | document.getElementById("content")
45 | );
46 |
--------------------------------------------------------------------------------
/src/list/popup/reducers.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | import { combineReducers } from "redux";
6 |
7 | import { cacheReducer, listReducer, profileReducer } from "../reducers";
8 |
9 | export default combineReducers({
10 | // we use combineReducers here to add another layer
11 | // in the object in order to match the pattern used
12 | // in manage/reducers.js
13 | app: combineReducers({profileWrap: profileReducer}),
14 | cache: cacheReducer,
15 | list: listReducer,
16 | });
17 |
--------------------------------------------------------------------------------
/src/list/popup/telemetry.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | import * as actions from "../actions";
6 | import helpers from "../telemetry";
7 |
8 | export default (store) => (next) => (action) => {
9 | try {
10 | switch (action.type) {
11 | case actions.CONCEAL_PASSWORD:
12 | helpers.passwordConcealed("itemDetailDoorhanger");
13 | break;
14 | case actions.COPIED_FIELD_COMPLETED:
15 | helpers.itemCopied(action, "itemDetailDoorhanger");
16 | break;
17 | case actions.LIST_ITEMS_COMPLETED:
18 | // Accessing item info from the store requires waiting a turn.
19 | setTimeout(() => {
20 | const state = store.getState();
21 | helpers.listShown("itemListDoorhanger", state.cache.items);
22 | }, 0);
23 | break;
24 | case actions.OPEN_WEBSITE:
25 | helpers.websiteOpened("itemDetailDoorhanger");
26 | break;
27 | case actions.REVEAL_PASSWORD:
28 | helpers.passwordRevealed("itemDetailDoorhanger");
29 | break;
30 | case actions.SELECT_ITEM_COMPLETED:
31 | if (action.item) {
32 | helpers.itemShown("itemDetailDoorhanger");
33 | }
34 | break;
35 | case actions.SELECT_ITEM_STARTING:
36 | if (action.id) {
37 | helpers.itemSelected("doorhanger");
38 | }
39 | break;
40 | }
41 | } catch (e) {
42 | // eslint-disable-next-line no-console
43 | console.error("Unable to record telemetry event", e);
44 | }
45 |
46 | return next(action);
47 | };
48 |
--------------------------------------------------------------------------------
/src/list/sort.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | import { SORT_BY_NAME, SORT_BY_LAST_USED, SORT_BY_LAST_CHANGED } from "./actions";
6 |
7 | const collator = new Intl.Collator();
8 |
9 | export const sortActions = [
10 | SORT_BY_NAME,
11 | SORT_BY_LAST_USED,
12 | SORT_BY_LAST_CHANGED,
13 | ];
14 |
15 | export const byName = (a, b) => collator.compare(a.title, b.title);
16 |
17 | export const byLastUsed = (a, b) => a.timeLastUsed < b.timeLastUsed;
18 |
19 | export const byLastChanged = (a, b) => a.timePasswordChanged < b.timePasswordChanged;
20 |
21 | export const getAction = (name) => {
22 | switch (name) {
23 | case "last-changed":
24 | return SORT_BY_LAST_CHANGED;
25 | case "last-used":
26 | return SORT_BY_LAST_USED;
27 | case "name":
28 | default:
29 | return SORT_BY_NAME;
30 | }
31 | };
32 |
33 | export const getSortForAction = (action) => {
34 | switch (action) {
35 | case SORT_BY_LAST_CHANGED:
36 | return byLastChanged;
37 | case SORT_BY_LAST_USED:
38 | return byLastUsed;
39 | case SORT_BY_NAME:
40 | default:
41 | return byName;
42 | }
43 | };
44 |
45 | export const getName = (action) => {
46 | switch (action) {
47 | case SORT_BY_LAST_CHANGED:
48 | return "last-changed";
49 | case SORT_BY_LAST_USED:
50 | return "last-used";
51 | case SORT_BY_NAME:
52 | default:
53 | return "name";
54 | }
55 | };
56 |
57 | export const getSort = (name) => {
58 | switch (name) {
59 | case "last-changed":
60 | return byLastChanged;
61 | case "last-used":
62 | return byLastUsed;
63 | case "name":
64 | default:
65 | return byName;
66 | }
67 | };
68 |
--------------------------------------------------------------------------------
/src/locales/README.md:
--------------------------------------------------------------------------------
1 | # Adding Locales
2 |
3 | To add a locale, just create a new directory with the name of your locale and
4 | add the necessary translations to it. For development builds, this locale will
5 | automatically be included. However, for production builds, you should **add your
6 | locale to locales.json**. This allows you to gradually work on adding
7 | translations to your locale without having to have it production-ready
8 | immediately.
9 |
--------------------------------------------------------------------------------
/src/locales/cak/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Tiwachib'ëx
6 | copy-to-clipboard-copied = ✔ Xwachib'ëx
7 | filter-input-clear =
8 | .title = Tijosq'ïx
9 | modal-root =
10 | .contentLabel = Modal ch'owen
11 | password-input-show =
12 | .title = Tik'ut
13 | password-input-hide =
14 | .title = Tewäx
15 | panel-back-button =
16 | .alt = Titzolin
17 |
--------------------------------------------------------------------------------
/src/locales/cs/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Zkopírovat
6 | copy-to-clipboard-copied = ✔ Zkopírováno
7 | filter-input-clear =
8 | .title = Vymazat
9 | modal-root =
10 | .contentLabel = Modální dialog
11 | password-input-show =
12 | .title = Zobrazit
13 | password-input-hide =
14 | .title = Skrýt
15 | panel-back-button =
16 | .alt = Přejít zpět
17 |
--------------------------------------------------------------------------------
/src/locales/cy/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Copïo
6 | copy-to-clipboard-copied = ✔ Copïwyd
7 | filter-input-clear =
8 | .title = Clirio
9 | modal-root =
10 | .contentLabel = Deialog moddol
11 | password-input-show =
12 | .title = Dangos
13 | password-input-hide =
14 | .title = Cuddio
15 | panel-back-button =
16 | .alt = Nôl
17 |
--------------------------------------------------------------------------------
/src/locales/de/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Kopieren
6 | copy-to-clipboard-copied = ✔ Kopiert
7 | filter-input-clear =
8 | .title = Leeren
9 | modal-root =
10 | .contentLabel = Modaler Dialog
11 | password-input-show =
12 | .title = Anzeigen
13 | password-input-hide =
14 | .title = Ausblenden
15 | panel-back-button =
16 | .alt = Zurück
17 |
--------------------------------------------------------------------------------
/src/locales/el/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Αντιγραφή
6 | copy-to-clipboard-copied = ✔ Αντιγράφηκε
7 | filter-input-clear =
8 | .title = Απαλοιφή
9 | modal-root =
10 | .contentLabel = Πλαίσιο διαλόγου
11 | password-input-show =
12 | .title = Εμφάνιση
13 | password-input-hide =
14 | .title = Απόκρυψη
15 | panel-back-button =
16 | .alt = Επιστροφή
17 |
--------------------------------------------------------------------------------
/src/locales/en-CA/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Copy
6 | copy-to-clipboard-copied = ✔ Copied
7 | filter-input-clear =
8 | .title = Clear
9 | modal-root =
10 | .contentLabel = Modal dialog
11 | password-input-show =
12 | .title = Show
13 | password-input-hide =
14 | .title = Hide
15 | panel-back-button =
16 | .alt = Go Back
17 |
--------------------------------------------------------------------------------
/src/locales/en-US/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Copy
6 | copy-to-clipboard-copied = ✔ Copied
7 |
8 | filter-input-clear =
9 | .title = Clear
10 |
11 | modal-root =
12 | .contentLabel = Modal dialog
13 |
14 | password-input-show =
15 | .title = Show
16 | password-input-hide =
17 | .title = Hide
18 |
19 | panel-back-button =
20 | .alt = Go Back
21 |
--------------------------------------------------------------------------------
/src/locales/fr/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Copier
6 | copy-to-clipboard-copied = ✔ Copié
7 | filter-input-clear =
8 | .title = Effacer
9 | modal-root =
10 | .contentLabel = Boîte de dialogue
11 | password-input-show =
12 | .title = Afficher
13 | password-input-hide =
14 | .title = Masquer
15 | panel-back-button =
16 | .alt = Retour
17 |
--------------------------------------------------------------------------------
/src/locales/fy-NL/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Kopiearje
6 | copy-to-clipboard-copied = ✔ Kopiearre
7 | filter-input-clear =
8 | .title = Wiskje
9 | modal-root =
10 | .contentLabel = Modaal dialoochfinster
11 | password-input-show =
12 | .title = Toane
13 | password-input-hide =
14 | .title = Ferstopje
15 | panel-back-button =
16 | .alt = Tebek
17 |
--------------------------------------------------------------------------------
/src/locales/hu/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Másolás
6 | copy-to-clipboard-copied = ✔ Másolva
7 | filter-input-clear =
8 | .title = Törlés
9 | modal-root =
10 | .contentLabel = Modális párbeszédablak
11 | password-input-show =
12 | .title = Megjelenítés
13 | password-input-hide =
14 | .title = Elrejtés
15 | panel-back-button =
16 | .alt = Ugrás vissza
17 |
--------------------------------------------------------------------------------
/src/locales/ia/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Copiar
6 | copy-to-clipboard-copied = ✔ Copiate
7 | filter-input-clear =
8 | .title = Clarar
9 | password-input-show =
10 | .title = Monstrar
11 | password-input-hide =
12 | .title = Celar
13 | panel-back-button =
14 | .alt = Regreder
15 |
--------------------------------------------------------------------------------
/src/locales/id/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Salin
6 | copy-to-clipboard-copied = ✔ Disalin
7 | filter-input-clear =
8 | .title = Bersihkan
9 | modal-root =
10 | .contentLabel = Dialog modal
11 | password-input-show =
12 | .title = Tampilkan
13 | password-input-hide =
14 | .title = Sembunyikan
15 | panel-back-button =
16 | .alt = Kembali
17 |
--------------------------------------------------------------------------------
/src/locales/it/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Copia
6 | copy-to-clipboard-copied = ✔ Copiato
7 | filter-input-clear =
8 | .title = Cancella
9 | modal-root =
10 | .contentLabel = Finestra di dialogo modale
11 | password-input-show =
12 | .title = Mostra
13 | password-input-hide =
14 | .title = Nascondi
15 | panel-back-button =
16 | .alt = Torna indietro
17 |
--------------------------------------------------------------------------------
/src/locales/kab/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Nɣel
6 | copy-to-clipboard-copied = Yenɣel
7 | filter-input-clear =
8 | .title = Sfeḍ
9 | modal-root =
10 | .contentLabel = Tanaka n udiwenni
11 | password-input-show =
12 | .title = Sken
13 | password-input-hide =
14 | .title = Ffer
15 | panel-back-button =
16 | .alt = Uɣal
17 |
--------------------------------------------------------------------------------
/src/locales/locales.json:
--------------------------------------------------------------------------------
1 | ["en-US", "cak", "cs", "cy", "de", "en-CA", "fr", "fy-NL", "hu", "id", "it", "kab", "nl", "nn-NO", "pt-BR", "ro", "sv-SE", "vi", "zh-CN", "zh-TW"]
2 |
--------------------------------------------------------------------------------
/src/locales/nl/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Kopiëren
6 | copy-to-clipboard-copied = ✔ Gekopieerd
7 | filter-input-clear =
8 | .title = Wissen
9 | modal-root =
10 | .contentLabel = Modaal dialoogvenster
11 | password-input-show =
12 | .title = Tonen
13 | password-input-hide =
14 | .title = Verbergen
15 | panel-back-button =
16 | .alt = Terug
17 |
--------------------------------------------------------------------------------
/src/locales/nn-NO/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Kopier
6 | copy-to-clipboard-copied = ✔ Kopiert
7 | filter-input-clear =
8 | .title = Tøm
9 | modal-root =
10 | .contentLabel = Modal dialog
11 | password-input-show =
12 | .title = Vis
13 | password-input-hide =
14 | .title = Gøym
15 | panel-back-button =
16 | .alt = Gå tilbake
17 |
--------------------------------------------------------------------------------
/src/locales/pt-BR/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Copiar
6 | copy-to-clipboard-copied = ✔ Copiado
7 | filter-input-clear =
8 | .title = Limpar
9 | modal-root =
10 | .contentLabel = Diálogo modal
11 | password-input-show =
12 | .title = Mostrar
13 | password-input-hide =
14 | .title = Esconder
15 | panel-back-button =
16 | .alt = Voltar
17 |
--------------------------------------------------------------------------------
/src/locales/ro/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Copiază
6 | copy-to-clipboard-copied = ✔ Copiat
7 | filter-input-clear =
8 | .title = Șterge
9 | modal-root =
10 | .contentLabel = Fereastră de dialog modal
11 | password-input-show =
12 | .title = Afișează
13 | password-input-hide =
14 | .title = Ascunde
15 | panel-back-button =
16 | .alt = Înapoi
17 |
--------------------------------------------------------------------------------
/src/locales/sl/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Kopiraj
6 | copy-to-clipboard-copied = ✔ Kopirano
7 | filter-input-clear =
8 | .title = Počisti
9 | modal-root =
10 | .contentLabel = Modalno pogovorno okno
11 | password-input-show =
12 | .title = Prikaži
13 | password-input-hide =
14 | .title = Skrij
15 | panel-back-button =
16 | .alt = Nazaj
17 |
--------------------------------------------------------------------------------
/src/locales/sv-SE/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Kopiera
6 | copy-to-clipboard-copied = ✔ Kopierad
7 | filter-input-clear =
8 | .title = Rensa
9 | modal-root =
10 | .contentLabel = Modal dialog
11 | password-input-show =
12 | .title = Visa
13 | password-input-hide =
14 | .title = Dölj
15 | panel-back-button =
16 | .alt = Gå tillbaka
17 |
--------------------------------------------------------------------------------
/src/locales/vi/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = Sao chép
6 | copy-to-clipboard-copied = ✔ Đã sao chép
7 | filter-input-clear =
8 | .title = Xóa
9 | modal-root =
10 | .contentLabel = Hộp thoại
11 | password-input-show =
12 | .title = Hiện
13 | password-input-hide =
14 | .title = Ẩn
15 | panel-back-button =
16 | .alt = Quay lại
17 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = 复制
6 | copy-to-clipboard-copied = ✔ 已复制
7 | filter-input-clear =
8 | .title = 清除
9 | modal-root =
10 | .contentLabel = 对话框
11 | password-input-show =
12 | .title = 显示
13 | password-input-hide =
14 | .title = 隐藏
15 | panel-back-button =
16 | .alt = 返回
17 |
--------------------------------------------------------------------------------
/src/locales/zh-TW/widgets.ftl:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | copy-to-clipboard-button = 複製
6 | copy-to-clipboard-copied = 已複製 ✔
7 | filter-input-clear =
8 | .title = 清除
9 | modal-root =
10 | .contentLabel = 對話框
11 | password-input-show =
12 | .title = 顯示
13 | password-input-hide =
14 | .title = 隱藏
15 | panel-back-button =
16 | .alt = 返回
17 |
--------------------------------------------------------------------------------
/src/manifest.json.tpl:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "{{title}}",
4 | "short_name": "{{name}}",
5 | "version": "{{version}}",
6 | "description": "{{description}}",
7 | "icons": {
8 | "48": "icons/icon-lockwise.svg",
9 | "96": "icons/icon-lockwise.svg"
10 | },
11 |
12 |
13 | "content_security_policy": "script-src 'self' {{testing_csp_scripts}} ; object-src 'self' {{testing_csp_objects}}",
14 |
15 | "browser_specific_settings": {
16 | "gecko": {
17 | "id": "{{id}}",
18 | "strict_min_version": "67.0",
19 | "strict_max_version": "69.*",
20 | "update_url": "https://lockwise.firefox.com/addon/updates.json"
21 | }
22 | },
23 |
24 | "background": {
25 | "scripts": ["background.js"]
26 | },
27 |
28 | "browser_action": {
29 | "default_icon": {
30 | "32": "icons/icon-toolbar.svg"
31 | },
32 | "browser_style": false
33 | },
34 |
35 | "commands": {
36 | "_execute_browser_action": {
37 | "suggested_key": {
38 | "default": "Ctrl+Shift+L"
39 | }
40 | }
41 | },
42 |
43 | "experiment_apis": {
44 | "logins": {
45 | "schema": "experiments/logins/schema.json",
46 | "parent": {
47 | "scopes": ["addon_parent"],
48 | "script": "experiments/logins/api.js",
49 | "paths": [["experiments", "logins"]]
50 | }
51 | },
52 | "sync": {
53 | "schema": "experiments/sync/schema.json",
54 | "parent": {
55 | "scopes": ["addon_parent"],
56 | "script": "experiments/sync/api.js",
57 | "paths": [["experiments", "sync"]]
58 | }
59 | },
60 | "temptelemetry": {
61 | "schema": "experiments/temptelemetry/schema.json",
62 | "parent": {
63 | "scopes": ["addon_parent"],
64 | "script": "experiments/temptelemetry/api.js",
65 | "paths": [["experiments", "temptelemetry"]]
66 | }
67 | }
68 | },
69 |
70 | "permissions": [
71 | "tabs",
72 | "clipboardRead",
73 | "clipboardWrite",
74 | "mozillaAddons",
75 | "storage",
76 | "telemetry"
77 | ]
78 | }
79 |
--------------------------------------------------------------------------------
/src/telemetry.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | export async function recordEvent({ method, object, extra = null, value = null}) {
6 | return browser.runtime.sendMessage({
7 | type: "telemetry_event",
8 | data: { method, object, extra, value },
9 | });
10 | }
11 |
12 | export async function scalarAdd({ name, value }) {
13 | return browser.runtime.sendMessage({
14 | type: "telemetry_add",
15 | data: { name, value },
16 | });
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/src/template.ejs:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 | <% for (let css of htmlWebpackPlugin.files.css) { %>
9 |
10 | <% } for (let js of htmlWebpackPlugin.files.js) { %>
11 |
12 | <% } %>
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/widgets/README.md:
--------------------------------------------------------------------------------
1 | # Common Widgets
2 |
3 | This directory contains the common widgets used throughout the extension. If you need a general-purpose UI widget, it should probably go in here.
4 |
5 | ## Visuals
6 |
7 | All the widgets here are designed to follow the [Photon Design System][photon], though we occasionally make modifications to better suit our needs.
8 |
9 | ## APIs
10 |
11 | Widgets should be designed as React components (no Redux, since that would add extra requirements to how a view's Redux store is designed), and should generally contain a few common options/methods to allow users of the widget to work with it as needed.
12 |
13 | ### Custom classNames
14 |
15 | Widgets should have a `className` argument in their constructor that allows users to customize the styling of the widget for a particular instance.
16 |
17 | ### focus() method
18 |
19 | Focusable widgets (buttons, form elements, links, etc) should have a `focus()` method that focuses the relevant HTML element. If the widget also contains selectable text, `focus()` should take an optional boolean value (defaulting to `false`) to focus *and* select the text.
20 |
21 | ### Extra props
22 |
23 | Finally, widgets should accept extra props in their constructor and apply them to the most-relevant subcomponent. This allows users to apply things like ARIA labels and other props to the widget.
24 |
25 | [photon]: https://design.firefox.com/photon/welcome.html
26 |
--------------------------------------------------------------------------------
/src/widgets/banner.css:
--------------------------------------------------------------------------------
1 | /**
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 | */
6 |
7 | .banner {
8 | border: solid 1px #d7d7db;
9 | background-color: #ffffff;
10 | padding: 24px 20px;
11 | }
12 |
--------------------------------------------------------------------------------
/src/widgets/banner.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 | */
6 |
7 | import PropTypes from "prop-types";
8 | import React from "react";
9 |
10 | import styles from "./banner.css";
11 | import { classNames } from "../common";
12 |
13 | // TODO: add "close banner" hooks (https://github.com/mozilla-lockbox/lockbox-addon/issues/95)
14 |
15 | export default function Banner({ className, children }) {
16 | const fullClassName = classNames([ styles.banner, className ]);
17 |
18 | return
{children}
;
19 | }
20 |
21 | Banner.propTypes = {
22 | className: PropTypes.string,
23 | children: PropTypes.node,
24 | };
25 |
--------------------------------------------------------------------------------
/src/widgets/breadcrumbs.css:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | .breadcrumbs {
6 | font-size: 15px;
7 | }
8 |
9 | .crumb {
10 | font-size: 15px;
11 | }
12 |
13 | .crumb:not(:first-child)::before {
14 | content: "";
15 | display: inline-block;
16 | width: 5px;
17 | height: 9px;
18 | background-image: url(/icons/chevron-right.svg);
19 | background-repeat: no-repeat;
20 | background-size: 5px 9px;
21 | margin: 0 16px;
22 | }
23 |
--------------------------------------------------------------------------------
/src/widgets/breadcrumbs.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | import PropTypes from "prop-types";
6 | import React from "react";
7 |
8 | import { classNames } from "../common";
9 | import { Link } from "./link";
10 |
11 | import styles from "./breadcrumbs.css";
12 |
13 | export default function Breadcrumbs({className, ...props}) {
14 | return (
15 |
16 | );
17 | }
18 |
19 | Breadcrumbs.propTypes = {
20 | className: PropTypes.string,
21 | };
22 |
23 | Breadcrumbs.defaultProps = {
24 | className: "",
25 | };
26 |
27 | export function Crumb({className, onClick, ...props}) {
28 | const finalClassName = classNames([styles.crumb, className]);
29 | if (onClick) {
30 | return (
31 |
32 | );
33 | }
34 |
35 | return (
36 |
37 | );
38 | }
39 |
40 | Crumb.propTypes = {
41 | className: PropTypes.string,
42 | onClick: PropTypes.func,
43 | };
44 |
45 | Crumb.defaultProps = {
46 | className: "",
47 | };
48 |
--------------------------------------------------------------------------------
/src/widgets/button.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | import PropTypes from "prop-types";
6 | import React from "react";
7 |
8 | import { classNames } from "../common";
9 |
10 | import styles from "./button.css";
11 |
12 | const THEME_CLASS_NAME = {
13 | primary: `${styles.primaryTheme}`,
14 | normal: `${styles.normalTheme}`,
15 | ghost: `${styles.ghostTheme}`,
16 | danger: `${styles.dangerTheme}`,
17 | };
18 |
19 | const SIZE_CLASS_NAME = {
20 | puffy: `${styles.puffySize}`,
21 | normal: `${styles.normalSize}`,
22 | micro: `${styles.microSize}`,
23 | wide: `${styles.normalSize} ${styles.wideSize}`,
24 | };
25 |
26 | export default class Button extends React.Component {
27 | static get propTypes() {
28 | return {
29 | theme: PropTypes.oneOf(Object.keys(THEME_CLASS_NAME)),
30 | size: PropTypes.oneOf(Object.keys(SIZE_CLASS_NAME)),
31 | id: PropTypes.string,
32 | className: PropTypes.string,
33 | };
34 | }
35 |
36 | static get defaultProps() {
37 | return {
38 | theme: "normal",
39 | size: "normal",
40 | id: null,
41 | className: "",
42 | };
43 | }
44 |
45 | focus() {
46 | this.buttonElement.focus();
47 | }
48 |
49 | render() {
50 | const {id, className, theme, size, ...props} = this.props;
51 | return (
52 |