├── setup.cfg ├── .dir-locals.el ├── src ├── webextension │ ├── locales │ │ ├── locales.json │ │ ├── README.md │ │ ├── en-US │ │ │ ├── unlock.ftl │ │ │ ├── widgets.ftl │ │ │ ├── settings.ftl │ │ │ ├── firstrun.ftl │ │ │ └── common.ftl │ │ └── fr-FR │ │ │ ├── unlock.ftl │ │ │ ├── widgets.ftl │ │ │ ├── firstrun.ftl │ │ │ ├── settings.ftl │ │ │ └── common.ftl │ ├── .dir-locals.el │ ├── images │ │ ├── intro-step-1.png │ │ ├── intro-step-2.png │ │ └── intro-step-3.png │ ├── fonts │ │ └── fira-mono-regular.woff │ ├── list │ │ ├── manage │ │ │ ├── containers │ │ │ │ ├── open-faq.css │ │ │ │ ├── send-feedback.css │ │ │ │ ├── add-item.css │ │ │ │ ├── current-breadcrumbs.js │ │ │ │ ├── current-account-summary.js │ │ │ │ ├── open-faq.js │ │ │ │ ├── send-feedback.js │ │ │ │ ├── add-item.js │ │ │ │ ├── all-items.js │ │ │ │ ├── modals.js │ │ │ │ └── account-details.js │ │ │ ├── components │ │ │ │ ├── item-list-panel.css │ │ │ │ ├── item-details.css │ │ │ │ ├── homepage.css │ │ │ │ ├── account-linked.css │ │ │ │ ├── account-linked.js │ │ │ │ ├── homepage.js │ │ │ │ ├── link-account.css │ │ │ │ ├── intro-page.css │ │ │ │ ├── app.css │ │ │ │ ├── home-breadcrumbs.js │ │ │ │ ├── link-account.js │ │ │ │ ├── item-details.js │ │ │ │ ├── app.js │ │ │ │ ├── intro-page.js │ │ │ │ └── item-list-panel.js │ │ │ ├── index.js │ │ │ └── reducers.js │ │ ├── popup │ │ │ ├── components │ │ │ │ ├── item-details-panel.css │ │ │ │ ├── app.css │ │ │ │ ├── app.js │ │ │ │ └── item-details-panel.js │ │ │ ├── reducers.js │ │ │ ├── telemetry.js │ │ │ ├── containers │ │ │ │ ├── current-selection.js │ │ │ │ └── all-items-panel.js │ │ │ └── index.js │ │ ├── components │ │ │ ├── item-fields.css │ │ │ ├── item-summary.css │ │ │ ├── item-list.js │ │ │ └── item-list.css │ │ ├── common.js │ │ ├── message-ports.js │ │ ├── filter.js │ │ ├── containers │ │ │ └── item-filter.js │ │ └── README.md │ ├── telemetry.js │ ├── widgets │ │ ├── toolbar.css │ │ ├── copy-to-clipboard-button.css │ │ ├── dialog-box.css │ │ ├── breadcrumbs.css │ │ ├── modal-root.css │ │ ├── stack.css │ │ ├── label-text.js │ │ ├── scrolling-list.css │ │ ├── text-area.css │ │ ├── field-text.js │ │ ├── link.css │ │ ├── toolbar.js │ │ ├── modal-root.js │ │ ├── text-area.js │ │ ├── input.js │ │ ├── breadcrumbs.js │ │ ├── README.md │ │ ├── stack.js │ │ ├── button.js │ │ └── link.js │ ├── icons │ │ ├── account.svg │ │ ├── arrowhead-left-16.svg │ │ ├── lb_unlocked.svg │ │ ├── show.svg │ │ ├── lb_locked.svg │ │ ├── chevron-right.svg │ │ ├── signout.svg │ │ ├── search.svg │ │ ├── hide.svg │ │ ├── clear.svg │ │ ├── default-avatar.svg │ │ ├── external-link.svg │ │ └── options.svg │ ├── settings │ │ ├── components │ │ │ ├── app.css │ │ │ ├── app.js │ │ │ └── local-reset.js │ │ ├── reducers.js │ │ ├── index.js │ │ ├── containers │ │ │ └── modals.js │ │ └── actions.js │ ├── common.js │ ├── background │ │ ├── telemetry.js │ │ ├── index.js │ │ ├── datastore.js │ │ ├── accounts │ │ │ └── configs.json │ │ └── README.md │ ├── firstrun │ │ ├── components │ │ │ ├── app.css │ │ │ ├── intro.js │ │ │ ├── using.css │ │ │ ├── intro.css │ │ │ ├── app.js │ │ │ └── using.js │ │ └── index.js │ ├── unlock │ │ ├── index.js │ │ └── components │ │ │ ├── app.css │ │ │ └── app.js │ └── manifest.json.tpl ├── icon.png ├── template.ejs └── install.rdf.ejs ├── .eslintignore ├── test ├── integration │ ├── pages │ │ ├── util │ │ │ ├── __init__.py │ │ │ └── util.py │ │ ├── __init__.py │ │ ├── base.py │ │ └── login.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_account_creation.py │ │ ├── test_doorhanger.py │ │ ├── test_guest_mode.py │ │ └── test_accessibility.py │ └── conftest.py ├── .eslintrc.json ├── .dir-locals.el └── unit │ ├── settings │ ├── mock-redux-state.js │ ├── reducers-test.js │ ├── components │ │ └── app-test.js │ └── actions-test.js │ ├── common.js │ ├── enzyme.js │ ├── firstrun │ └── components │ │ ├── intro-test.js │ │ ├── app-test.js │ │ └── using-test.js │ ├── list │ ├── manage │ │ ├── components │ │ │ ├── account-linked-test.js │ │ │ ├── homepage-test.js │ │ │ ├── item-details-test.js │ │ │ └── home-breadcrumbs-test.js │ │ ├── containers │ │ │ ├── current-breadcrumbs-test.js │ │ │ ├── open-faq-test.js │ │ │ ├── send-feedback-test.js │ │ │ └── add-item-test.js │ │ └── mock-redux-state.js │ ├── popup │ │ ├── mock-redux-state.js │ │ └── components │ │ │ ├── item-details-panel-test.js │ │ │ └── app-test.js │ ├── containers │ │ └── item-filter-test.js │ ├── components │ │ └── item-list-test.js │ └── filter-test.js │ ├── widgets │ ├── label-text-test.js │ ├── stack-test.js │ ├── field-text-test.js │ ├── button-test.js │ ├── text-area-test.js │ ├── toolbar-test.js │ └── link-test.js │ ├── index.js │ ├── mocks │ ├── xpcom.js │ └── l10n.js │ ├── telemetry-test.js │ └── chai-focus.js ├── requirements ├── flake8.txt └── tests.txt ├── .pyup.yml ├── docs ├── images │ ├── tour-01.welcome.png │ ├── tour-04.signup-fxa.png │ ├── tour-02.create-entry.png │ └── tour-03.doorhanger-search.png ├── user-guide.md ├── SECURITY.md ├── README.md ├── code_of_conduct.md ├── css │ └── extra.css ├── developer │ └── test-plan-telemetry.md ├── CODEOWNERS └── index.md ├── mocha-webpack.opts ├── .gitignore ├── jpm-prefs.json ├── stylelint.config.js ├── tox.ini ├── .babelrc ├── mkdocs.yml ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── .eslintrc.js ├── karma.conf.js ├── webpack.config.test.js └── json-webpack-plugin.js /setup.cfg: -------------------------------------------------------------------------------- 1 | [isort] 2 | -------------------------------------------------------------------------------- /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ((markdown-mode 2 | (mode . visual-line))) 3 | -------------------------------------------------------------------------------- /src/webextension/locales/locales.json: -------------------------------------------------------------------------------- 1 | ["en-US", "fr-FR"] 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | !.eslintrc.js 2 | coverage 3 | dist 4 | node_modules 5 | site 6 | temp 7 | -------------------------------------------------------------------------------- /test/integration/pages/util/__init__.py: -------------------------------------------------------------------------------- 1 | """Contain utility functions and tools.""" 2 | -------------------------------------------------------------------------------- /test/integration/tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Tests package containing extension tests.""" 2 | -------------------------------------------------------------------------------- /requirements/flake8.txt: -------------------------------------------------------------------------------- 1 | flake8==3.5.0 2 | flake8-isort==2.5 3 | flake8-docstrings==1.3.0 4 | -------------------------------------------------------------------------------- /src/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozilla-lockwise/lockbox-extension/HEAD/src/icon.png -------------------------------------------------------------------------------- /test/integration/pages/__init__.py: -------------------------------------------------------------------------------- 1 | """Module containing the pages available for interaction.""" 2 | -------------------------------------------------------------------------------- /.pyup.yml: -------------------------------------------------------------------------------- 1 | # see https://pyup.io/docs/configuration/ for all available options 2 | 3 | schedule: every week 4 | -------------------------------------------------------------------------------- /test/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "extends": "../.eslintrc.js" 6 | } 7 | -------------------------------------------------------------------------------- /test/.dir-locals.el: -------------------------------------------------------------------------------- 1 | ; Use rjsx-mode instead of js2-mode for files in this directory. 2 | ((js2-mode . ((mode . rjsx)))) 3 | -------------------------------------------------------------------------------- /docs/images/tour-01.welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozilla-lockwise/lockbox-extension/HEAD/docs/images/tour-01.welcome.png -------------------------------------------------------------------------------- /docs/images/tour-04.signup-fxa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozilla-lockwise/lockbox-extension/HEAD/docs/images/tour-04.signup-fxa.png -------------------------------------------------------------------------------- /src/webextension/.dir-locals.el: -------------------------------------------------------------------------------- 1 | ; Use rjsx-mode instead of js2-mode for files in this directory. 2 | ((js2-mode . ((mode . rjsx)))) 3 | -------------------------------------------------------------------------------- /docs/images/tour-02.create-entry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozilla-lockwise/lockbox-extension/HEAD/docs/images/tour-02.create-entry.png -------------------------------------------------------------------------------- /mocha-webpack.opts: -------------------------------------------------------------------------------- 1 | --colors 2 | --recursive 3 | --require ignore-styles 4 | --require babel-core/register 5 | --require jsdom-global/register 6 | -------------------------------------------------------------------------------- /docs/images/tour-03.doorhanger-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozilla-lockwise/lockbox-extension/HEAD/docs/images/tour-03.doorhanger-search.png -------------------------------------------------------------------------------- /src/webextension/images/intro-step-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozilla-lockwise/lockbox-extension/HEAD/src/webextension/images/intro-step-1.png -------------------------------------------------------------------------------- /src/webextension/images/intro-step-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozilla-lockwise/lockbox-extension/HEAD/src/webextension/images/intro-step-2.png -------------------------------------------------------------------------------- /src/webextension/images/intro-step-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozilla-lockwise/lockbox-extension/HEAD/src/webextension/images/intro-step-3.png -------------------------------------------------------------------------------- /src/webextension/fonts/fira-mono-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozilla-lockwise/lockbox-extension/HEAD/src/webextension/fonts/fira-mono-regular.woff -------------------------------------------------------------------------------- /requirements/tests.txt: -------------------------------------------------------------------------------- 1 | fxapom==1.10.2 2 | PyPOM==2.0.0 3 | pytest==3.7.1 4 | pytest-selenium==1.13.0 5 | pytest-xdist==1.22.5 6 | selenium==3.14.0 7 | axe-selenium-python==2.0.4 8 | pytest-axe==1.0.0 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | /coverage/ 3 | /dist/ 4 | /docs/api.md 5 | /node_modules/ 6 | /site/ 7 | /temp/ 8 | /*.xpi 9 | /addon.env 10 | .DS_Store 11 | .cache 12 | .tox 13 | *.log 14 | *.pyc 15 | .pytest_cache 16 | -------------------------------------------------------------------------------- /jpm-prefs.json: -------------------------------------------------------------------------------- 1 | { 2 | "toolkit.telemetry.enabled": true, 3 | "toolkit.telemetry.server": "https://127.0.0.1/telemetry-dummy/", 4 | "xpinstall.signatures.required": false, 5 | "extensions.legacy.enabled": true 6 | } 7 | -------------------------------------------------------------------------------- /src/webextension/list/manage/containers/open-faq.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 | .faq { 6 | font-size: 15px; 7 | } 8 | -------------------------------------------------------------------------------- /src/webextension/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 | } 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/ 4 | 5 | - If you believe that you've found a security vulnerability, please report it by sending email to the addresses: security@mozilla.org and lockbox-dev@mozilla.com 6 | -------------------------------------------------------------------------------- /src/webextension/list/manage/containers/send-feedback.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 | .send-feedback { 6 | font-size: 15px; 7 | } 8 | -------------------------------------------------------------------------------- /src/webextension/list/manage/containers/add-item.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 | .add-item { 6 | min-width: initial; 7 | width: 32px; 8 | } 9 | -------------------------------------------------------------------------------- /src/webextension/list/manage/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 | .filter-toolbar > * + * { 6 | margin-inline-start: 8px; 7 | } 8 | -------------------------------------------------------------------------------- /stylelint.config.js: -------------------------------------------------------------------------------- 1 | const defaultSeverity = (process.env.STRICT_LINT !== "1") ? "warning" : "error"; 2 | 3 | module.exports = { 4 | defaultSeverity, 5 | extends: "stylelint-config-recommended", 6 | rules: { 7 | "font-family-no-missing-generic-family-keyword": null, 8 | "no-descending-specificity": null, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to Lockbox'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-lockbox.github.io/lockbox-extension/ 7 | [repo-docs-link]: https://github.com/mozilla-lockbox/lockbox-extension/tree/master/docs 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/webextension/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) { 6 | return browser.runtime.sendMessage({ 7 | type: "proxy_telemetry_event", method, object, extra, 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /test/unit/settings/mock-redux-state.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 | // Keep these in sync with . 6 | 7 | export const initialState = { 8 | modal: { 9 | id: null, 10 | props: null, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /src/webextension/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/webextension/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 } from "../reducers"; 8 | 9 | export default combineReducers({ 10 | cache: cacheReducer, 11 | list: listReducer, 12 | }); 13 | -------------------------------------------------------------------------------- /src/webextension/widgets/toolbar.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 | .toolbar { 6 | margin: 0; 7 | padding: 10px; 8 | display: flex; 9 | align-items: center; 10 | } 11 | 12 | .toolbar > * + * { 13 | margin-inline-start: 20px; 14 | } 15 | 16 | .toolbar-space { 17 | flex: 1; 18 | } 19 | -------------------------------------------------------------------------------- /test/unit/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 simulateTyping(wrapper, value, {clear = false} = {}) { 6 | if (clear) { 7 | wrapper.instance().value = value; 8 | } else { 9 | wrapper.instance().value += value; 10 | } 11 | wrapper.simulate("change"); 12 | } 13 | -------------------------------------------------------------------------------- /src/webextension/icons/account.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/webextension/icons/arrowhead-left-16.svg: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/webextension/locales/en-US/unlock.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 | document = 6 | .title = Unlock Lockbox 7 | 8 | unlock-title = { product-title } 9 | unlock-tagline = { product-tagline } 10 | 11 | unlock-action-signin = { product-action-signin } 12 | unlock-action-prefs = { product-action-prefs } 13 | -------------------------------------------------------------------------------- /src/webextension/icons/lb_unlocked.svg: -------------------------------------------------------------------------------- 1 | unlockbox-16 2 | -------------------------------------------------------------------------------- /src/webextension/locales/fr-FR/unlock.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 | document = 6 | .title = Débloquer Lockbox 7 | 8 | unlock-title = { product-title } 9 | unlock-tagline = { product-tagline } 10 | 11 | unlock-action-signin = { product-action-signin } 12 | unlock-action-prefs = { product-action-prefs } 13 | -------------------------------------------------------------------------------- /src/webextension/widgets/copy-to-clipboard-button.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 | .copy-button { 6 | color: #0060df; 7 | } 8 | 9 | .copied-label { 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | height: 32px; 14 | min-width: 80px; 15 | font-size: 13px; 16 | color: #00c100; 17 | } 18 | -------------------------------------------------------------------------------- /src/webextension/icons/show.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/webextension/settings/components/app.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 | html { 6 | display: flex; 7 | } 8 | 9 | body { 10 | padding: 0; 11 | margin: 1em; 12 | min-height: 150px; 13 | color: #0c0c0d; 14 | background-color: #f9f9fa; 15 | font: caption; 16 | font-size: 13px; 17 | -moz-user-select: none; 18 | display: flex; 19 | } 20 | -------------------------------------------------------------------------------- /src/webextension/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 | }; 12 | } 13 | 14 | export function classNames(classNames) { 15 | return classNames.filter((i) => i).join(" "); 16 | } 17 | -------------------------------------------------------------------------------- /src/webextension/list/popup/components/app.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 | body { 6 | padding: 0; 7 | margin: 0; 8 | width: 280px; 9 | height: 360px; 10 | color: #0c0c0d; 11 | background-color: #ededf0; 12 | font: caption; 13 | font-size: 13px; 14 | -moz-user-select: none; 15 | } 16 | 17 | body > main, 18 | .app { 19 | height: 100%; 20 | } 21 | -------------------------------------------------------------------------------- /src/webextension/icons/lb_locked.svg: -------------------------------------------------------------------------------- 1 | lockbox-16 2 | -------------------------------------------------------------------------------- /src/webextension/widgets/dialog-box.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 | .modal-dialog { 6 | padding: 1.5em 1.5em 1em; 7 | font-size: 22px; 8 | font-weight: 300; 9 | text-align: center; 10 | } 11 | 12 | .modal-dialog > menu { 13 | margin: 1.5em 0 0 0; 14 | padding: 0; 15 | } 16 | 17 | .modal-dialog > menu > button + button { 18 | margin-inline-start: 2.5ch; 19 | } 20 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py3-integration-tests, flake8, a11y 3 | skipsdist = True 4 | 5 | [testenv] 6 | skip_install = True 7 | passenv = DISPLAY MOZ_HEADLESS 8 | deps = -rrequirements/tests.txt 9 | commands = pytest --driver=Firefox --verbose {posargs:test/integration} 10 | 11 | [testenv:flake8] 12 | deps = 13 | -rrequirements/flake8.txt 14 | -rrequirements/tests.txt 15 | commands = flake8 {posargs:.} 16 | 17 | [flake8] 18 | exclude = .tox, node_modules 19 | 20 | [testenv:a11y] 21 | commands = pytest --driver=Firefox --verbose {posargs:test/integration} --axe 22 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-react", 4 | ["@babel/preset-env", { 5 | "targets": {"browsers": ["firefox >= 56"]}, 6 | "useBuiltins": true, 7 | "modules": false 8 | }] 9 | ], 10 | "plugins": [ 11 | "@babel/plugin-syntax-object-rest-spread", 12 | "@babel/plugin-syntax-async-generators" 13 | ], 14 | "env": { 15 | "production": { 16 | "presets": [ 17 | "minify" 18 | ] 19 | }, 20 | "test": { 21 | "plugins": [ 22 | "istanbul", 23 | "rewire" 24 | ] 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/webextension/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/webextension/background/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) { 6 | return browser.runtime.sendMessage({ 7 | type: "telemetry_event", method, object, extra, 8 | }); 9 | } 10 | 11 | export async function setScalar(name, value) { 12 | return browser.runtime.sendMessage({ 13 | type: "telemetry_scalar", name, value, 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /src/webextension/locales/fr-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 = Copie 6 | copy-to-clipboard-copied = ✔ Copié 7 | 8 | filter-input-clear = 9 | .title = Éffacer 10 | 11 | modal-root = 12 | .contentLabel = Dialogue modal 13 | 14 | password-input-show = 15 | .title = Afficher 16 | password-input-hide = 17 | .title = Masquer 18 | 19 | panel-back-button = 20 | .alt = Retour arrière 21 | -------------------------------------------------------------------------------- /src/webextension/list/manage/components/item-details.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-details { 6 | padding: 1em 4.6em 4.6em; 7 | background-color: #f9f9fa; 8 | } 9 | 10 | .item-details > h1 { 11 | font-weight: 300; 12 | font-size: 22px; 13 | margin: 0 0 1.5em; 14 | padding-bottom: 1em; 15 | border-bottom: 1px solid #d7d7db; 16 | } 17 | 18 | .buttons { 19 | margin-top: 1em; 20 | padding: 1em 0; 21 | } 22 | -------------------------------------------------------------------------------- /src/webextension/locales/en-US/settings.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 | settings-local-reset-title = Reset Lockbox 6 | settings-local-reset-description = This resets Lockbox to its uninitialized state. Once performed, it cannot be undone. 7 | settings-local-reset-button = 💥💣 Reset 💣💥 8 | 9 | modal-local-reset = Are you sure you wish to reset Lockbox? 10 | .confirmLabel = Yes, reset it 11 | .cancelLabel = No, do not reset 12 | -------------------------------------------------------------------------------- /test/integration/tests/test_account_creation.py: -------------------------------------------------------------------------------- 1 | """Test login and new account creation flows.""" 2 | 3 | 4 | def test_sign_in_with_fxa(fxa_account, login_page): 5 | """Sign in with fxa from the login page.""" 6 | fxa = fxa_account 7 | home_page = login_page.sign_in(fxa.email, fxa.password) 8 | assert home_page.sign_in_button_is_displayed() is False 9 | 10 | 11 | def test_sign_in_with_fxa_from_home(fxa_account, home_page): 12 | """Sign in with fxa from 'Home' page.""" 13 | fxa = fxa_account 14 | home_page.sign_in(fxa.email, fxa.password) 15 | assert home_page.sign_in_button_is_displayed() is False 16 | -------------------------------------------------------------------------------- /src/webextension/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/webextension/firstrun/components/app.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 | body { 6 | padding: 0; 7 | margin: 0; 8 | color: #0c0c0d; 9 | background-color: #f9f9fa; 10 | font: caption; 11 | font-size: 13px; 12 | -moz-user-select: none; 13 | } 14 | 15 | .firstrun { 16 | width: 612px; 17 | margin: 3em auto; 18 | display: flex; 19 | flex-flow: column nowrap; 20 | align-items: center; 21 | } 22 | 23 | .firstrun > img { 24 | width: 300px; 25 | } 26 | -------------------------------------------------------------------------------- /src/webextension/locales/en-US/firstrun.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 | document = 6 | .title = Welcome to Lockbox 7 | 8 | firstrun-intro-title = Welcome to { product-title } 9 | firstrun-intro-tagline = { product-tagline } 10 | 11 | firstrun-using-guest-title = New to Lockbox? 12 | 13 | firstrun-using-guest-action = Get Started 14 | 15 | firstrun-using-returning-title = Returning User? 16 | 17 | firstrun-using-returning-action = Sign in to your Firefox Account 18 | -------------------------------------------------------------------------------- /src/webextension/locales/fr-FR/firstrun.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 | document 6 | .title = Bienvenue dans Lockbox 7 | 8 | firstrun-intro-title = Bienvenue dans { product-title } 9 | firstrun-intro-tagline = { product-tagline } 10 | 11 | firstrun-using-guest-title = Nouveau dans Lockbox? 12 | 13 | firstrun-using-guest-action = Commençons 14 | 15 | firstrun-using-returning-title = Retour utilisateurs ? 16 | 17 | firstrun-using-returning-action = Connectez-vous à votre compte Firefox 18 | -------------------------------------------------------------------------------- /src/webextension/widgets/modal-root.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 | .modal { 6 | border-radius: 4px; 7 | box-shadow: 0 4px 16px 0 rgba(12, 12, 13, 0.1); 8 | min-width: 300px; 9 | color: #0c0c0d; 10 | background-color: #f9f9fa; 11 | } 12 | 13 | .overlay { 14 | position: fixed; 15 | top: 0; 16 | bottom: 0; 17 | left: 0; 18 | right: 0; 19 | display: flex; 20 | justify-content: center; 21 | align-items: center; 22 | background-color: rgba(0, 0, 0, 0.5); 23 | } 24 | -------------------------------------------------------------------------------- /src/webextension/icons/chevron-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /test/integration/tests/test_doorhanger.py: -------------------------------------------------------------------------------- 1 | """Tests for the door hanger.""" 2 | 3 | import pytest 4 | 5 | 6 | @pytest.mark.xfail 7 | def test_door_hanger_interaction(fxa_account, login_page): 8 | """Add an entry and test it shows in the door hanger.""" 9 | fxa = fxa_account 10 | home_page = login_page.sign_in(fxa.email, fxa.password) 11 | home_page.create_new_entry("Tuna, Inc.", "https://satuna.org", "tuna4life", 12 | "tunafish", "The tuna swim at midnight") 13 | 14 | lists = home_page.door_hanger.find_entrys() 15 | assert 'Tuna, Inc.' in lists[0].title 16 | entry = lists[0].click() 17 | assert 'Tuna, Inc.' in entry.title 18 | -------------------------------------------------------------------------------- /src/webextension/locales/fr-FR/settings.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 | settings-local-reset-title = Réinitialiser Lockbox 6 | settings-local-reset-description = Celà va réinitialiser Lockbox à son état initial. Une fois celà fait, il est impossible d'annuler l'action. 7 | settings-local-reset-button = 💥💣 Réinitialisation 💣💥 8 | 9 | modal-local-reset = Êtes-vous sûr de vouloir procéder à une réinitialisation de Lockbox? 10 | .confirmLabel = Oui, le réinitialiser 11 | .cancelLabel = Non, ne pas réinitialiser 12 | -------------------------------------------------------------------------------- /src/webextension/widgets/stack.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 | /* XXX: Sadly, I can't come up with a better way to make something that works 6 | like a than by just using XUL's box model... */ 7 | 8 | .stack { 9 | display: -moz-stack; 10 | } 11 | 12 | .stretch { 13 | height: 100%; 14 | width: 100%; 15 | } 16 | 17 | .stack-item { 18 | display: -moz-box; 19 | visibility: hidden; 20 | } 21 | 22 | .stack-item[data-selected] { 23 | visibility: visible; 24 | } 25 | 26 | .stack-item > * { 27 | -moz-box-flex: 1; 28 | } 29 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/webextension/widgets/label-text.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 "./input.css"; 11 | 12 | export default function LabelText({className, ...props}) { 13 | return ( 14 | 15 | ); 16 | } 17 | 18 | LabelText.propTypes = { 19 | className: PropTypes.string, 20 | }; 21 | 22 | LabelText.defaultProps = { 23 | className: "", 24 | }; 25 | -------------------------------------------------------------------------------- /src/webextension/settings/components/app.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 | 8 | import LocalReset from "./local-reset"; 9 | import ModalRoot from "../containers/modals"; 10 | 11 | import "./app.css"; 12 | 13 | export default function App() { 14 | return ( 15 |
16 | 17 |

nOTe: tHIs eXTENSION Is nOt mAINTAINEd

18 |
19 | 20 | 21 |
22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /src/webextension/firstrun/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 | 8 | import AppLocalizationProvider from "../l10n"; 9 | import App from "./components/app"; 10 | import * as telemetry from "../telemetry"; 11 | 12 | telemetry.recordEvent("render", "firstrun"); 13 | 14 | ReactDOM.render( 15 | 17 | 18 | , 19 | document.getElementById("content") 20 | ); 21 | -------------------------------------------------------------------------------- /src/webextension/list/manage/components/homepage.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 | .homepage { 6 | display: flex; 7 | flex-flow: column nowrap; 8 | align-items: center; 9 | margin: 3em; 10 | } 11 | 12 | .homepage > h1, 13 | .homepage > h2, 14 | .homepage > h3, 15 | .homepage > p { 16 | color: #0c0c0d; 17 | letter-spacing: 0.2px; 18 | line-height: 1.5; 19 | text-align: center; 20 | } 21 | 22 | .homepage h1 { 23 | font-size: 17px; 24 | font-weight: 300; 25 | font-style: italic; 26 | text-transform: lowercase; 27 | } 28 | 29 | .homepage > img { 30 | max-width: 279px; 31 | } 32 | -------------------------------------------------------------------------------- /src/webextension/locales/fr-FR/common.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 | product-title = Lockbox 6 | product-tagline = Une façon simple de stocker, retrouver et de gérer les infos de connexion de site web 7 | 8 | # Lors ce que Kinto est supporté, le texte ci-dessus devrait être modifié comme suit: 9 | # Créer un compte Firefox - ou ajouter Lockbox à un compte existant - protège vos identifiants 10 | # avec le chiffrage le plus puissant disponible et synchronisations de vos infos Lockbox à travers vos comptes. 11 | 12 | 13 | product-action-signin = S'identifier 14 | product-action-prefs = Réglages 15 | -------------------------------------------------------------------------------- /src/webextension/widgets/scrolling-list.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 | .scrolling-list { 6 | margin: 0; 7 | padding: 0; 8 | list-style: none; 9 | overflow-x: hidden; 10 | overflow-y: auto; 11 | } 12 | 13 | .scrolling-list:-moz-focusring { 14 | outline-style: none; 15 | } 16 | 17 | .styled-item { 18 | color: #0c0c0d; 19 | background-color: #f9f9fa; 20 | } 21 | 22 | .scrolling-list > .styled-item[data-selected] { 23 | background-color: #b1b1b3; 24 | } 25 | 26 | .scrolling-list:focus > .styled-item[data-selected] { 27 | color: #ffffff; 28 | background-color: #0060df; 29 | } 30 | -------------------------------------------------------------------------------- /src/webextension/widgets/text-area.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 | .text-area { 6 | padding: 8px; 7 | font: caption; 8 | font-size: 13px; 9 | color: #0c0c0d; 10 | background-color: #ffffff; 11 | border: 1px solid rgba(12, 12, 13, 0.3); 12 | border-radius: 2px; 13 | } 14 | 15 | .text-area:hover { 16 | border-color: rgba(12, 12, 13, 0.5); 17 | } 18 | 19 | .text-area:focus { 20 | border-color: #0a84ff; 21 | /* Note: This is adapted from the Photon button spec, since the input fields 22 | spec doesn't define this. */ 23 | box-shadow: 0 0 0 3px rgba(10, 132, 255, 0.3); 24 | } 25 | -------------------------------------------------------------------------------- /src/webextension/icons/signout.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/webextension/list/manage/containers/current-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 { connect } from "react-redux"; 6 | 7 | import HomeBreadcrumbs from "../components/home-breadcrumbs"; 8 | import { requestSelectItem } from "../../actions"; 9 | 10 | export default connect( 11 | (state) => { 12 | const item = state.cache.currentItem; 13 | return { 14 | itemId: state.list.selectedItemId, 15 | itemTitle: item ? item.title : undefined, 16 | }; 17 | }, 18 | (dispatch) => ({ 19 | onClickHome: () => { dispatch(requestSelectItem(null)); }, 20 | }) 21 | )(HomeBreadcrumbs); 22 | -------------------------------------------------------------------------------- /src/webextension/list/manage/components/account-linked.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 | .linked { 6 | margin: 2em auto; 7 | padding: 0; 8 | display: flex; 9 | flex-flow: column nowrap; 10 | align-items: center; 11 | } 12 | 13 | .linked > h2, 14 | .linked > p { 15 | text-align: center; 16 | letter-spacing: 0.2px; 17 | line-height: 1.5; 18 | } 19 | 20 | .linked > h2 { 21 | margin: 0 0 .5em; 22 | padding: 0; 23 | font-size: 17px; 24 | font-weight: 600; 25 | line-height: 1.32; 26 | color: #058b00; 27 | } 28 | 29 | .linked > p { 30 | margin-top: 0; 31 | font-size: 15px; 32 | white-space: pre-line; 33 | } 34 | -------------------------------------------------------------------------------- /src/webextension/list/manage/components/account-linked.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 | 8 | import styles from "./account-linked.css"; 9 | 10 | export default function AccountLinked() { 11 | return ( 12 |
13 | 14 |

aCCOUNt lINKEd

15 |
16 | 17 |

Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

18 |
19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /test/unit/enzyme.js: -------------------------------------------------------------------------------- 1 | import Enzyme, { mount } from "enzyme"; 2 | import Adapter from "enzyme-adapter-react-16"; 3 | 4 | Enzyme.configure({adapter: new Adapter()}); 5 | 6 | const MOUNT_INJECT_ID = "mount-inject-node"; 7 | 8 | export function getInjectNode({id = MOUNT_INJECT_ID, style = ""} = {}) { 9 | let inject = document.getElementById(id); 10 | if (inject) { 11 | return inject; 12 | } 13 | 14 | inject = document.createElement("div"); 15 | inject.id = id; 16 | inject.style = style; 17 | document.body.appendChild(inject); 18 | return inject; 19 | } 20 | 21 | export function mountIntoDOM(node, options = {}) { 22 | const inject = getInjectNode(); 23 | return mount(node, { attachTo: inject, ...options }); 24 | } 25 | 26 | // XXX: Export everything from enzyme. 27 | export { mount }; 28 | export default Enzyme; 29 | -------------------------------------------------------------------------------- /src/webextension/firstrun/components/intro.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 | 8 | import styles from "./intro.css"; 9 | 10 | export default function Intro() { 11 | return ( 12 |
13 | 14 |

wELCOMe

15 |
16 | 17 |

mORe wELCOMe

18 |
19 | 20 |

uNMAINTAINEd dISCLAIMEr

21 |
22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /src/webextension/firstrun/components/using.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 | .using { 6 | display: flex; 7 | flex-flow: column nowrap; 8 | align-items: center; 9 | } 10 | 11 | .using h1, 12 | .using h2, 13 | .using p { 14 | text-align: center; 15 | } 16 | 17 | .using > .actions { 18 | margin: 0; 19 | display: flex; 20 | flex-flow: column nowrap; 21 | align-items: stretch; 22 | } 23 | 24 | .using > .actions > h2 { 25 | margin: 1em 0 .5em; 26 | font-size: 22px; 27 | font-weight: 300; 28 | line-height: 1.5; 29 | letter-spacing: 0.2px; 30 | } 31 | 32 | .using > .actions > button { 33 | margin: 0; 34 | font-size: 15px; 35 | } 36 | -------------------------------------------------------------------------------- /test/unit/firstrun/components/intro-test.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 chai, { expect } from "chai"; 6 | import chaiEnzyme from "chai-enzyme"; 7 | import React from "react"; 8 | 9 | import mountWithL10n from "test/mocks/l10n"; 10 | import Intro from "src/webextension/firstrun/components/intro"; 11 | 12 | chai.use(chaiEnzyme()); 13 | 14 | describe("firstrun > components > ", () => { 15 | it("render ", () => { 16 | const wrapper = mountWithL10n( 17 | 18 | ); 19 | 20 | expect(wrapper.find("h1")).to.have.text("wELCOMe"); 21 | expect(wrapper.find("h2")).to.have.text("mORe wELCOMe"); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/webextension/locales/en-US/common.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 | product-title = Lockbox 6 | product-tagline = The simple way to store, retrieve and manage website login info 7 | 8 | product-disclaimer = This is just a prototype and not officially supported. Any data stored or functionality is not guaranteed to remain. 9 | 10 | # when Kinto is supported, the above should be changed to: 11 | # Creating a Firefox account - or adding Lockbox to an existing account - protects your logins 12 | # with the strongest encryption available and syncs your Lockbox info across accounts. 13 | 14 | 15 | product-action-signin = Sign In 16 | product-action-prefs = Preferences 17 | -------------------------------------------------------------------------------- /src/webextension/firstrun/components/intro.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 | .intro { 6 | display: flex; 7 | flex-flow: column nowrap; 8 | justify-content: center; 9 | align-items: center; 10 | } 11 | 12 | .intro > h1, 13 | .intro > h2, 14 | .intro > h3, 15 | .intro > p { 16 | text-align: center; 17 | line-height: 1.5; 18 | letter-spacing: 0.2px; 19 | } 20 | 21 | .intro > h1 { 22 | font-size: 33px; 23 | font-weight: 300; 24 | margin: 0; 25 | } 26 | 27 | .intro > h2 { 28 | font-size: 17px; 29 | font-weight: 300; 30 | font-style: italic; 31 | margin: 0px; 32 | text-transform: lowercase; 33 | } 34 | 35 | .intro > p { 36 | font-size: 15px; 37 | } 38 | -------------------------------------------------------------------------------- /src/webextension/settings/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 | // keep in sync with 6 | 7 | import { combineReducers } from "redux"; 8 | 9 | import { 10 | SHOW_MODAL, HIDE_MODAL, 11 | } from "./actions"; 12 | 13 | export function modalReducer(state = {id: null, props: null}, action) { 14 | switch (action.type) { 15 | case SHOW_MODAL: 16 | return {...state, id: action.id, props: action.props}; 17 | case HIDE_MODAL: 18 | return {...state, id: null, props: null}; 19 | default: 20 | return state; 21 | } 22 | } 23 | 24 | const reducer = combineReducers({ 25 | modal: modalReducer, 26 | }); 27 | 28 | export default reducer; 29 | -------------------------------------------------------------------------------- /src/webextension/widgets/field-text.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 "./input.css"; 11 | 12 | export default function FieldText({className, monospace, ...props}) { 13 | return ( 14 | 17 | ); 18 | } 19 | 20 | FieldText.propTypes = { 21 | className: PropTypes.string, 22 | monospace: PropTypes.bool, 23 | }; 24 | 25 | FieldText.defaultProps = { 26 | className: "", 27 | monospace: false, 28 | }; 29 | -------------------------------------------------------------------------------- /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-extension 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 | - 'FAQs': 'faqs.md' 17 | - 'Contribute': 'contributing.md' 18 | - 'Telemetry and Metrics': 'metrics.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/webextension/unlock/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 | 8 | import AppLocalizationProvider from "../l10n"; 9 | import App from "./components/app"; 10 | import * as telemetry from "../telemetry"; 11 | 12 | // This is the closest approximation we can get to a toolbar click when the 13 | // popup is registered. 14 | telemetry.recordEvent("render", "popupUnlock"); 15 | 16 | ReactDOM.render( 17 | 19 | 20 | , 21 | document.getElementById("content") 22 | ); 23 | -------------------------------------------------------------------------------- /src/template.ejs: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | <% const path = require("path"); 9 | const dir = path.dirname(htmlWebpackPlugin.options.filename); 10 | for (let css of htmlWebpackPlugin.files.css) { 11 | %> 12 | 13 | <% } for (let js of htmlWebpackPlugin.files.js) { %> 14 | 15 | <% } %> 16 | 17 | 18 | 19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | *Please note: your idea may have already been considered and you may find it in an issue by searching this repository. Also, this project is not currently or actively planning to implement new features as it is an experimental prototype.* 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /src/webextension/list/components/item-fields.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-fields { 6 | display: grid; 7 | grid-template-columns: minmax(200px, 1fr); 8 | max-width: 360px; 9 | } 10 | 11 | .item-fields > label, 12 | .item-fields > .field { 13 | display: contents; 14 | } 15 | 16 | .item-fields textarea { 17 | resize: none; 18 | } 19 | 20 | .input { 21 | max-width: 242px; 22 | } 23 | 24 | .password { 25 | max-width: 258px; 26 | } 27 | 28 | .notes { 29 | min-height: 60px; 30 | } 31 | 32 | .notes-read-only { 33 | min-height: 73px; 34 | } 35 | 36 | .first-label { 37 | margin-top: 0; 38 | } 39 | 40 | .inline-button { 41 | display: flex; 42 | } 43 | 44 | .inline-button > *:first-child { 45 | flex: 1; 46 | } 47 | -------------------------------------------------------------------------------- /src/webextension/manifest.json.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "{{title}}", 4 | "version": "{{version}}", 5 | 6 | "description": "{{description}}", 7 | 8 | "applications": { 9 | "gecko": { 10 | "id": "{{id}}" 11 | } 12 | }, 13 | 14 | "background": { 15 | "scripts": ["background.js"] 16 | }, 17 | 18 | "browser_action": { 19 | "default_icon": { 20 | "32": "icons/lb_locked.svg" 21 | }, 22 | "default_title": "Lockbox", 23 | "browser_style": false 24 | }, 25 | 26 | "commands": { 27 | "_execute_browser_action": { 28 | "suggested_key": { 29 | "default": "Ctrl+Shift+L" 30 | } 31 | } 32 | }, 33 | 34 | "permissions": [ 35 | "identity", 36 | "storage", 37 | "tabs", 38 | "clipboardWrite" 39 | ], 40 | 41 | "options_ui": { 42 | "page": "settings/index.html", 43 | "browser_style": false 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/unit/firstrun/components/app-test.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 chai, { expect } from "chai"; 6 | import chaiEnzyme from "chai-enzyme"; 7 | import React from "react"; 8 | 9 | import mountWithL10n from "test/mocks/l10n"; 10 | import App from "src/webextension/firstrun/components/app"; 11 | import Intro from "src/webextension/firstrun/components/intro"; 12 | import Using from "src/webextension/firstrun/components/using"; 13 | 14 | chai.use(chaiEnzyme()); 15 | 16 | describe("firstrun > components > ", () => { 17 | it("render app", () => { 18 | const wrapper = mountWithL10n( 19 | 20 | ); 21 | 22 | expect(wrapper).to.contain(Intro); 23 | expect(wrapper).to.contain(Using); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/unit/settings/reducers-test.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 { expect } from "chai"; 6 | 7 | import reducer, { 8 | modalReducer, 9 | } from "src/webextension/settings/reducers"; 10 | 11 | describe("settings > reducers", () => { 12 | describe("modal reducer", () => { 13 | it("initial state", () => { 14 | expect(modalReducer(undefined, {})).to.deep.equal({ 15 | id: null, 16 | props: null, 17 | }); 18 | }); 19 | }); 20 | 21 | describe("<> reducer", () => { 22 | it("initial state", () => { 23 | expect(reducer(undefined, {})).to.deep.equal({ 24 | modal: { 25 | id: null, 26 | props: null, 27 | }, 28 | }); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/unit/list/manage/components/account-linked-test.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 chai, { expect } from "chai"; 6 | import chaiEnzyme from "chai-enzyme"; 7 | import React from "react"; 8 | 9 | import mountWithL10n from "test/mocks/l10n"; 10 | import AccountLinked from "src/webextension/list/manage/components/account-linked"; 11 | 12 | chai.use(chaiEnzyme()); 13 | 14 | describe("list > manage > components > ", () => { 15 | it("render", () => { 16 | const wrapper = mountWithL10n( 17 | 18 | ); 19 | 20 | expect(wrapper.find("h2")).to.have.text("aCCOUNt lINKEd"); 21 | expect(wrapper.find("p")).to.have.text("Praesent commodo cursus magna, vel scelerisque nisl consectetur et."); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report to help us improve 4 | 5 | --- 6 | 7 | *Please note: this project is not currently or actively planning to fix non-critical (data loss, security related) bugs. This is an experimental prototype.* 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 1. Go to '...' 15 | 2. Click on '....' 16 | 3. Scroll down to '....' 17 | 4. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Desktop (please complete the following information):** 26 | - OS: [e.g. iOS] 27 | - Browser version [e.g. 62, Nightly 63.a.01) 28 | - Version [e.g. 22] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | let extraPlugins = []; 2 | if (process.env.STRICT_LINT !== "1") { 3 | extraPlugins.push("only-warn"); 4 | } 5 | 6 | module.exports = { 7 | "parserOptions": { 8 | "ecmaVersion": 2017, 9 | "sourceType": "module", 10 | "ecmaFeatures": { 11 | "jsx": true, 12 | "experimentalObjectRestSpread": true, 13 | }, 14 | }, 15 | "extends": [ 16 | "eslint:recommended", 17 | "plugin:mozilla/recommended", 18 | "plugin:react/recommended", 19 | ], 20 | "env": { 21 | "browser": true, 22 | "node": true, 23 | "webextensions": true, 24 | "es6": true, 25 | }, 26 | "plugins": [ 27 | "mozilla", 28 | "react", 29 | ...extraPlugins, 30 | ], 31 | "rules": { 32 | "comma-dangle": ["error", "always-multiline"], 33 | "curly": "error", 34 | "no-console": "warn", 35 | "semi": "error", 36 | "mozilla/no-define-cc-etc": "off", 37 | "mozilla/use-chromeutils-import": "off", 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /src/webextension/list/manage/components/homepage.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 | 8 | import AccountDetails from "../containers/account-details"; 9 | 10 | import styles from "./homepage.css"; 11 | 12 | export default function Homepage() { 13 | const imgSrc = browser.extension.getURL("/images/nessie_v2.svg"); 14 | 15 | return ( 16 |
17 | 18 | 19 |

tHe sIMPLe wAy tO sTORE...

20 |
21 | 22 |

uNMAINTAINEd dISCLAIMEr

23 |
24 | 25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/webextension/list/manage/containers/current-account-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 { connect } from "react-redux"; 6 | 7 | import AccountSummary from "../components/account-summary"; 8 | import { openAccountPage, openOptions, signout } from "../../actions"; 9 | 10 | export default connect( 11 | (state) => { 12 | if (state.account.mode === "authenticated") { 13 | return { 14 | displayName: state.account.displayName, 15 | avatar: state.account.avatar, 16 | }; 17 | } 18 | return {}; 19 | }, 20 | (dispatch) => ({ 21 | onClickAccount: () => { dispatch(openAccountPage()); }, 22 | onClickOptions: () => { dispatch(openOptions()); }, 23 | onClickSignout: () => { dispatch(signout()); }, 24 | }), 25 | )(AccountSummary); 26 | -------------------------------------------------------------------------------- /src/webextension/icons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/webextension/firstrun/components/app.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 DocumentTitle from "react-document-title"; 8 | 9 | import Intro from "./intro"; 10 | import StartUsing from "./using"; 11 | 12 | import styles from "./app.css"; 13 | 14 | export default function App() { 15 | const imgSrc = browser.extension.getURL("/images/nessie_v2.svg"); 16 | 17 | return ( 18 | 19 | 20 |
21 | 22 | 23 | 24 |
25 |
26 |
27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /src/webextension/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 | return { 11 | id, 12 | title: item.title, 13 | origins: item.origin ? [item.origin] : [], 14 | entry: { 15 | kind: "login", 16 | username: item.username, 17 | password: item.password, 18 | notes: item.notes, 19 | }, 20 | }; 21 | } 22 | 23 | export function flattenItem(item) { 24 | return { 25 | title: item.title, 26 | origin: item.origins[0] || "", 27 | username: item.entry.username, 28 | password: item.entry.password, 29 | notes: item.entry.notes, 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /src/webextension/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 * as telemetry from "../../telemetry"; 7 | 8 | export default (store) => (next) => (action) => { 9 | try { 10 | switch (action.type) { 11 | case actions.SELECT_ITEM_COMPLETED: 12 | if (action.item) { 13 | telemetry.recordEvent("itemSelected", "doorhanger", 14 | {itemid: action.item.id}); 15 | } 16 | break; 17 | case actions.COPIED_FIELD: 18 | telemetry.recordEvent(`${action.field}Copied`, "doorhanger"); 19 | break; 20 | } 21 | } catch (e) { 22 | // eslint-disable-next-line no-console 23 | console.error("Unable to record telemetry event", e); 24 | } 25 | 26 | return next(action); 27 | }; 28 | -------------------------------------------------------------------------------- /src/webextension/list/manage/components/link-account.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 | .link { 6 | display: flex; 7 | flex-direction: column; 8 | align-items: center; 9 | margin: 2em auto; 10 | padding: 0; 11 | max-width: 500px; 12 | } 13 | 14 | .link > h2, 15 | .link > p { 16 | text-align: center; 17 | letter-spacing: 0.2px; 18 | line-height: 1.5; 19 | } 20 | 21 | .link > h2 { 22 | margin: 0 0 .5em; 23 | padding: 0; 24 | font-size: 17px; 25 | font-weight: 600; 26 | line-height: 1.32; 27 | } 28 | 29 | .link > p { 30 | font-size: 15px; 31 | margin: 0 0 0.5em; 32 | } 33 | 34 | .link > menu { 35 | display: flex; 36 | flex-wrap: wrap; 37 | justify-content: center; 38 | margin: 0; 39 | padding: 0; 40 | } 41 | 42 | .link > menu > button { 43 | margin: 0.5em; 44 | } 45 | -------------------------------------------------------------------------------- /src/webextension/settings/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 reducer from "./reducers"; 14 | 15 | const store = createStore(reducer, undefined, applyMiddleware(thunk)); 16 | 17 | ReactDOM.render( 18 | 19 | 21 | 22 | 23 | , 24 | document.getElementById("content") 25 | ); 26 | -------------------------------------------------------------------------------- /src/webextension/background/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 openDataStore, { DEFAULT_APP_KEY } from "./datastore"; 6 | import { openAccount, GUEST } from "./accounts"; 7 | import initializeMessagePorts from "./message-ports"; 8 | import updateBrowserAction from "./browser-action"; 9 | 10 | openAccount(browser.storage.local).then(async (account) => { 11 | let datastore = await openDataStore({ salt: account.uid }); 12 | if (datastore.initialized && account.mode === GUEST) { 13 | try { 14 | await datastore.unlock(DEFAULT_APP_KEY); 15 | } catch (err) { 16 | // eslint-disable-next-line no-console 17 | console.error("datastore is in an inconsistent state."); 18 | } 19 | } 20 | 21 | initializeMessagePorts(); 22 | await updateBrowserAction({account, datastore}); 23 | }); 24 | -------------------------------------------------------------------------------- /src/webextension/icons/hide.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/webextension/list/manage/components/intro-page.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 | .intro-images { 6 | display: grid; 7 | grid-auto-flow: column; 8 | grid-template-columns: 1fr 1fr 1fr; 9 | grid-template-rows: auto auto auto; 10 | grid-column-gap: 4em; 11 | margin: 8em auto 4em; 12 | padding: 0 4em; 13 | max-width: 880px; 14 | } 15 | 16 | .intro-image { 17 | display: contents; 18 | text-align: center; 19 | } 20 | 21 | .intro-image > h1, 22 | .intro-image > p { 23 | overflow: hidden; 24 | } 25 | 26 | .intro-image > img { 27 | width: 100%; 28 | border: 1px solid #d7d7db; 29 | border-radius: 5px; 30 | box-shadow: 0 1px 4px rgba(12, 12, 13, 0.1); 31 | } 32 | 33 | .intro-image > h1 { 34 | font-size: 17px; 35 | margin-bottom: 0; 36 | } 37 | 38 | .intro-image > p { 39 | font-size: 15px; 40 | line-height: 1.5; 41 | } 42 | -------------------------------------------------------------------------------- /src/webextension/list/manage/containers/open-faq.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 { ExternalLink } from "../../../widgets/link"; 11 | import { openFAQ } from "../../actions"; 12 | 13 | import styles from "./open-faq.css"; 14 | 15 | function OpenFAQ({onOpenFAQ}) { 16 | return ( 17 | 18 | 19 | fAq 20 | 21 | 22 | ); 23 | } 24 | 25 | OpenFAQ.propTypes = { 26 | onOpenFAQ: PropTypes.func.isRequired, 27 | }; 28 | 29 | export default connect( 30 | undefined, 31 | (dispatch) => ({ 32 | onOpenFAQ: () => { dispatch(openFAQ()); }, 33 | }) 34 | )(OpenFAQ); 35 | -------------------------------------------------------------------------------- /src/webextension/list/popup/components/app.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 DocumentTitle from "react-document-title"; 8 | 9 | import CurrentSelection from "../containers/current-selection"; 10 | 11 | import styles from "./app.css"; 12 | 13 | export default class App extends React.Component { 14 | componentDidMount() { 15 | this._filterField.focus(true); 16 | } 17 | 18 | render() { 19 | return ( 20 | 21 | 22 |
23 | { 24 | this._filterField = element; 25 | }}/> 26 |
27 |
28 |
29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/webextension/widgets/link.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 | .link { 6 | border: 0; 7 | padding: 0; 8 | text-decoration: none; 9 | color: #0060df; 10 | background: transparent; 11 | border-radius: 2px; 12 | cursor: pointer; 13 | } 14 | 15 | .link::-moz-focus-inner { 16 | border: none; 17 | } 18 | 19 | .link:hover { 20 | text-decoration: underline; 21 | } 22 | 23 | .link:active { 24 | color: #003eaa; 25 | text-decoration: underline; 26 | } 27 | 28 | .link:focus { 29 | box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3); 30 | } 31 | 32 | .external::after { 33 | background-image: url(/icons/external-link.svg); 34 | background-repeat: no-repeat; 35 | background-size: 16px 16px; 36 | content: ""; 37 | display: inline-block; 38 | height: 16px; 39 | margin: -.3rem .15rem 0 .25rem; 40 | vertical-align: middle; 41 | width: 16px; 42 | } 43 | -------------------------------------------------------------------------------- /test/unit/widgets/label-text-test.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 chai, { expect } from "chai"; 6 | import chaiEnzyme from "chai-enzyme"; 7 | import React from "react"; 8 | 9 | import { mount } from "test/enzyme"; 10 | import LabelText from "src/webextension/widgets/label-text"; 11 | 12 | chai.use(chaiEnzyme()); 13 | 14 | describe("widgets > ", () => { 15 | it("render text", () => { 16 | const wrapper = mount(hi); 17 | expect(wrapper.find("span")).to.have.text("hi"); 18 | expect(wrapper.find("span").prop("className")).to.match( 19 | /^\S+label-text\S+$/ 20 | ); 21 | }); 22 | 23 | it("merge classNames", () => { 24 | const wrapper = mount(hi); 25 | expect(wrapper.find("span").prop("className")).to.match( 26 | /^\S+label-text\S+ foo$/ 27 | ); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/unit/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 | require("babel-polyfill"); 6 | 7 | const tests = require.context(".", true, /-test\.js$/); 8 | tests.keys().forEach(tests); 9 | 10 | // This is super-brittle, but should ensure that all (well, most!) source files 11 | // are loaded so we have something approaching accurate coverage. Unfortunately, 12 | // the combination of karma, webpack, and babel makes coverage inordinantly 13 | // difficult to get. 14 | const skippedSrc = [ 15 | "./webextension/background/index.js", 16 | "./webextension/firstrun/index.js", 17 | "./webextension/list/manage/index.js", 18 | "./webextension/list/popup/index.js", 19 | "./webextension/unlock/index.js", 20 | "./webextension/settings/index.js", 21 | ]; 22 | 23 | const src = require.context("src", true, /\.js$/); 24 | src.keys().filter((x) => { 25 | return !skippedSrc.includes(x); 26 | }).forEach(src); 27 | -------------------------------------------------------------------------------- /src/webextension/settings/containers/modals.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 ModalRoot from "../../widgets/modal-root"; 8 | import { LocalizedConfirmDialog } from "../../widgets/dialog-box"; 9 | import { hideModal, localReset } from "../actions"; 10 | 11 | export const LocalResetModal = connect( 12 | (state) => ({ 13 | l10nId: "modal-local-reset", 14 | theme: "danger", 15 | }), 16 | (dispatch) => ({ 17 | onConfirm: () => { dispatch(localReset()); }, 18 | }) 19 | )(LocalizedConfirmDialog); 20 | 21 | const MODALS = { 22 | "local-reset": LocalResetModal, 23 | }; 24 | 25 | export default connect( 26 | (state) => ({ 27 | modals: MODALS, 28 | modalId: state.modal.id, 29 | modalProps: state.modal.props, 30 | }), 31 | (dispatch) => ({ 32 | onClose: () => { dispatch(hideModal()); }, 33 | }) 34 | )(ModalRoot); 35 | -------------------------------------------------------------------------------- /src/webextension/widgets/toolbar.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 "./toolbar.css"; 11 | 12 | export default function Toolbar({className, children}) { 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | } 19 | 20 | Toolbar.propTypes = { 21 | className: PropTypes.string, 22 | children: PropTypes.node, 23 | }; 24 | 25 | Toolbar.defaultProps = { 26 | className: "", 27 | }; 28 | 29 | export function ToolbarSpace({className}) { 30 | return ( 31 | 32 | ); 33 | } 34 | 35 | ToolbarSpace.propTypes = { 36 | className: PropTypes.string, 37 | }; 38 | 39 | ToolbarSpace.defaultProps = { 40 | className: "", 41 | }; 42 | -------------------------------------------------------------------------------- /test/unit/mocks/xpcom.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 | window.ADDON_INSTALL = "ADDON_INSTALL"; 6 | window.ADDON_UPGRADE = "ADDON_UPGRADE"; 7 | window.ADDON_UNINSTALL = "ADDON_UNINSTALL"; 8 | 9 | window.Components = { 10 | utils: { 11 | "import": () => { }, 12 | }, 13 | }; 14 | 15 | window.Services = { 16 | prefs: { 17 | _prefs: {}, 18 | 19 | getBoolPref(key) { 20 | return this._prefs[key] || false; 21 | }, 22 | 23 | setBoolPref(key, value) { 24 | this._prefs[key] = value; 25 | }, 26 | 27 | clearUserPref(key) { 28 | delete this._prefs[key]; 29 | }, 30 | 31 | prefHasUserValue(key) { 32 | return key in this._prefs; 33 | }, 34 | 35 | mockResetPrefs() { 36 | this._prefs = {}; 37 | }, 38 | }, 39 | 40 | telemetry: { 41 | registerEvents() {}, 42 | recordEvent() {}, 43 | registerScalars() {}, 44 | scalarSet() {}, 45 | }, 46 | }; 47 | -------------------------------------------------------------------------------- /src/webextension/list/manage/components/app.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 | body { 6 | padding: 0; 7 | margin: 0; 8 | color: #0c0c0d; 9 | background-color: #f9f9fa; 10 | font: caption; 11 | font-size: 13px; 12 | -moz-user-select: none; 13 | overflow: hidden; 14 | } 15 | 16 | html, 17 | body, 18 | body > main, 19 | .app { 20 | height: 100%; 21 | } 22 | 23 | .app-main { 24 | display: grid; 25 | height: 100%; 26 | grid-template-columns: 1fr 3fr; 27 | } 28 | 29 | .navigation { 30 | height: 48px; 31 | padding-inline-start: 4.6em; 32 | } 33 | 34 | .navigation > * + * { 35 | margin-inline-start: 40px; 36 | } 37 | 38 | .aside { 39 | grid-column: 1; 40 | min-width: 150px; 41 | min-height: 0; 42 | display: flex; 43 | flex-direction: column; 44 | border-inline-end: 1px solid #d7d7db; 45 | } 46 | 47 | .app-main > article { 48 | grid-column: 2; 49 | background-color: #f9f9fa; 50 | overflow: auto; 51 | } 52 | -------------------------------------------------------------------------------- /src/webextension/unlock/components/app.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 | body { 6 | padding: 0; 7 | margin: 0; 8 | width: 280px; 9 | height: 360px; 10 | color: #0c0c0d; 11 | background-color: #ededf0; 12 | font: caption; 13 | font-size: 13px; 14 | -moz-user-select: none; 15 | } 16 | 17 | body > main { 18 | height: 100%; 19 | } 20 | 21 | .lockie { 22 | display: block; 23 | margin: 4em auto 0; 24 | width: 200px; 25 | } 26 | 27 | .unlock-content { 28 | margin: 1em auto 3em; 29 | width: 240px; 30 | display: flex; 31 | flex-flow: column nowrap; 32 | align-items: center; 33 | } 34 | 35 | .unlock-content > h1 { 36 | font-size: 2.5em; 37 | font-weight: 300; 38 | text-align: center; 39 | margin: 0 0 0.25em; 40 | } 41 | 42 | .unlock-content > h2 { 43 | font-size: 1.15em; 44 | font-weight: 300; 45 | font-style: italic; 46 | text-align: center; 47 | text-transform: lowercase; 48 | margin: 0; 49 | } 50 | -------------------------------------------------------------------------------- /test/integration/tests/test_guest_mode.py: -------------------------------------------------------------------------------- 1 | """Tests for guest mode.""" 2 | 3 | 4 | def test_guest_login(login_page): 5 | """Log into Lockbox.""" 6 | home_page = login_page.click_get_started() 7 | assert home_page.sign_in_button_is_displayed() 8 | 9 | 10 | def test_add_entry_as_guest(home_page): 11 | """Add a new entry.""" 12 | home_page.create_new_entry("Tuna, Inc.", "https://satuna.org", "tuna4life", 13 | "tunafish", "The tuna swim at midnight") 14 | 15 | entry = home_page.entries[0] 16 | assert len(home_page.entries) == 1 17 | assert 'Tuna, Inc.' in entry.name 18 | detail = entry.click() 19 | detail.edit() 20 | assert 'https://satuna.org' in detail.site_url 21 | assert 'tuna4life' in detail.username 22 | assert 'tunafish' in detail.password 23 | assert 'The tuna swim at midnight' in detail.note 24 | 25 | 26 | def test_delete_entry_as_guest(home_page): 27 | """Test Deleting an entry.""" 28 | home_page.create_new_entry() 29 | 30 | entry = home_page.entries[0].click() 31 | entry.delete() 32 | assert len(home_page.entries) == 0 33 | -------------------------------------------------------------------------------- /src/webextension/background/datastore.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 DataStore from "lockbox-datastore"; 6 | import * as telemetry from "./telemetry"; 7 | 8 | let datastore; 9 | 10 | async function recordMetric(method, itemid, fields) { 11 | let extra = { 12 | itemid, 13 | }; 14 | if (fields) { 15 | extra = { 16 | ...extra, 17 | fields, 18 | }; 19 | } 20 | telemetry.recordEvent(method, "datastore", extra); 21 | } 22 | 23 | export const DEFAULT_APP_KEY = { 24 | "kty": "oct", 25 | "kid": "L9-eBkDrYHdPdXV_ymuzy_u9n3drkQcSw5pskrNl4pg", 26 | "k": "WsTdZ2tjji2W36JN9vk9s2AYsvp8eYy1pBbKPgcSLL4", 27 | }; 28 | 29 | export function clearDataStore() { 30 | datastore = undefined; 31 | } 32 | 33 | export default async function openDataStore(cfg = {}) { 34 | if (!datastore) { 35 | datastore = await DataStore.open({ 36 | ...cfg, 37 | recordMetric, 38 | }); 39 | } 40 | return datastore; 41 | } 42 | -------------------------------------------------------------------------------- /src/webextension/list/message-ports.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 { accountDetailsUpdated, addedItem, updatedItem, removedItem } from "./actions"; 6 | 7 | let messagePort; 8 | 9 | export default function initializeMessagePorts(store) { 10 | // Listen for changes to the datastore from other sources and dispatch actions 11 | // to sync those changes with our Redux store. 12 | messagePort = browser.runtime.connect(); 13 | messagePort.onMessage.addListener((message) => { 14 | switch (message.type) { 15 | case "account_details_updated": 16 | store.dispatch(accountDetailsUpdated(message.account)); 17 | break; 18 | case "added_item": 19 | store.dispatch(addedItem(message.item)); 20 | break; 21 | case "updated_item": 22 | store.dispatch(updatedItem(message.item)); 23 | break; 24 | case "removed_item": 25 | store.dispatch(removedItem(message.id)); 26 | break; 27 | } 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /src/webextension/list/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 | export function parseFilterString(filter) { 6 | filter = filter.trim(); 7 | if (!filter) { 8 | return []; 9 | } 10 | return filter.split(/\s+/).map((i) => i.toLocaleLowerCase()); 11 | } 12 | 13 | function match(filterElement, value) { 14 | return value.includes(filterElement); 15 | } 16 | 17 | function matchAny(filterElement, values) { 18 | for (let i of values) { 19 | if (match(filterElement, i)) { 20 | return true; 21 | } 22 | } 23 | return false; 24 | } 25 | 26 | export function filterItem(filter, item) { 27 | const title = item.title.toLocaleLowerCase(); 28 | const username = item.username.toLocaleLowerCase(); 29 | const origins = item.origins.map((i) => i.toLocaleLowerCase()); 30 | 31 | for (let i of filter) { 32 | if (!match(i, title) && !match(i, username) && !matchAny(i, origins)) { 33 | return false; 34 | } 35 | } 36 | return true; 37 | } 38 | -------------------------------------------------------------------------------- /src/webextension/icons/clear.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/webextension/list/manage/containers/send-feedback.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 { PanelFooterButton } from "../../../widgets/panel"; 11 | import { sendFeedback } from "../../actions"; 12 | 13 | import styles from "./send-feedback.css"; 14 | 15 | function SendFeedback({onSendFeedback}) { 16 | return ( 17 | 18 | 20 | fEEDBACk 21 | 22 | 23 | ); 24 | } 25 | 26 | SendFeedback.propTypes = { 27 | onSendFeedback: PropTypes.func.isRequired, 28 | }; 29 | 30 | export default connect( 31 | undefined, 32 | (dispatch) => ({ 33 | onSendFeedback: () => { dispatch(sendFeedback()); }, 34 | }) 35 | )(SendFeedback); 36 | -------------------------------------------------------------------------------- /src/webextension/widgets/modal-root.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 Modal from "react-modal"; 9 | 10 | import styles from "./modal-root.css"; 11 | 12 | export default function ModalRoot({modals, modalId, modalProps, onClose}) { 13 | let modal = null; 14 | if (modalId) { 15 | const CurrentModal = modals[modalId]; 16 | modal = ; 17 | } 18 | 19 | return ( 20 | 21 | 23 | {modal} 24 | 25 | 26 | ); 27 | } 28 | 29 | ModalRoot.propTypes = { 30 | modals: PropTypes.objectOf(PropTypes.func).isRequired, 31 | modalId: PropTypes.string, 32 | modalProps: PropTypes.object, 33 | onClose: PropTypes.func.isRequired, 34 | }; 35 | -------------------------------------------------------------------------------- /src/webextension/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/webextension/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 } from "../../../widgets/panel"; 10 | import { ItemFields } from "../../components/item-fields"; 11 | 12 | import styles from "./item-details-panel.css"; 13 | 14 | export default function ItemDetailsPanel({fields, onCopy, onBack}) { 15 | return ( 16 | 17 | 18 | 19 | eNTRy dETAILs 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | } 29 | 30 | ItemDetailsPanel.propTypes = { 31 | ...ItemFields.propTypes, 32 | onCopy: PropTypes.func.isRequired, 33 | onBack: PropTypes.func.isRequired, 34 | }; 35 | -------------------------------------------------------------------------------- /test/unit/telemetry-test.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 chai, { expect } from "chai"; 6 | import sinon from "sinon"; 7 | import sinonChai from "sinon-chai"; 8 | 9 | import "test/mocks/browser"; 10 | import * as telemetry from "src/webextension/telemetry"; 11 | 12 | chai.use(sinonChai); 13 | 14 | describe("telemetry", () => { 15 | let onMessage; 16 | 17 | beforeEach(() => { 18 | browser.runtime.onMessage.addListener(onMessage = sinon.stub().returns({})); 19 | }); 20 | 21 | afterEach(() => { 22 | browser.runtime.onMessage.mockClearListener(); 23 | }); 24 | 25 | it("recordEvent()", async () => { 26 | const result = await telemetry.recordEvent("method", "object", 27 | {extra: "value"}); 28 | expect(result).to.deep.equal({}); 29 | expect(onMessage).to.have.been.calledWith({ 30 | type: "proxy_telemetry_event", 31 | method: "method", 32 | object: "object", 33 | extra: {extra: "value"}, 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /src/webextension/widgets/text-area.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 "./text-area.css"; 11 | 12 | export default class TextArea extends React.Component { 13 | static get propTypes() { 14 | return { 15 | className: PropTypes.string, 16 | }; 17 | } 18 | 19 | static get defaultProps() { 20 | return { 21 | className: "", 22 | }; 23 | } 24 | 25 | focus(select = false) { 26 | this.textAreaElement.focus(); 27 | if (select) { 28 | this.textAreaElement.setSelectionRange( 29 | 0, this.textAreaElement.value.length 30 | ); 31 | } 32 | } 33 | 34 | render() { 35 | const {className, ...props} = this.props; 36 | return ( 37 |