Compare commits

..

No commits in common. "develop" and "1.9.9.3" have entirely different histories.

81 changed files with 1814 additions and 5311 deletions

197
.eslintrc Normal file
View file

@ -0,0 +1,197 @@
{
"extends": "eslint:recommended",
"ignorePatterns": ["**/*.min.js"],
"env": {
"browser": true,
"es2022": true,
"jquery": true,
"webextensions": true
},
"parserOptions": {
"ecmaVersion": 13
},
"rules": {
"array-bracket-spacing": ["error", "always"],
"arrow-body-style": "warn",
"arrow-parens": "off",
"arrow-spacing": "warn",
"block-scoped-var": "off",
"brace-style": ["warn", "1tbs"],
"camelcase": "warn",
"class-methods-use-this": "off",
"comma-dangle": "off",
"computed-property-spacing": ["error", "never"],
"consistent-return": "off",
"dot-notation": "off",
"eqeqeq": "error",
"func-names": "off",
"function-paren-newline": "off",
"guard-for-in": "off",
"implicit-arrow-linebreak": "off",
"indent": ["error", 4],
"linebreak-style": ["error", "unix"],
"lines-around-comment": "off",
"max-len": "off",
"no-alert": "error",
"no-await-in-loop": "off",
"no-bitwise": "off",
"no-console": "off",
"no-continue": "off",
"no-control-regex": "off",
"no-else-return": "off",
"no-empty-function": ["error", { "allow": ["arrowFunctions", "methods", "asyncMethods"] }],
"no-extend-native": "off",
"no-global-assign": "off",
"no-lonely-if": "off",
"no-loop-func": "off",
"no-mixed-operators": "off",
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
"no-multi-spaces": "warn",
"no-param-reassign": "off",
"no-plusplus": "off",
"no-prototype-builtins": "off",
"no-redeclare": "off",
"no-restricted-globals": "off",
"no-restricted-syntax": "off",
"no-return-await": "off",
"no-tabs": "warn",
"no-underscore-dangle": "off",
"no-unused-vars": ["warn", { "args": "none", "varsIgnorePattern": "[A-Z_]+|tr|[$]" }],
"no-use-before-define": "off",
"no-useless-escape": "off",
"no-useless-return": "off",
"no-var": "error",
"object-curly-spacing": ["warn", "always"],
"object-shorthand": "off",
"operator-linebreak": "off",
"prefer-arrow-callback": "off",
"prefer-const": "warn",
"prefer-destructuring": "off",
"prefer-template": "off",
"quote-props": "off",
"quotes": ["error", "single", { "avoidEscape": true }],
"semi": ["error", "always"],
"space-before-blocks": "warn",
"space-before-function-paren": "off",
"space-in-parens": ["error", "never"],
"space-unary-ops": "off",
"spaced-comment": "off",
"strict": "off",
"vars-on-top": "off"
},
"globals": {
"_called": "readonly",
"acceptedOTPFields": "readonly",
"assertInputFields": "readonly",
"assertPasswordChangeFields": "readonly",
"assertRegex": "readonly",
"assertSearchField": "readonly",
"assertSearchForm": "readonly",
"assertTOTPField": "readonly",
"AssociatedAction": "readonly",
"AuthenticatorAssertionResponse": "readonly",
"AuthenticatorAttestationResponse": "readonly",
"Autocomplete": "readonly",
"BannerPosition": "readonly",
"BLUE_BUTTON": "readonly",
"bootstrap": "readonly",
"browser": "readonly",
"browserAction": "readonly",
"CHECK_UPDATE_NEVER": "readonly",
"CHECK_UPDATE_ONE_MONTH": "readonly",
"CHECK_UPDATE_ONE_WEEK": "readonly",
"CHECK_UPDATE_THREE_DAYS": "readonly",
"cloneInto": "readonly",
"compareVersion": "readonly",
"createResult": "readonly",
"createStylesheet": "readonly",
"DatabaseState": "readonly",
"debugLogMessage": "readonly",
"elementsOverlap": "readonly",
"EXTENSION_NAME": "readonly",
"getCurrentTab": "readonly",
"getLoginData": "readonly",
"getTopLevelDomainFromUrl": "readonly",
"GRAY_BUTTON_CLASS": "readonly",
"GREEN_BUTTON": "readonly",
"httpAuth": "readonly",
"Icon": "readonly",
"IGNORE_AUTOSUBMIT": "readonly",
"IGNORE_FULL": "readonly",
"IGNORE_NORMAL": "readonly",
"IGNORE_NOTHING": "readonly",
"importScripts": "readonly",
"IMPROVED_DETECTION_PREDEFINED_SITELIST": "readonly",
"initColorTheme": "readonly",
"isEdge": "readonly",
"isFirefox": "readonly",
"keepass": "readonly",
"keepassClient": "readonly",
"kpActions": "readonly",
"kpErrors": "readonly",
"kpxc": "readonly",
"kpxcAssert": "readonly",
"kpxcBanner": "readonly",
"kpxcCustomLoginFieldsBanner": "readonly",
"kpxcEvent": "readonly",
"kpxcFields": "readonly",
"kpxcFill": "readonly",
"kpxcForm": "readonly",
"kpxcIcons": "readonly",
"kpxcObserverHelper": "readonly",
"kpxcPasswordGenerator": "readonly",
"kpxcPasswordIcons": "readonly",
"kpxcSites": "readonly",
"kpxcTOTPAutocomplete": "readonly",
"kpxcTOTPIcons": "readonly",
"kpxcUI": "readonly",
"kpxcUserAutocomplete": "readonly",
"kpxcUsernameIcons": "readonly",
"logDebug": "readonly",
"logError": "readonly",
"kpxcPasskeysUtils": "readonly",
"ManualFill": "readonly",
"matchesWithNodeName": "readonly",
"MAX_AUTOCOMPLETE_NAME_LEN": "readonly",
"MAX_OPACITY": "readonly",
"MAX_TOTP_INPUT_LENGTH": "readonly",
"menuContexts": "readonly",
"MIN_INPUT_FIELD_OFFSET_WIDTH": "readonly",
"MIN_INPUT_FIELD_WIDTH_PX": "readonly",
"MIN_OPACITY": "readonly",
"MIN_TOTP_INPUT_LENGTH": "readonly",
"module": "readonly",
"nacl": "readonly",
"OBSERVER_OPTIONS": "readonly",
"ORANGE_BUTTON": "readonly",
"page": "readonly",
"Pixels": "readonly",
"PublicKeyCredential": "readonly",
"PREDEFINED_SITELIST": "readonly",
"RED_BUTTON": "readonly",
"retrieveColorScheme": "readonly",
"sendMessage": "readonly",
"showNotification": "readonly",
"siteMatch": "readonly",
"slashNeededForUrl": "readonly",
"SORT_BY_GROUP_AND_TITLE": "readonly",
"SORT_BY_GROUP_AND_USERNAME": "readonly",
"SORT_BY_MATCHING_CREDENTIALS_SETTING": "readonly",
"SORT_BY_RELEVANT_ENTRY": "readonly",
"SORT_BY_TITLE": "readonly",
"SORT_BY_USERNAME": "readonly",
"statusResponse": "readonly",
"Tests": "readonly",
"tr": "readonly",
"trimURL": "readonly",
"updateDefaultPasswordManager": "readonly"
},
"overrides": [
{
"files": ["./*.js"],
"env": {
"node": true
}
}
]
}

View file

@ -1,196 +0,0 @@
# Contributing to KeePassXC-Browser
The following is a set of guidelines for contributing to KeePassXC-Browser on GitHub.
These are just guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
#### Table of contents
[What should I know before I get started?](#what-should-i-know-before-i-get-started)
* [Open Source Contribution Policy](#open-source-contribution-policy)
[How can I contribute?](#how-can-i-contribute)
* [Feature requests](#feature-requests)
* [Bug reports](#bug-reports)
* [Discuss with the team](#discuss-with-the-team)
* [Your first code contribution](#your-first-code-contribution)
* [Using AI](#using-ai)
* [Pull requests](#pull-requests)
* [Translations](#translations)
[Styleguides](#styleguides)
* [Git branch strategy](#git-branch-strategy)
* [Git commit messages](#git-commit-messages)
* [Coding styleguide](#coding-styleguide)
## What should I know before I get started?
### Open Source Contribution Policy
**Source**: [Version 0.3, 20151118](https://medium.com/@jmaynard/a-contribution-policy-for-open-source-that-works-bfc4600c9d83#.i9ntbhmad)
#### Policy
We will accept contributions of good code that we can use from anyone.
#### What this means
This means exactly that. We dont care about anything but your code. We dont care about your race, religion, national origin, biological gender, perceived gender, sexual orientation, lifestyle, political viewpoint, or anything extraneous like that. We will neither reject your contribution nor grant it preferential treatment on any basis except the code itself. We do, however, reserve the right to limit your access to our community if you violate our [Code of Conduct][code-of-conduct].
#### If Your Contribution Is Rejected
If we reject your contribution, it means only that we do not consider it suitable for our project in the form it was submitted. It is not personal. If you ask civilly, we will be happy to discuss it with you and help you understand why it was rejected, and if possible improve it so we can accept it.
## How can I contribute?
### Feature requests
We're always looking for suggestions to improve our application. If you have a suggestion to improve an existing feature, or would like to suggest a completely new feature for KeePassXC, please use the [issue tracker on GitHub][issues-section].
### Bug reports
Our software isn't always perfect, but we strive to always improve our work. You may file bug reports in the issue tracker.
Before submitting a bug report, check if the problem has already been reported. Please refrain from opening a duplicate issue. If you want to add further information to an existing issue, simply add a comment on that issue.
### Discuss with the team
As with feature requests, you can talk to the KeePassXC team about bugs, new features, other issues and pull requests on the dedicated issue tracker, on the [Matrix development channel](https://matrix.to/#/!RhJPJPGwQIFVQeXqZa:matrix.org?via=matrix.org), or in the IRC channel on Libera.Chat (`#keepassxc-dev` on `irc.libera.chat`, or use a [webchat link](https://web.libera.chat/#keepassxc-dev)).
### Your first code contribution
Unsure where to begin contributing to KeePassXC? You can start by looking through these `beginner` and `help-wanted` issues:
* [Beginner issues][beginner] issues which should only require a few lines of code, and a test or two.
* ['Help wanted' issues][help-wanted] issues which should be a bit more involved than `beginner` issues.
Both issue lists are sorted by total number of comments. While not perfect, looking at the number of comments on an issue can give a general idea of how much an impact a given change will have.
### Using AI
Submissions written using generative AI (even partially) are not accepted.
### Pull requests
Along with our desire to hear your feedback and suggestions, we're also interested in accepting direct assistance in the form of code.
All pull requests must comply with the above requirements and with the [styleguides](#styleguides).
### Translations
Translations are managed on [Transifex](https://explore.transifex.com/keepassxc/keepassxc-browser/) which offers a web interface.
Please join an existing language team or request a new one if there is none.
If you open a Pull Request with new strings that require translations, it is enough to include new strings to the `_locales/en/messages.json` file.
### Architecture
Some basic information about the extension architecture is described in the [Extension details][extension-details] wiki page.
## Styleguides
### Git branch strategy
The Branch Strategy is based on [git-flow-lite](http://nvie.com/posts/a-successful-git-branching-model/).
* **develop** points to the development of the next release, contains tested and reviewed code
* **feature/**[name] points to a branch with a new feature, one which is candidate for merge into develop (subject to rebase)
* **fix/**[name] points to a branch with a fix for a particular issue ID
* **refactor/**[name] - points to a branch with a refactored feature
### Git commit messages
* Use the present tense ("Add feature" not "Added feature")
* Use the imperative mood ("Move cursor to…" not "Moves cursor to…")
* Limit the first line to 72 characters or less
* Reference issues and pull requests liberally
* If your pull request fixes an existing issue, add "Fixes #ISSUENUMBER" to your pull request description
### Coding styleguide
The coding style of the project is applied using `ESLint` and `Prettier` tools. A thorough description
of the coding style can be found in the `.eslintrc` and `.prettierrc` files, but the main conventions are presented here.
ESLint can be run manually by using the command `npm run lint`. Prettier formatting is not used by default because it can break some specific code styles, but can be enabled modifying the `.prettierignore` file.
#### Naming convention
`lowerCamelCase`
For names made of only one word, the first letter should be lowercase.
For names made of multiple concatenated words, the first letter of the whole is lowercase, and the first letter of each subsequent word is capitalized.
Applies to functions and Objects as well.
`UpperCamelCase`
For classes and enum-like Objects.
`ALL_CAPS`
For global/const variables and enum-like Object values.
#### Indention
- For **JavaScript files** (*.js): 4 spaces
- For **HTML files** (*.ui*): 2 spaces
- For **JSON files** (*.json): 2 spaces
- For **TypeScript files** (*.ts): 2 spaces
#### Global/const variables
```javascript
const TIMEOUT_VALUE 2000;
const DEFAULT_VALUE = 'all';
```
#### Classes
```javascript
class Icon {
constructor(databaseState) {
this.databaseState = databaseState;
this.icon = null;
}
}
```
#### Enum-like Objects
```javascript
const ManualFill = {
NONE: 0,
PASSWORD: 1,
BOTH: 2
};
```
#### Braces
```javascript
if (condition) {
doSomething();
} else {
doSomethingElse();
}
const exampleFunction = async function() {
doSomething();
};
```
#### Multiple conditions
```javascript
if (condition1 === condition2
|| condition3 === condition4
|| condition5 === condition6) {
doSomething();
}
if (['text1', 'text2', 'text3'].includes(inputText)) {
doSomething();
}
```
#### Variables
Always use `const` and `let` to define a variable. Do not use `var`.
[beginner]:https://github.com/keepassxreboot/keepassxc-browser/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22
[code-of-conduct]:https://github.com/keepassxreboot/keepassxc/blob/develop/CODE-OF-CONDUCT.md
[help-wanted]:https://github.com/keepassxreboot/keepassxc-browser/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22help%20wanted%22
[issues-section]:https://github.com/keepassxreboot/keepassxc-browser/issues
[extension-details]:https://github.com/keepassxreboot/keepassxc-browser/wiki/Extension-details@

View file

@ -1,86 +1,3 @@
1.10.0.1 (2026-03-08)
=========================
- Remove webRequestBlocking permission from V3 manifests [#2893]
- Fix dynamic password input detection when replacing username [#2896]
1.10.0 (2026-03-04)
=========================
- Add preliminary support for Safari [#2800]
- Add 'mfaCode' to accepted 2FA token fields (thanks hostops) [#2804]
- Add "postcode" to ignored TOTP list (thanks JackDunnCodes) [#2793]
- Add support for Related Origin Requests with passkeys [#2828]
- Add support for features list [#2848]
- Add support for publicKey in passkeys response [#2782]
- Fix hiding form switch animation when options page loads (thanks stefansundin) [#2807]
- Fix TOTP field detection with Microsoft sites [#2832]
- Fix the ability to fake same-orogin with passkeys (thanks a2kolbasov) [#2849]
- Fix detecting transitions [#2855]
- Fix Google password input detection [#2861]
- Fix Epic Games and Paypal password input detection [#2863]
- Fix using invalid parameters with MutationObserver [#2784]
- Fix ignoring partial nodeNames [#2745]
- Fix incorrect error message on keyboard fill [#2814]
- Fix reopen database shortcut (thanks a2kolbasov) [#2767]
- Fix showing notifications always on the top window [#2789]
- Fix missing digits in TOTP fill on some sites (thanks ElementW) [#2794]
- Fix setting default redirect allowance to 3 (thanks SinnySupernova) [#2797]
- Fix createObserver() check [#2873]
- Fix retry on input field detection with Custom Login Fields [#2875]
- Fix detecting new password input on forms [#2878]
- Fix form submit to ignore password related buttons [#2882]
- Refactor icon handling [#2791]
- Update webextension-polyfill library [#2822]
1.9.11 (2025-11-26)
=========================
- Add support for reseting all settings to defaults [#2720]
- Add support for toJSON() with createPublicKeyCredential (passkeys) [#2734]
- Add site exception for dei.gr [#2760]
- Add submit button exception for Dubverse [#2759]
- Add support for selecting a custom submit button with Custom Login Fields [#2756]
- Fix check for Firefox browser [#2724, #2725]
- Fix JavaScript error in console (thanks ygoe) [#2721]
- Fix overlap check with labels [#2731]
- Fix checking overlays before fill [#2733]
- Fix requerying form if password input changed [#2738]
- Fix incorrect fields variable [#2740]
- Fix malformed page content with passkeys script injection (thanks a2kolbasov) [#2757]
- Fix layout issues with banners [#2764]
- Update Playwright libraries [#2727]
1.9.10 (2025-10-05)
=========================
- Add Disable passkeys option to Site Preferences [#2694]
- Add overlay exceptions feature to Predefined Sites [#2693]
- Change DOM to use popover for icons and content [#2687]
- Change some icons [#2706]
- Fix detecting existing login combination [#2682]
- Fix Mutation Observer to handle multiple element mutations (thanks mihai-vlc) [#2635]
- Fix using popovers with Firefox ESR 115 [#2690]
- Fix filling relevant entries [#2691]
- Fix filling wrapped input fields [#2699]
- Fix filling username to already filled field [#2700]
- Fix ignore regex list (thanks EamonNerbonne) [#2711]
- Fix is element inside function [#2714]
- Fix handling window offsets for DOM content positioning [#2698]
- Refactor Site Preferences options settings page [#2666]
- Refactor browser information function [#2709]
1.9.9.6 (2025-09-09)
=========================
- Add caching to overlay detection for improved performance [#2670]
- Fix protection against form overlays [#2676]
- Fix using custom functions with Elements in TreeWalker [#2677]
1.9.9.5 (2025-09-03)
=========================
- Fix topmost input element check with labels [#2667]
- Fix Custom Login Fields with topmost element check [#2673]
1.9.9.4 (2025-08-29)
=========================
- Fix topmost element check with Shadow DOM [#2659]
1.9.9.3 (2025-08-27)
=========================
- Add protection against style changes for wrapper div elements [#2646]

View file

@ -49,10 +49,6 @@ Check [keepassxc-protocol](keepassxc-protocol.md) for the details about the mess
Translations are managed on [Transifex](https://explore.transifex.com/keepassxc/keepassxc-browser/) which offers a web interface. Please join an existing language team or request a new one if there is none.
## Contributing
You may directly contribute your own code by submitting a pull request. Please read the [CONTRIBUTING](.github/CONTRIBUTING.md) document for further information.
## Development and testing
See [wiki](https://github.com/keepassxreboot/keepassxc-browser/wiki/Loading-the-extension-manually).

View file

@ -1,9 +1,9 @@
{
"manifest_version": 3,
"name": "KeePassXC-Browser",
"version": "1.10.0.1",
"version_name": "1.10.0.1",
"minimum_chrome_version": "124",
"version": "1.9.9.3",
"version_name": "1.9.9.3",
"minimum_chrome_version": "93",
"description": "__MSG_extensionDescription__",
"author": "KeePassXC Team",
"icons": {
@ -53,8 +53,7 @@
"content/fields.js",
"content/fill.js",
"content/form.js",
"content/icon.js",
"content/icon-handler.js",
"content/icons.js",
"content/keepassxc-browser.js",
"content/observer-helper.js",
"content/pwgen.js",
@ -119,15 +118,12 @@
"choose_credential_fields": {
"description": "__MSG_popupChooseCredentialsText__"
},
"retrieve_credentials_forced": {
"description": "__MSG_credentialsRetrieveButton__"
"retrive_credentials_forced": {
"description": "__MSG_popupReopenButton__"
},
"request_autotype": {
"description": "__MSG_contextMenuRequestGlobalAutoType__"
},
"reopen_database": {
"description": "__MSG_popupReopenButton__"
},
"reload_extension": {
"description": "__MSG_popupReloadButton__"
}
@ -170,7 +166,8 @@
"tabs",
"webNavigation",
"webRequest",
"webRequestAuthProvider"
"webRequestAuthProvider",
"webRequestBlocking"
],
"optional_permissions": [
"privacy"

View file

@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "KeePassXC-Browser",
"version": "1.10.0.1",
"version": "1.9.9.3",
"description": "__MSG_extensionDescription__",
"author": "KeePassXC Team",
"icons": {
@ -66,8 +66,7 @@
"content/fields.js",
"content/fill.js",
"content/form.js",
"content/icon.js",
"content/icon-handler.js",
"content/icons.js",
"content/keepassxc-browser.js",
"content/observer-helper.js",
"content/pwgen.js",
@ -132,15 +131,12 @@
"choose_credential_fields": {
"description": "__MSG_popupChooseCredentialsText__"
},
"retrieve_credentials_forced": {
"description": "__MSG_credentialsRetrieveButton__"
"retrive_credentials_forced": {
"description": "__MSG_popupReopenButton__"
},
"request_autotype": {
"description": "__MSG_contextMenuRequestGlobalAutoType__"
},
"reopen_database": {
"description": "__MSG_popupReopenButton__"
},
"reload_extension": {
"description": "__MSG_popupReloadButton__"
}
@ -185,7 +181,7 @@
"applications": {
"gecko": {
"id": "keepassxc-browser@keepassxc.org",
"strict_min_version": "100.0"
"strict_min_version": "96.0"
}
},
"storage": {

View file

@ -1,216 +0,0 @@
import { defineConfig, globalIgnores } from "eslint/config";
import globals from "globals";
import path from "node:path";
import { fileURLToPath } from "node:url";
import js from "@eslint/js";
import { FlatCompat } from "@eslint/eslintrc";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all
});
export default defineConfig([globalIgnores(["**/*.min.js"]), {
extends: compat.extends("eslint:recommended"),
languageOptions: {
globals: {
...globals.browser,
...globals.jquery,
...globals.webextensions,
_called: "readonly",
acceptedOTPFields: "readonly",
assertInputFields: "readonly",
assertPasswordChangeFields: "readonly",
assertRegex: "readonly",
assertSearchField: "readonly",
assertSearchForm: "readonly",
assertTOTPField: "readonly",
AssociatedAction: "readonly",
AuthenticatorAssertionResponse: "readonly",
AuthenticatorAttestationResponse: "readonly",
Autocomplete: "readonly",
BannerPosition: "readonly",
BLUE_BUTTON: "readonly",
bootstrap: "readonly",
browser: "readonly",
browserAction: "readonly",
CHECK_UPDATE_NEVER: "readonly",
CHECK_UPDATE_ONE_MONTH: "readonly",
CHECK_UPDATE_ONE_WEEK: "readonly",
CHECK_UPDATE_THREE_DAYS: "readonly",
cloneInto: "readonly",
compareVersion: "readonly",
createResult: "readonly",
createStylesheet: "readonly",
DatabaseState: "readonly",
debugLogMessage: "readonly",
DEFINED_CUSTOM_FIELDS: "readonly",
elementsOverlap: "readonly",
EXTENSION_NAME: "readonly",
getCurrentTab: "readonly",
getIconClass: "readonly",
getLoginData: "readonly",
getTopLevelDomainFromUrl: "readonly",
GRAY_BUTTON_CLASS: "readonly",
GREEN_BUTTON: "readonly",
httpAuth: "readonly",
Icon: "readonly",
IGNORE_AUTOSUBMIT: "readonly",
IGNORE_FULL: "readonly",
IGNORE_NORMAL: "readonly",
IGNORE_NOTHING: "readonly",
IGNORE_PASSKEYS: "readonly",
importScripts: "readonly",
IMPROVED_DETECTION_PREDEFINED_SITELIST: "readonly",
initColorTheme: "readonly",
isEdge: "readonly",
isElementInside: "readonly",
isFirefox: "readonly",
isIframeAllowed: "readonly",
isSafari: "readonly",
keepass: "readonly",
keepassClient: "readonly",
kpActions: "readonly",
kpErrors: "readonly",
kpxc: "readonly",
kpxcAssert: "readonly",
kpxcBanner: "readonly",
kpxcCustomLoginFieldsBanner: "readonly",
kpxcEvent: "readonly",
kpxcFields: "readonly",
kpxcFill: "readonly",
kpxcForm: "readonly",
kpxcIcons: "readonly",
kpxcObserverHelper: "readonly",
kpxcPasswordGenerator: "readonly",
kpxcPasswordIcons: "readonly",
kpxcSites: "readonly",
kpxcTOTPAutocomplete: "readonly",
kpxcTOTPIcons: "readonly",
kpxcUI: "readonly",
kpxcUserAutocomplete: "readonly",
kpxcUsernameIcons: "readonly",
logDebug: "readonly",
logError: "readonly",
kpxcPasskeysUtils: "readonly",
ManualFill: "readonly",
matchesWithNodeName: "readonly",
MAX_AUTOCOMPLETE_NAME_LEN: "readonly",
MAX_OPACITY: "readonly",
MAX_TOTP_INPUT_LENGTH: "readonly",
menuContexts: "readonly",
MIN_INPUT_FIELD_OFFSET_WIDTH: "readonly",
MIN_INPUT_FIELD_WIDTH_PX: "readonly",
MIN_OPACITY: "readonly",
MIN_TOTP_INPUT_LENGTH: "readonly",
module: "readonly",
nacl: "readonly",
OBSERVER_OPTIONS: "readonly",
ORANGE_BUTTON: "readonly",
page: "readonly",
Pixels: "readonly",
PublicKeyCredential: "readonly",
PREDEFINED_SITELIST: "readonly",
RED_BUTTON: "readonly",
retrieveColorScheme: "readonly",
sendMessage: "readonly",
showNotification: "readonly",
siteMatch: "readonly",
SitePreferences: "readonly",
slashNeededForUrl: "readonly",
SORT_BY_GROUP_AND_TITLE: "readonly",
SORT_BY_GROUP_AND_USERNAME: "readonly",
SORT_BY_MATCHING_CREDENTIALS_SETTING: "readonly",
SORT_BY_RELEVANT_ENTRY: "readonly",
SORT_BY_TITLE: "readonly",
SORT_BY_USERNAME: "readonly",
statusResponse: "readonly",
Tests: "readonly",
tr: "readonly",
trimURL: "readonly",
updateDefaultPasswordManager: "readonly",
},
ecmaVersion: 13,
sourceType: "script",
},
rules: {
"array-bracket-spacing": ["error", "always"],
"arrow-body-style": "warn",
"arrow-parens": "off",
"arrow-spacing": "warn",
"block-scoped-var": "off",
"brace-style": ["warn", "1tbs"],
"camelcase": "warn",
"class-methods-use-this": "off",
"comma-dangle": "off",
"computed-property-spacing": ["error", "never"],
"consistent-return": "off",
"dot-notation": "off",
"eqeqeq": "error",
"func-names": "off",
"function-paren-newline": "off",
"guard-for-in": "off",
"implicit-arrow-linebreak": "off",
"indent": ["error", 4],
"linebreak-style": ["error", "unix"],
"lines-around-comment": "off",
"max-len": "off",
"no-alert": "error",
"no-await-in-loop": "off",
"no-bitwise": "off",
"no-console": "off",
"no-continue": "off",
"no-control-regex": "off",
"no-else-return": "off",
"no-empty-function": ["error", {
"allow": ["arrowFunctions", "methods", "asyncMethods"],
}],
"no-extend-native": "off",
"no-global-assign": "off",
"no-lonely-if": "off",
"no-loop-func": "off",
"no-mixed-operators": "off",
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
"no-multi-spaces": "warn",
"no-param-reassign": "off",
"no-plusplus": "off",
"no-prototype-builtins": "off",
"no-redeclare": "off",
"no-restricted-globals": "off",
"no-restricted-syntax": "off",
"no-return-await": "off",
"no-tabs": "warn",
"no-underscore-dangle": "off",
"no-unused-vars": ["warn", {
"args": "none",
"caughtErrorsIgnorePattern": "^_",
"varsIgnorePattern": "[A-Z_]+|tr|[$]",
}],
"no-use-before-define": "off",
"no-useless-escape": "off",
"no-useless-return": "off",
"no-var": "error",
"object-curly-spacing": ["warn", "always"],
"object-shorthand": "off",
"operator-linebreak": "off",
"prefer-arrow-callback": "off",
"prefer-const": "warn",
"prefer-destructuring": "off",
"prefer-template": "off",
"quote-props": "off",
"quotes": ["error", "single", {
"avoidEscape": true,
}],
"semi": ["error", "always"],
"space-before-blocks": "warn",
"space-before-function-paren": "off",
"space-in-parens": ["error", "never"],
"space-unary-ops": "off",
"spaced-comment": "off",
"strict": "off",
"vars-on-top": "off",
},
}]);

View file

@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "Текстово поле",
"message": "Поле за низ",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Изпращащ бутон",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Избиране поле за потребителското име",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Избиране на поле за низ",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Избиране на изпращащ бутон",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Потвърдете избора си или изберете повече полета като текстови полета.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Потвърдете избора си или изберете повече полета като полета за низ.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Също можете да използвате цифрите, за да избирате полета посредством клавиатурата.",
@ -370,10 +362,6 @@
"message": "Полета за низове",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Изпращащ бутон",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- без потребителско име -",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "Изтекъл",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Получаване на регистрации",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Грешка: Не са намерени полета, които да бъдат попълнени.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Предпочитания за сайтове",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Настройки",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Относно",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Изнасяне на настройки",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Нулиране на настройките",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Група по подразбиране при добавяне на регистрации:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Изисква KeePassXC издание: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "Разширението изисква KeePassXC издание $1. По-ранни издания могат да доведат до нежелано поведение.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "По подразбиране: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Текущите настройки ще бъдат презаписани. Наистина ли желаете да бъдат внесени настройките от файла?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "Желаете ли настройките да бъдат нулирани до подразбираните стойности?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Хранилища, свързани с KeePassXC-Browser.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "Адрес на страница",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Възможности",
"optionsColumnIgnore": {
"message": "Пренебрегване",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Изключване на автоматично изпращане",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Изключване на Passkeys",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Изключване на всички функции",
"description": "Site Preferences option selection."

View file

@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "String Field",
"message": "Strengfelt",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Submit Button",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Vælg et brugernavnsfelt",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Vælg strengfelter",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Choose submit button",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Please confirm your selection or choose more fields as String Fields.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Bekræft venligst dine valg eller vælge flere felter as strengfelter.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Du kan også bruge nummeret til at vælge inputfelterne fra tastaturet.",
@ -370,10 +362,6 @@
"message": "Strengfelter",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Submit Button",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- intet brugernavn -",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "Udløbet",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Retrieve credentials",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Fejl: Kan ikke finde felterne som skal udfyldes.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Sideindstillinger",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Om",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Eksportér indstillinger",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Reset all settings",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Standardgruppe for gemte nye adgangskoder:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Kræver KeePassXC-version: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Standard: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "De nuværende indstillinger tilsidesættes. Vil du virkelig importere indstillingsfilen?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "All settings will be reset to defaults. Are you sure?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Databaser som er forbundet til KeePassXC-Browser.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "Sidens URL",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Features",
"optionsColumnIgnore": {
"message": "Ignorer",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Slå autoindsendelse fra",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Disable passkeys",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Deaktivér alle funktionaliteter",
"description": "Site Preferences option selection."

View file

@ -322,10 +322,6 @@
"message": "Zeichenkettenfeld",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Senden-Schaltfläche",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Feld für Benutzernamen auswählen",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Zeichenkettenfelder auswählen",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Senden-Schaltfläche auswählen",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Bitte bestätigen Sie Ihre Auswahl oder wählen Sie weitere Felder als Zeichenkettenfelder aus.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Bitte bestätigen Sie Ihre Auswahl oder wählen Sie weitere Felder als <em>Zeichenkettenfelder</em> aus.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Sie können die Zahlen auch verwenden, um die Eingabefelder über die Tastatur auszuwählen.",
@ -370,10 +362,6 @@
"message": "Zeichenkettenfelder",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Senden-Schaltfläche",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- kein Benutzername -",
"description": "Shown when no username is set in the credentials."
@ -383,7 +371,7 @@
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Ausfüllen der Anmeldedaten in nicht vertrauenswürdigen iframes blockiert.",
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
@ -406,10 +394,6 @@
"message": "Abgelaufen",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Anmeldedaten abrufen",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Fehler:\nAuszufüllende Felder können nicht gefunden werden.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Seiteneinstellungen",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Einstellungen",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Über",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Einstellungen exportieren",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Alle Einstellungen zurücksetzen",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Standardgruppe zum Speichern neuer Passwörter:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Benötigt KeePassXC-Version: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "Die Erweiterung erfordert die KeePassXC-Version: $1. Frühere Versionen können zu unerwünschtem Verhalten führen.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Standard: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Die aktuellen Einstellungen werden überschrieben. Möchten Sie die Einstellungsdatei wirklich importieren?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "Alle Einstellungen werden auf die Standardeinstellungen zurückgesetzt. Sind Sie sicher?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Die folgenden KeePassXC-Datenbanken sind mit dem KeePassXC-Browser verbunden.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "Seiten-URL",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Funktionen",
"optionsColumnIgnore": {
"message": "Ignorieren",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Automatisches Absenden deaktivieren",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Passkeys deaktivieren",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Alle Funktionen deaktivieren",
"description": "Site Preferences option selection."

View file

@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "String Field",
"message": "Πεδίο String",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Submit Button",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Επιλέξτε ένα πεδίο ονόματος χρήστη",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Επιλέξτε Πεδία Strings",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Choose submit button",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Please confirm your selection or choose more fields as String Fields.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Επιβεβαιώστε την επιλογή σας ή επιλέξτε περισσότερα πεδία ως πεδία String.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Μπορείτε επίσης να χρησιμοποιήσετε τους αριθμούς για να επιλέξετε τα πεδία εισαγωγής από το πληκτρολόγιο.",
@ -370,10 +362,6 @@
"message": "Πεδία Strings",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Submit Button",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- χωρίς όνομα χρήστη -",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "Έληξε",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Retrieve credentials",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Σφάλμα: Δεν είναι δυνατή η εύρεση πεδίων για συμπλήρωση.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Site Preferences",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Σχετικά με",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Ρυθμίσεις εξαγωγής",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Reset all settings",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Προεπιλεγμένη ομάδα για την αποθήκευση νέων κωδικών πρόσβασης:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Απαιτείται έκδοση KeePassXC: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Προεπιλογή: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Οι τρέχουσες ρυθμίσεις θα παρακαμφθούν. Θέλετε πραγματικά να εισαγάγετε το αρχείο ρυθμίσεων;",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "All settings will be reset to defaults. Are you sure?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Συνδεδεμένες βάσεις δεδομένων με το KeePassXC-Browser.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "Διεύθυνση URL σελίδας",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Features",
"optionsColumnIgnore": {
"message": "Αγνοήστε",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Απενεργοποίηση Αυτόματης Υποβολής",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Disable passkeys",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Απενεργοποίηση όλων των λειτουργιών",
"description": "Site Preferences option selection."

View file

@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "String Field",
"message": "String field",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Submit Button",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Choose a username field",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Choose String Fields",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Choose submit button",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Please confirm your selection or choose more fields as String Fields.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Please confirm your selection or choose more fields as String fields.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "You can also use the numbers to choose the input fields from keyboard.",
@ -370,10 +362,6 @@
"message": "String Fields",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Submit Button",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- no username -",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "Expired",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Retrieve credentials",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Error: Cannot find fields to fill in.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Site Preferences",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "About",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Export settings",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Reset all settings",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Default group for saving new passwords:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Requires KeePassXC version: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Default: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Current settings will be overridden. Do you really want to import the settings file?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "All settings will be reset to defaults. Are you sure?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Databases connected to KeePassXC-Browser.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "Page URL",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Features",
"optionsColumnIgnore": {
"message": "Ignore",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Disable Auto-Submit",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Disable passkeys",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Disable all features",
"description": "Site Preferences option selection."

View file

@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "String Field",
"message": "String field",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Submit Button",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Choose a username field",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Choose String Fields",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Choose submit button",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Please confirm your selection or choose more fields as String Fields.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Please confirm your selection or choose more fields as String fields.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "You can also use the numbers to choose the input fields from keyboard.",
@ -370,10 +362,6 @@
"message": "String Fields",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Submit Button",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- no username -",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "Expired",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Retrieve credentials",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Error: Cannot find fields to fill in.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Site Preferences",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "About",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Export settings",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Reset all settings",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Default group for saving new passwords:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Requires KeePassXC version: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behaviour.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Default: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Current settings will be overridden. Do you really want to import the settings file?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "All settings will be reset to defaults. Are you sure?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Databases connected to KeePassXC-Browser.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "Page URL",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Features",
"optionsColumnIgnore": {
"message": "Ignore",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Disable Auto-Submit",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Disable passkeys",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Disable all features",
"description": "Site Preferences option selection."

View file

@ -322,10 +322,6 @@
"message": "Campo de cadena",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Botón Enviar",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Elija un campo nombre de usuario",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Elija Campos de Cadena",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Seleccione el botón enviar",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Confirme su selección o elija más campos como campos de cadena.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Confirme su selección o elija más campos como Campos de cadena.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "También puede utilizar los números para elegir los campos de entrada desde el teclado.",
@ -370,10 +362,6 @@
"message": "Campos de Cadena",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Botón Enviar",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- sin nombre de usuario -",
"description": "Shown when no username is set in the credentials."
@ -383,7 +371,7 @@
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Relleno de credenciales bloqueado en iframe que no es de confianza.",
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
@ -406,10 +394,6 @@
"message": "Caducado",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Retrieve credentials",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Error: No se pueden encontrar campos para rellenar.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Preferencias del sitio",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Configuración",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Acerca de",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Exportar configuración",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Restablecer todos los ajustes",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Grupo predeterminado para guardar nuevas contraseñas:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Requiere la versión de KeePassXC: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Por defecto: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "La configuración actual será sobrescrita. ¿Desea importar el archivo de configuración?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "Todos los ajustes se restablecerán a los valores predeterminados. ¿Está seguro?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Las siguientes bases de datos KeePassXC están conectadas a KeePassXC-Browser.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "URL de la página",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Características",
"optionsColumnIgnore": {
"message": "Ignorar",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Deshabilitar autoenvío",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Deshabilitar claves de acceso",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Desactivar todas las características",
"description": "Site Preferences option selection."

View file

@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "Lisämerkkijonokenttä",
"message": "Lisämerkkijonokenttä #",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Lähetä-nappi",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Valitse käyttäjätunnuksen kenttä",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Valitse lisämerkkijonokentät",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Valitse lähetä-nappi",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Vahvista valintasi tai merkitse lisää kenttiä lisämerkkijonokentiksi.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Vahvista valintasi tai merkitse lisää kenttiä <em>lisämerkkijonokentiksi</em>.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Voit myös valita kenttiä käyttämällä näppäimistön numeroita.",
@ -370,10 +362,6 @@
"message": "Lisämerkkijonokentät",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Lähetä-nappi",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- ei käyttäjätunnusta -",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "Vanhentunut",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Hae tietueet",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Virhe:\nTäytön tarvitsemia kenttiä ei löydy.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Sivustokohtaiset asetukset",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Asetukset",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Tietoja",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Vie asetukset",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Palauta kaikki asetukset oletuksiin",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Oletusryhmä johon uudet salasanat tallennetaan:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Vaatii KeePassXC:n version: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "Tämä selainlaajennus vaatii KeePassXC-version: $1. Aiemmat versiot voivat aiheuttaa epätoivottua käytöstä.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Oletus: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Nykyiset asetukset ylikirjoitetaan. Oletko varma, että haluat tuoda asetustiedoston?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "Kaikki asetukset palautetaan oletuksiin. Oletko varma?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Seuraavat KeePassXC:n tietokannat ovat yhdistettynä selainlaajennukseen.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "Sivun URL",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Ominaisuudet",
"optionsColumnIgnore": {
"message": "Älä huomioi",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Automattisyöttö pois päältä",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Poista pääsyavaimet käytöstä",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Poista kaikki ominaisuudet käytöstä",
"description": "Site Preferences option selection."

View file

@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "Champ de chaînes de caractères",
"message": "Champ de chaîne de caractères",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Bouton denvoi",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Choisissez un champ de nom dutilisateur",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Choisissez des champs de chaînes de caractères",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Choisir le bouton denvoi",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Confirmez votre choix ou choisissez dautres champs comme champs de chaînes de caractères.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Vous pouvez aussi utiliser les chiffres pour choisir les champs dentrée avec le clavier.",
@ -370,10 +362,6 @@
"message": "Champs de type chaîne",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Bouton denvoi",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": " aucun nom dutilisateur ",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "est expiré",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Obtenir les identifiants.",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Erreur : Impossible de trouver des champs à remplir.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Préférences des sites",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Paramètres",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "À propos",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Exporter les paramètres",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Réinitialiser tous les paramètres",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Groupe par défaut pour lenregistrement des nouveaux mots de passe :",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Exige KeePassXC version : $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "Lextension nécessite la version KeePassXC : $1. Les versions antérieures peuvent entraîner des comportements indésirables.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Par défaut : $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Les paramètres actuels seront remplacés. Voulez-vous importer le fichier de paramètres?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "Tous les paramètres seront réinitialisés aux valeurs par défaut. Confirmez-vous?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Bases de données KeePassXC connectées à KeePassXC-Browser.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "URL de la page",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Fonctions",
"optionsColumnIgnore": {
"message": "Ignorer",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Désactiver lenvoi automatique",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Désactiver les clés daccès",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Désactiver toutes les fonctions",
"description": "Site Preferences option selection."

View file

@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "String Field",
"message": "שדה מחרוזת #",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Submit Button",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "נא לבחור שדה שם משתמש",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "נא לבחור שדה מחרוזת",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Choose submit button",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Please confirm your selection or choose more fields as String Fields.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "נא לאשר את הבחירה או לבחור שדות נוספים כשדות מחרוזת.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "ניתן להשתמש גם במספרים לבחירת שדות הקלט מלוח המקשים.",
@ -370,10 +362,6 @@
"message": "שדות מחרוזת",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Submit Button",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- אין שם משתמש -",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "תפוגה",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Retrieve credentials",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "שגיאה: לא ניתן למצוא שדות למילוי.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "העדפות אתר",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "אודות",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "ייצוא הגדרות",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Reset all settings",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "קבוצת ברירת מחדל לשמירת ססמאות חדשות:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "נדרשת גרסת KeePassXC: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "ברירת מחדל: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "ההגדרות הנוכחיות יידרסו. האם לייבא את קובץ ההגדרות?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "All settings will be reset to defaults. Are you sure?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "מסדי נתונים מחוברים לדפדפן־KeePassXC.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "מען URL עמוד",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Features",
"optionsColumnIgnore": {
"message": "להתעלם",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "השבתת הגשה אוטומטית",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Disable passkeys",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "השבתת כל התכונות",
"description": "Site Preferences option selection."

View file

@ -8,7 +8,7 @@
"description": "Add button title text on Site Preferences tab."
},
"editSitePreferenceButtonTitle": {
"message": "URL-cím szerkesztése ezen webhely beállításai számára.",
"message": "Edit URL for this Site Preferences.",
"description": "Edit button title text on Site Preferences tab."
},
"allowIframeButtonTitle": {
@ -20,7 +20,7 @@
"description": "Connect button title text."
},
"copyDebugInfoButtonTitle": {
"message": "Hibakeresési információk másolása a vágólapra",
"message": "Copy debug info to clipboard",
"description": "Copy debug info button title text on About tab."
},
"dismissHttpAuthButtonTitle": {
@ -322,10 +322,6 @@
"message": "Karakterláncmező",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Beküldés gomb",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Válasszon egy felhasználónév-mezőt",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Válasszon egy karakterláncmezőt",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Beküldés gomb kijelölése",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Erősítse meg a választását vagy válasszon több mezőt <em>karakterlánc mezőkként</em>.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Használhatja a billentyűzeten lévő számokat is a beviteli mezők kiválasztásához.",
@ -370,10 +362,6 @@
"message": "Karakterláncmezők",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Beküldés gomb",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": " nincs felhasználónév ",
"description": "Shown when no username is set in the credentials."
@ -383,7 +371,7 @@
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "A hitelesítő adatok kitöltése blokkolása a nem megbízható iframe-ben.",
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
@ -406,10 +394,6 @@
"message": "Lejárt",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Hitelesítő adatok lekérése",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Hiba:\nNem találhatók kitöltendő mezők.",
"description": "Alert message when no fields are found to fill in."
@ -647,11 +631,11 @@
"description": "Dismiss button text when in HTTP Authentication popup."
},
"optionsDefaultSettingsTitle": {
"message": "Alapértelmezett beállítások",
"message": "Default settings",
"description": "Default settings title for Getting Started page."
},
"optionsWelcomeTitle": {
"message": "KeePassXC-böngésző",
"message": "KeePassXC-Browser",
"description": "Main card title for Getting Started page."
},
"optionsTitle": {
@ -694,10 +678,6 @@
"message": "Oldalbeállítások",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Beállítások",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Névjegy",
"description": "About page header."
@ -723,7 +703,7 @@
"description": "Add button text."
},
"optionsButtonEdit": {
"message": "Szerkesztés",
"message": "Edit",
"description": "Edit button text."
},
"optionsButtonUpdate": {
@ -750,10 +730,6 @@
"message": "Beállítások exportálása",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Minden beállítás visszaállítása",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Alapértelmezett csoport az új jelszavak mentéséhez:",
"description": "Default group options text."
@ -811,7 +787,7 @@
"description": "Automatically fill-in single credential entry checkbox text."
},
"optionsCheckboxAutoFillRelevantCredential": {
"message": "Releváns hitelesítésiadat-bejegyzések automatikus kitöltése",
"message": "Automatically fill in relevant credential entries",
"description": "Automatically fill-in relevant credential entry checkbox text."
},
"optionsCheckboxAutoFillSingleTotp": {
@ -855,11 +831,11 @@
"description": "Show login notifications checkbox text."
},
"optionsDefaultPasswordManager": {
"message": "Beállítás alapértelmezett jelszókezelőnek.",
"message": "Set as default password manager",
"description": "Default password manager checkbox text."
},
"optionsDefaultPasswordManagerHelpText": {
"message": "A KeePassXC-böngészőt beállítja alapértelmezett jelszókezelőnek a böngészőben.",
"message": "Sets KeePassXC-Browser as the default password manager for the browser.",
"description": "Default password manager help text."
},
"optionsRedirectAllowance": {
@ -959,7 +935,7 @@
"description": "OTP field icon option help text."
},
"optionsHideIconsAlertText": {
"message": "Szükség esetén az ikonok manuálisan is elrejthetők Shift+kattintással.",
"message": "You can also hide icons manually when needed with shift+click.",
"description": "Hide icons alert text text."
},
"optionsAutoRetrieveCredentialsHelpText": {
@ -975,7 +951,7 @@
"description": "Auto-Fill Single Entry option help text."
},
"optionsAutoFillRelevantCredentialHelpText": {
"message": "Releváns hitelesítésiadat-bejegyzések automatikus kitöltése ha egy második kitöltés is szükséges.",
"message": "Automatically fill in the relevant credential if a second fill is needed.",
"description": "Auto-Fill Relevant Credential option help text."
},
"optionsAutoFillSingleEntryWarning": {
@ -1058,10 +1034,6 @@
"message": "Szükséges KeePassXC verzió: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "A kiterjesztéshez a KeePassXC ezen verziója szükséges: $1. A korábbi verziók nem kívánt viselkedést okozhatnak.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Alapértelmezett: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "A jelenlegi beállítások felül lesznek írva. Biztos, hogy importálja a beállításfájlt?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "Minden beállítás alapértelmezett értékre áll vissza. Valóban ezt szeretné?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "A következő KeePassXC adatbázisok vannak kapcsolódva a KeePassXC-böngészőhöz.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "Oldal URL",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Képességek",
"optionsColumnIgnore": {
"message": "Mellőzés",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Automatikus beküldés letiltása",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Jelkulcsok letiltása",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Az összes funkció letiltása",
"description": "Site Preferences option selection."
@ -1435,31 +1399,31 @@
"description": "Lock database button title text."
},
"welcomeText": {
"message": "Üdvözöljük a KeePassXC-böngészőben!",
"message": "Welcome to KeePassXC-Browser!",
"description": "Main title of Getting Started page."
},
"documentationGettingStarted": {
"message": "A bevezető útmutatónk segítségével gyorsan elsajátíthatja a használatot.",
"message": "Our Getting Started Guide will get you up and running quickly.",
"description": "Getting Started document text."
},
"documentationUserGuide": {
"message": "Átfogóbb dokumentációra lenne szüksége? A felhasználói útmutatónk segíthet.",
"message": "Looking for more comprehensive documentation? Our User Guide is there to help.",
"description": "User Guide text."
},
"documentationTroubleshootingGuide": {
"message": "Segítségre van szüksége a böngésző integrációjának hibaelhárításában? Olvassa el a hibaelhárítási útmutatót.",
"message": "Need help troubleshooting the browser integration? Check the Troubleshooting Guide.",
"description": "Troubleshooting Guide text."
},
"gettingStartedWelcomeText": {
"message": "Üdvözlet a KeePassXC-böngészőben, a KeePassXC hivatalos böngészőbővítményében.",
"message": "Welcome to KeePassXC-Browser, the official browser extension for KeePassXC.",
"description": "Welcome to KeePassXC-Browser, the official browser extension for KeePassXC."
},
"gettingStartedSecondWelcomeText": {
"message": "Kérjük, nézze át az alapértelmezett beállításokat és ellenőrizze az összes kívánt opciót.",
"message": "Please go through the default settings and check all your preferred options.",
"description": "Please go through the default settings and check all your preferred options."
},
"gettingStartedNewUser": {
"message": "Új felhasználó? Nézze meg a hivatkozott dokumentációt!",
"message": "Are you a new user? Check links to our documentation.",
"description": "Are you a new user? Check links to our documentation."
}
}

View file

@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "String Field",
"message": "Ruas lema",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Submit Button",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Pilih ruas nama pengguna",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Pilih Ruas String",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Choose submit button",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Please confirm your selection or choose more fields as String Fields.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Harap konfirmasi pilihan Anda atau pilih lebih banyak bidang sebagai bidang String.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Anda juga dapat menggunakan angka untuk memilih bidang input dari keyboard.",
@ -370,10 +362,6 @@
"message": "Ruas String",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Submit Button",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- tidak ada nama pengguna -",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "Kedaluwarsa",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Retrieve credentials",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Error: Tidak dapat menemukan bidang yang harus diisi.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Site Preferences",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Tentang",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Ekspor pengaturan",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Reset all settings",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Grup baku untuk menyimpan sandi baru:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Membutuhkan KeePassXC versi: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Standar: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Pengaturan saat ini akan diganti. Apakah Anda benar-benar ingin mengimpor berkas pengaturan?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "All settings will be reset to defaults. Are you sure?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Basis data terhubung ke KeePassXC-Browser.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "Halaman URL",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Features",
"optionsColumnIgnore": {
"message": "Abaikan",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Nonaktifkan Pengiriman Otomatis",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Disable passkeys",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Nonaktifkan semua fitur",
"description": "Site Preferences option selection."

View file

@ -8,7 +8,7 @@
"description": "Add button title text on Site Preferences tab."
},
"editSitePreferenceButtonTitle": {
"message": "Modifica URL per queste preferenze sito",
"message": "Modifica URL per queste Preferenze Sito",
"description": "Edit button title text on Site Preferences tab."
},
"allowIframeButtonTitle": {
@ -20,7 +20,7 @@
"description": "Connect button title text."
},
"copyDebugInfoButtonTitle": {
"message": "Copia informazioni debug negli appunti",
"message": "Copia informazioni di debug negli appunti",
"description": "Copy debug info button title text on About tab."
},
"dismissHttpAuthButtonTitle": {
@ -244,7 +244,7 @@
"description": "Error notification shown when not connected to KeePassXC."
},
"errorCurrentDatabaseNotConnected": {
"message": "Il database attuale non è connesso.",
"message": "Il database corrente non è connesso.",
"description": "Error notification shown when current database is not connected during action."
},
"passwordGeneratorErrorTooLong": {
@ -322,10 +322,6 @@
"message": "Campo stringa",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Pulsante Invia",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Scegli un campo nome utente",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Scegli campi stringa",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Scegli pulsante Invia",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Conferma la selezione o scegli più campi come campi stringa.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Conferma la selezione o scegli altri campi come campi stringa.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Puoi anche usare i numeri per scegliere i campi di input dalla tastiera.",
@ -370,10 +362,6 @@
"message": "Campi stringa",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Pulsante Invia",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- nessun nome utente -",
"description": "Shown when no username is set in the credentials."
@ -383,7 +371,7 @@
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Riempimento delle credenziali bloccato in iframe non attendibile.",
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
@ -391,7 +379,7 @@
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
},
"credentialsMultipleFoundSecondLine": {
"message": "Per ulteriori opzioni seelziona l'icona di KeePassXC-Browser.",
"message": "Premi l'icona di KeePassXC-Browser per ulteriori opzioni.",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
},
"credentialsNoUsernameFound": {
@ -406,10 +394,6 @@
"message": "Scaduta",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Recupera credenziali",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Errore: impossibile trovare campi da compilare.",
"description": "Alert message when no fields are found to fill in."
@ -503,7 +487,7 @@
"description": "Checking status message in popup."
},
"popupNotConfigured": {
"message": "KeePassXC-Browser non è stato configurato. \nPer associarlo a KeePassXC seleziona il pulsante 'Connetti'.",
"message": "KeePassXC-Browser non è stato configurato. Premi il pulsante connetti per associarlo a KeePassXC.",
"description": "A popup message shown when the extension has not been connected to KeePassXC."
},
"popupNeedReconfigure": {
@ -511,7 +495,7 @@
"description": "A popup message shown when the extension has been disconnected from KeePassXC."
},
"popupNeedReconfigureMessage": {
"message": "Per stabilire una nuova connessione seleziona il pulsante 'Riconnetti'.",
"message": "Premi il pulsante riconnetti per stabilire una nuova connessione.",
"description": "A popup message shown when reconnect is needed."
},
"popupConfiguredNotAssociated": {
@ -551,7 +535,7 @@
"description": "A message shown choosing what credentials user wants to update."
},
"rememberChooseGroup": {
"message": "Per memorizzare le nuove credenziali scegli un gruppo.",
"message": "Scegli un gruppo per memorizzare le nuove credenziali.",
"description": "A message shown choosing what group user wants to use for new credentials."
},
"rememberInfoDefaultGroupNotFound": {
@ -694,10 +678,6 @@
"message": "Preferenze sito",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Impostazioni",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Info",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Esporta impostazioni",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Ripristina tutte le impostazioni",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Gruppo predefinito salvataggio nuove password:",
"description": "Default group options text."
@ -763,7 +739,7 @@
"description": "Default group help text."
},
"optionsLabelDefaultPasskeyGroup": {
"message": "Gruppo predefinito salvataggio nuove passkey:",
"message": "Gruppo predefinito per il salvataggio delle nuove passkeys:",
"description": "Default passkey group options text."
},
"optionsLabelDefaultGroupCheckboxText": {
@ -855,11 +831,11 @@
"description": "Show login notifications checkbox text."
},
"optionsDefaultPasswordManager": {
"message": "Imposta come password manager predefinito",
"message": "Imposta come password manager di default",
"description": "Default password manager checkbox text."
},
"optionsDefaultPasswordManagerHelpText": {
"message": "Imposta KeePassXC-Browser come password manager predefinito per il browser.",
"message": "Imposta KeePassXC-Browser come password manager di default per il browser.",
"description": "Default password manager help text."
},
"optionsRedirectAllowance": {
@ -959,7 +935,7 @@
"description": "OTP field icon option help text."
},
"optionsHideIconsAlertText": {
"message": "Puoi nascondere le icone manualmente quando occorre con Maiusc+Clic",
"message": "Puoi nascondere le icone manualmente quando occorre con Shift+Click",
"description": "Hide icons alert text text."
},
"optionsAutoRetrieveCredentialsHelpText": {
@ -975,7 +951,7 @@
"description": "Auto-Fill Single Entry option help text."
},
"optionsAutoFillRelevantCredentialHelpText": {
"message": "Se è necessario un secondo riempimento compila automaticamente le credenziali pertinenti.",
"message": "Compila automaticamente le credenziali pertinenti se è necessario un secondo riempimento.",
"description": "Auto-Fill Relevant Credential option help text."
},
"optionsAutoFillSingleEntryWarning": {
@ -1058,10 +1034,6 @@
"message": "Richiede KeePassXC versione: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "L'estensione richiede la versione KeePassXC: $1. Le versioni precedenti possono causare comportamenti indesiderati.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Predefinito: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Le impostazioni attuali verranno sovrascritte. Vuoi veramente importare le impostazioni?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "Tutte le impostazioni verranno ripristinate ai valori predefiniti. \nVuoi continuare?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Database KeePassXC connessi a KeePassXC-Browser.",
"description": "Info text about connected databases."
@ -1115,7 +1083,7 @@
"description": "Confirmation text when removing database from the list."
},
"optionsDatabasesRemoveIdentifierConfirmFirst": {
"message": "Vuoi rimuovere l'identificatore $1 dall'elenco database? ",
"message": "Vuoi davvero rimuovere l'identificatore $1 dall'elenco del database? ",
"description": "Confirmation text when removing database from the list."
},
"optionsDatabasesRemoveIdentifierConfirmSecond": {
@ -1146,8 +1114,8 @@
"message": "URL pagina",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Funzionalità",
"optionsColumnIgnore": {
"message": "Ignora",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Disabilita l'invio automatico",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Disabilita passkeys",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Disattiva tutte le funzionalità",
"description": "Site Preferences option selection."
@ -1435,23 +1399,23 @@
"description": "Lock database button title text."
},
"welcomeText": {
"message": "Benvenuto in KeePassXC-Browser!",
"message": "Benvenuto su KeePassXC-Browser!",
"description": "Main title of Getting Started page."
},
"documentationGettingStarted": {
"message": "Grazie alla nostra 'Guida introduttiva' sarai subito operativo.",
"message": "Grazie alla nostra Guida introduttiva sarai subito operativo.",
"description": "Getting Started document text."
},
"documentationUserGuide": {
"message": "Cerchi una documentazione più completa? \nLa nostra 'Guida per l'utente' è qui per aiutarti.",
"message": "Cerchi una documentazione più completa? La nostra Guida per l'utente è qui per aiutarti.",
"description": "User Guide text."
},
"documentationTroubleshootingGuide": {
"message": "Hai bisogno di aiuto per risolvere i problemi di integrazione del browser? Consulta la 'Guida alla risoluzione dei problemi'.",
"message": "Hai bisogno di aiuto per risolvere i problemi di integrazione del browser? Consulta la Guida alla risoluzione dei problemi.",
"description": "Troubleshooting Guide text."
},
"gettingStartedWelcomeText": {
"message": "Benvenuti in KeePassXC-Browser, l'estensione ufficiale del browser per KeePassXC.",
"message": "Benvenuti su KeePassXC-Browser, l'estensione ufficiale del browser per KeePassXC.",
"description": "Welcome to KeePassXC-Browser, the official browser extension for KeePassXC."
},
"gettingStartedSecondWelcomeText": {
@ -1459,7 +1423,7 @@
"description": "Please go through the default settings and check all your preferred options."
},
"gettingStartedNewUser": {
"message": "Sei un nuovo utente? \nConsulta i collegamenti alla nostra documentazione.",
"message": "Sei un nuovo utente? Consulta i link alla nostra documentazione.",
"description": "Are you a new user? Check links to our documentation."
}
}

View file

@ -8,7 +8,7 @@
"description": "Add button title text on Site Preferences tab."
},
"editSitePreferenceButtonTitle": {
"message": "このサイト設定の URL を編集します。",
"message": "Edit URL for this Site Preferences.",
"description": "Edit button title text on Site Preferences tab."
},
"allowIframeButtonTitle": {
@ -20,7 +20,7 @@
"description": "Connect button title text."
},
"copyDebugInfoButtonTitle": {
"message": "クリップボードにデバッグ情報をコピー",
"message": "Copy debug info to clipboard",
"description": "Copy debug info button title text on About tab."
},
"dismissHttpAuthButtonTitle": {
@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "String Field",
"message": "文字列フィールド",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Submit Button",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "ユーザー名フィールドを選択してください",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "文字列フィールドを選択してください",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Choose submit button",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Please confirm your selection or choose more fields as String Fields.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "選択したものを確認するか、文字列フィールドとして別のフィールドを追加で選択してください。",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "キーボードの数字キーで入力フィールドを選択できます。",
@ -370,10 +362,6 @@
"message": "文字列フィールド",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Submit Button",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- ユーザー名なし -",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "期限切れ",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Retrieve credentials",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "エラー: 入力するフィールドを見つけられません。",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "サイト設定",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "情報",
"description": "About page header."
@ -723,7 +703,7 @@
"description": "Add button text."
},
"optionsButtonEdit": {
"message": "編集",
"message": "Edit",
"description": "Edit button text."
},
"optionsButtonUpdate": {
@ -750,10 +730,6 @@
"message": "設定をエクスポート",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Reset all settings",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "新しいパスワードを保存するデフォルトのグループ:",
"description": "Default group options text."
@ -811,7 +787,7 @@
"description": "Automatically fill-in single credential entry checkbox text."
},
"optionsCheckboxAutoFillRelevantCredential": {
"message": "関連する資格情報を自動的に補完する",
"message": "Automatically fill in relevant credential entries",
"description": "Automatically fill-in relevant credential entry checkbox text."
},
"optionsCheckboxAutoFillSingleTotp": {
@ -855,11 +831,11 @@
"description": "Show login notifications checkbox text."
},
"optionsDefaultPasswordManager": {
"message": "デフォルトのパスワードマネージャーに設定する",
"message": "Set as default password manager",
"description": "Default password manager checkbox text."
},
"optionsDefaultPasswordManagerHelpText": {
"message": "KeePassXC-Browser をブラウザーのデフォルトのパスワードマネージャーに設定します",
"message": "Sets KeePassXC-Browser as the default password manager for the browser.",
"description": "Default password manager help text."
},
"optionsRedirectAllowance": {
@ -871,7 +847,7 @@
"description": "Redirect allowance help text."
},
"optionsCheckboxAutoFillAndSend": {
"message": "HTTP ベーシック認証への資格情報の補完を許可する",
"message": "HTTP Basic 認証の資格情報の入力を許可する",
"description": "Allow filling HTTP Basic Auth credentials checkbox text."
},
"optionsDebugLogging": {
@ -959,7 +935,7 @@
"description": "OTP field icon option help text."
},
"optionsHideIconsAlertText": {
"message": "必要に応じて Shift + クリックでアイコンを隠すこともできます。",
"message": "You can also hide icons manually when needed with shift+click.",
"description": "Hide icons alert text text."
},
"optionsAutoRetrieveCredentialsHelpText": {
@ -975,7 +951,7 @@
"description": "Auto-Fill Single Entry option help text."
},
"optionsAutoFillRelevantCredentialHelpText": {
"message": "2 回目の入力が必要な場合、関連する資格情報を自動的に補完します。",
"message": "Automatically fill in the relevant credential if a second fill is needed.",
"description": "Auto-Fill Relevant Credential option help text."
},
"optionsAutoFillSingleEntryWarning": {
@ -1055,13 +1031,9 @@
"description": "Settings page info text about the latest KeePassXC version."
},
"optionsKeePassXCVersionRequired": {
"message": "次のバージョン以上の KeePassXC が必要です: $1",
"message": "次のバージョンの KeePassXC が必要です: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "デフォルト: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "現在の設定を上書きします。本当に設定ファイルをインポートしますか?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "All settings will be reset to defaults. Are you sure?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "KeePassXC-Browser に接続されているデータベースです。",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "ページの URL",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Features",
"optionsColumnIgnore": {
"message": "無視",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "自動送信を無効にする",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Disable passkeys",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "すべての機能を無効にする",
"description": "Site Preferences option selection."
@ -1435,7 +1399,7 @@
"description": "Lock database button title text."
},
"welcomeText": {
"message": "KeePassXC-Browser にようこそ!",
"message": "Welcome to KeePassXC-Browser!",
"description": "Main title of Getting Started page."
},
"documentationGettingStarted": {

View file

@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "String Field",
"message": "문자열 필드",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Submit Button",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "사용자 이름 필드 선택",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "문자열 필드 선택",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Choose submit button",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Please confirm your selection or choose more fields as String Fields.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "선택 사항을 확인하거나 더 많은 <em>문자열 필드</em>를 선택하십시오.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "키보드에서 숫자를 눌러서 입력 필드를 선택할 수 있습니다.",
@ -370,10 +362,6 @@
"message": "문자열 필드",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Submit Button",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- 사용자 이름 없음 -",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "만료됨",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Retrieve credentials",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "오류:\n입력할 필드를 찾을 수 없습니다.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Site Preferences",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "정보",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "설정 내보내기",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Reset all settings",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "새 암호를 저장할 기본 그룹:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "필요한 KeePassXC 버전: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "기본값: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "현재 설정을 덮어쓸 것입니다. 설정 파일을 가져오시겠습니까?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "All settings will be reset to defaults. Are you sure?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "다음 KeePassXC 데이터베이스가 KeePassXC-브라우저에 연결되어 있습니다.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "페이지 URL",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Features",
"optionsColumnIgnore": {
"message": "무시",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "자동 제출 비활성화",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Disable passkeys",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "모든 기능 비활성화",
"description": "Site Preferences option selection."

View file

@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "String Field",
"message": "Eilutės laukas",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Submit Button",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Pasirinkite vartotojo vardo lauką",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Pasirinkite eilučių laukus",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Choose submit button",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Please confirm your selection or choose more fields as String Fields.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Patvirtinkite savo pasirinkimą arba pasirinkite daugiau laukų kaip eilutės laukus.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Taip pat galite naudoti skaičius, kad pasirinktumėte įvesties laukus iš klaviatūros.",
@ -370,10 +362,6 @@
"message": "Eilučių laukai",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Submit Button",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- nėra naudotojo vardo -",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "Nebegalioja",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Retrieve credentials",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Klaida: nepavyksta rasti pildomų laukų.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Svetainės nuostatos",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Apie",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Eksportuoti nustatymus",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Reset all settings",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Numatytoji naujų slaptažodžių įrašymo grupė:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Reikalinga KeePassXC versija: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Numatytoji reikšmė: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Dabartiniai nustatymai bus nepaisomi. Ar tikrai norite importuoti nustatymų failą?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "All settings will be reset to defaults. Are you sure?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Duomenų bazės, prijungtos prie \"KeePassXC-Browser\".",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "Puslapio URL",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Features",
"optionsColumnIgnore": {
"message": "Nepaisyti",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Automatinio pateikimo išjungimas",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Disable passkeys",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Išjungti visas ypatybes",
"description": "Site Preferences option selection."

View file

@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "String Field",
"message": "Tekstfelt",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Submit Button",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Velg et brukernavnfelt",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Velg tekstfelt",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Choose submit button",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Please confirm your selection or choose more fields as String Fields.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Bekreft utvalget, eller velg flere felt som tekstfelt.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Du kan også bruke tallene til å velge innputtfeltene fra tastatur.",
@ -370,10 +362,6 @@
"message": "Tekstfelt",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Submit Button",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- ikke noe brukernavn -",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "Utløpt",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Retrieve credentials",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Feil: Finner ikke felt å fylle ut.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Site Preferences",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Om",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Eksporter innstillinger",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Reset all settings",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Standardgruppe for lagring av nye passord:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Krever KeePassXC-versjon: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Standard: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Gjeldende innstillinger vil bli overstyrt. Vil du virkelig importere filen med nye innstillinger?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "All settings will be reset to defaults. Are you sure?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Databaser koblet til KeePassXCBrowser.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "Nettside-URL",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Features",
"optionsColumnIgnore": {
"message": "Ignorere",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Deaktiver automatisk sending",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Disable passkeys",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Deaktiver alle funksjoner",
"description": "Site Preferences option selection."

View file

@ -322,10 +322,6 @@
"message": "Tekenreeksveld",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Knop Indienen",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Kies een gebruikersnaamveld",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Kies tekenreeksvelden",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Knop Indienen kiezen",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Bevestig de selectie of kies meer velden als tekenreeksvelden.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Bevestig je selectie of stel meerdere velden in als <em>tekenreeksvelden</em>.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Je kunt tevens gebruikmaken van de numerieke toetsen op je toetsenbord.",
@ -370,10 +362,6 @@
"message": "Tekenreeksvelden",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Knop Indienen",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- geen gebruikersnaam -",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "Verlopen",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Inloggegevens ophalen",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Fout: geen invulvelden aangetroffen.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Websitevoorkeuren",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Instellingen",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Over",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Instellingen exporteren",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Alle instellingen opnieuw instellen",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Standaardgroep voor het opslaan van nieuwe wachtwoorden:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Vereiste KeePassXC-versie: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "De extensie vereist KeePassXC-versie: $1. Eerdere versies kunnen ongewenst gedrag veroorzaken.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Standaard: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "De huidige instellingen worden overschreven. Weet je zeker dat je het instellingenbestand wilt importeren?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "Alle instellingen worden teruggezet naar standaardinstellingen. Weet je het zeker?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Databases verbonden met KeePassXC-Browser.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "Pagina-url",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Functies",
"optionsColumnIgnore": {
"message": "Negeren",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Automatisch indienen uitschakelen",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Passkeys uitschakelen",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Alle functies uitschakelen",
"description": "Site Preferences option selection."

View file

@ -322,10 +322,6 @@
"message": "Pole ciągu",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Przycisk przesyłania",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Wybierz pole nazwy użytkownika",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Wybierz pola ciągów",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Wybierz przycisk przesyłania",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Potwierdź swój wybór lub wybierz więcej pól jako pola ciągów.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Możesz także użyć liczb, aby wybrać pola wprowadzania z klawiatury.",
@ -370,10 +362,6 @@
"message": "Pola ciągów",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Przycisk przesyłania",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- brak nazwy użytkownika -",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "Wygasłe",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Pobierz dane uwierzytelniające",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Błąd:\nNie można znaleźć pól do wypełnienia.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Ustawienia witryn",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Ustawienia",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Informacje",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Eksportuj ustawienia",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Resetuj wszystkie ustawienia",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Domyślna grupa do zapisywania nowych haseł:",
"description": "Default group options text."
@ -963,7 +939,7 @@
"description": "Hide icons alert text text."
},
"optionsAutoRetrieveCredentialsHelpText": {
"message": "KeePassXC-Browser natychmiast pobierze dane uwierzytelniające po aktywowaniu karty.",
"message": "KeePassXC-Browser natychmiast odbierze dane uwierzytelniające po aktywowaniu karty.",
"description": "Auto-Retrive Credentials option help text."
},
"optionsAutomaticReconnectHelpText": {
@ -1058,10 +1034,6 @@
"message": "Wymaga KeePassXC w wersji: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "Rozszerzenie wymaga KeePassXC w wersji $1. Starsze wersje mogą powodować niepożądane zachowania.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Domyślne: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Bieżące ustawienia zostaną zastąpione. Czy na pewno chcesz zaimportować plik ustawień?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "Wszystkie ustawienia zostaną przywrócone do wartości domyślnych. Czy na pewno chcesz to zrobić?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Bazy danych podłączone do KeePassXC-Browser.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "Adres URL strony",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Funkcje",
"optionsColumnIgnore": {
"message": "Ignorowanie",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Wyłącz automatyczne przesyłanie",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Wyłącz klucze dostępu",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Wyłącz wszystkie funkcje",
"description": "Site Preferences option selection."

View file

@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "Campo do Texto",
"message": "Campo para texto",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Botão Enviar",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Escolha um campo para o nome de usuário",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Escolha os campos para texto",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Selecione o botão enviar",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Por favor, confirme sua seleção ou escolha mais campos como Campos de Texto.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Confirme a sua seleção ou escolha mais campos como texto.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Você também pode usar os números para escolher os campos de entrada do teclado.",
@ -370,10 +362,6 @@
"message": "Campos para texto",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Botão Enviar",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- sem usuário -",
"description": "Shown when no username is set in the credentials."
@ -383,7 +371,7 @@
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Preenchimento de credenciais bloqueado em iframe não confiável.",
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
@ -406,10 +394,6 @@
"message": "Expirado",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Recuperar credenciais",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Erro: Não foi possível encontrar campos para preencher.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Preferências do Site",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Configurações",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Sobre",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Exportar as configurações",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Redefinir todas as configurações",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Grupo padrão para salvar novas senhas:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Precisa da versão do KeePassXC: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "A extensão requer a versão KeePassXC: $1. Versões anteriores podem causar comportamentos indesejados.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Padrão: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "As configurações atuais serão substituídas. Deseja realmente importar o arquivo de configuração?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "Todas as configurações serão redefinidas para os padrões. Tem certeza?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Os seguintes bancos de dados do KeePassXC estão conectados ao KeePassXC-Browser.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "URL da página",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Características",
"optionsColumnIgnore": {
"message": "Ignorar",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Desativar o envio automático",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Desativar chaves de acesso",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Desativar todos os recursos",
"description": "Site Preferences option selection."

View file

@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "String Field",
"message": "Campo de cadeia",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Submit Button",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Escolha o campo para o nome de utilizador",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Escolha os campos de cadeia",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Choose submit button",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Please confirm your selection or choose more fields as String Fields.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Confirme a sua seleção ou escolha mais campos como campos de cadeia",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Também pode usar os números para escolher os campos com o teclado",
@ -370,10 +362,6 @@
"message": "Campos de cadeia",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Submit Button",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- sem nome de utilizador -",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "Caducada",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Retrieve credentials",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Erro: não foram encontrados campos que possam ser preenchidos",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Preferências do site",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Acerca",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Exportar definições",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Reset all settings",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Grupo padrão para as novas palavras-passe:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Necessita de KeePassXC, versão: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Padrão: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "As definições atuais serão substituídas. Tem a certeza de que deseja importar os dados do ficheiro?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "All settings will be reset to defaults. Are you sure?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Bases de dados conectadas com KeePassXC-Browser",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "URL da página",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Features",
"optionsColumnIgnore": {
"message": "Ignorar",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Desativar submissão automática",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Disable passkeys",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Desativar todas as funcionalidades",
"description": "Site Preferences option selection."

View file

@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "String Field",
"message": "String field",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Submit Button",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Choose a username field",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Choose String Fields",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Choose submit button",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Please confirm your selection or choose more fields as String Fields.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Confirmați selecția sau alegeți mai multe câmpuri ca și câmpuri de tip șir de caractere.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "De asemenea, puteți utiliza numerele pentru a alege câmpurile de intrare de la tastatură.",
@ -370,10 +362,6 @@
"message": "String Fields",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Submit Button",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "-niciun nume de utilizator-",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "Expirat",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Retrieve credentials",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Eroare: Imposibil de găsit câmpuri de completat.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Site Preferences",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Despre",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Setări de export",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Reset all settings",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Grup implicit pentru salvarea parolelor noi:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Necesită versiunea de KeePassXC: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Implicit: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Setările curente vor fi anulate. Doriți cu adevărat să importați fișierul cu setări?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "All settings will be reset to defaults. Are you sure?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Baze de date conectate la KeePassXC-Browser.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "URL Site",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Features",
"optionsColumnIgnore": {
"message": "Dezactivare funcții",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Dezactivează funcția de Auto-Trimite",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Disable passkeys",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Dezactivează toate funcțiile",
"description": "Site Preferences option selection."

View file

@ -4,11 +4,11 @@
"description": "Name of the extension."
},
"addSitePreferenceButtonTitle": {
"message": "Добавить настройки сайта для этого URL-адреса.",
"message": "Добавить настройки сайта для этого URL.",
"description": "Add button title text on Site Preferences tab."
},
"editSitePreferenceButtonTitle": {
"message": "Изменить URL-адрес для этих настроек сайта.",
"message": "Edit URL for this Site Preferences.",
"description": "Edit button title text on Site Preferences tab."
},
"allowIframeButtonTitle": {
@ -20,7 +20,7 @@
"description": "Connect button title text."
},
"copyDebugInfoButtonTitle": {
"message": "Копировать отладочную информацию в буфер обмена",
"message": "Copy debug info to clipboard",
"description": "Copy debug info button title text on About tab."
},
"dismissHttpAuthButtonTitle": {
@ -244,7 +244,7 @@
"description": "Error notification shown when not connected to KeePassXC."
},
"errorCurrentDatabaseNotConnected": {
"message": "Текущая база данных не подключена.",
"message": "Current database is not connected.",
"description": "Error notification shown when current database is not connected during action."
},
"passwordGeneratorErrorTooLong": {
@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "String Field",
"message": "Строковое поле",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Submit Button",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Выберите поле логина",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Выберите строковые поля",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Choose submit button",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Please confirm your selection or choose more fields as String Fields.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Пожалуйста, подтвердите свой выбор или выберите больше строковых полей.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Вы также можете использовать числа для выбора полей ввода с клавиатуры.",
@ -370,10 +362,6 @@
"message": "Строковые поля",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Submit Button",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- нет логина -",
"description": "Shown when no username is set in the credentials."
@ -383,7 +371,7 @@
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Заполнение учётных данных заблокировано в ненадежном iframe.",
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
@ -406,10 +394,6 @@
"message": "Истекло",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Retrieve credentials",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "ОШИБКА: нет полей для заполнения.",
"description": "Alert message when no fields are found to fill in."
@ -647,7 +631,7 @@
"description": "Dismiss button text when in HTTP Authentication popup."
},
"optionsDefaultSettingsTitle": {
"message": "Настройки по умолчанию",
"message": "Default settings",
"description": "Default settings title for Getting Started page."
},
"optionsWelcomeTitle": {
@ -694,10 +678,6 @@
"message": "Настройки сайта",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Настройки",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Сведения",
"description": "About page header."
@ -723,7 +703,7 @@
"description": "Add button text."
},
"optionsButtonEdit": {
"message": "Изменить",
"message": "Edit",
"description": "Edit button text."
},
"optionsButtonUpdate": {
@ -750,10 +730,6 @@
"message": "Экспорт настроек",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Сбросить все настройки",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Группа по умолчанию для сохранения новых паролей:",
"description": "Default group options text."
@ -811,7 +787,7 @@
"description": "Automatically fill-in single credential entry checkbox text."
},
"optionsCheckboxAutoFillRelevantCredential": {
"message": "Автоматически заполнять соответствующие записи учётных данных",
"message": "Automatically fill in relevant credential entries",
"description": "Automatically fill-in relevant credential entry checkbox text."
},
"optionsCheckboxAutoFillSingleTotp": {
@ -855,11 +831,11 @@
"description": "Show login notifications checkbox text."
},
"optionsDefaultPasswordManager": {
"message": "Установить как менеджер паролей по умолчанию",
"message": "Set as default password manager",
"description": "Default password manager checkbox text."
},
"optionsDefaultPasswordManagerHelpText": {
"message": "Устанавливает KeePassXC-Browser в качестве менеджера паролей по умолчанию для браузера.",
"message": "Sets KeePassXC-Browser as the default password manager for the browser.",
"description": "Default password manager help text."
},
"optionsRedirectAllowance": {
@ -959,7 +935,7 @@
"description": "OTP field icon option help text."
},
"optionsHideIconsAlertText": {
"message": "При необходимости вы также можете скрыть значки вручную, нажав Shift+клик.",
"message": "You can also hide icons manually when needed with shift+click.",
"description": "Hide icons alert text text."
},
"optionsAutoRetrieveCredentialsHelpText": {
@ -975,7 +951,7 @@
"description": "Auto-Fill Single Entry option help text."
},
"optionsAutoFillRelevantCredentialHelpText": {
"message": "Автоматически заполнить соответствующие учётные данные, если требуется повторное заполнение.",
"message": "Automatically fill in the relevant credential if a second fill is needed.",
"description": "Auto-Fill Relevant Credential option help text."
},
"optionsAutoFillSingleEntryWarning": {
@ -1058,10 +1034,6 @@
"message": "Требуемая версия KeePassXC: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "По умолчанию: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Текущие настройки будут переопределены. Вы действительно хотите импортировать файл настроек?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "Все настройки будут сброшены на значения по умолчанию. Вы уверены?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Базы данных, подключенные к KeePassXC-Browser.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "Адрес страницы",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Функции",
"optionsColumnIgnore": {
"message": "Игнорировать",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Выключить автоподстановку",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Отключить ключи доступа",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Отключить все функции",
"description": "Site Preferences option selection."
@ -1435,31 +1399,31 @@
"description": "Lock database button title text."
},
"welcomeText": {
"message": "Добро пожаловать в KeePassXC-Browser!",
"message": "Welcome to KeePassXC-Browser!",
"description": "Main title of Getting Started page."
},
"documentationGettingStarted": {
"message": "Наше руководство по началу работы поможет вам быстро приступить к работе.",
"message": "Our Getting Started Guide will get you up and running quickly.",
"description": "Getting Started document text."
},
"documentationUserGuide": {
"message": "Ищете более подробную документацию? Наше руководство пользователя вам поможет.",
"message": "Looking for more comprehensive documentation? Our User Guide is there to help.",
"description": "User Guide text."
},
"documentationTroubleshootingGuide": {
"message": "Нужна помощь в устранении неполадок интеграции с браузером? Ознакомьтесь с руководством по устранению неполадок.",
"message": "Need help troubleshooting the browser integration? Check the Troubleshooting Guide.",
"description": "Troubleshooting Guide text."
},
"gettingStartedWelcomeText": {
"message": "Добро пожаловать в KeePassXC-Browser, официальное расширение браузера для KeePassXC.",
"message": "Welcome to KeePassXC-Browser, the official browser extension for KeePassXC.",
"description": "Welcome to KeePassXC-Browser, the official browser extension for KeePassXC."
},
"gettingStartedSecondWelcomeText": {
"message": "Пожалуйста, проверьте настройки по умолчанию и выберите все предпочтительные параметры.",
"message": "Please go through the default settings and check all your preferred options.",
"description": "Please go through the default settings and check all your preferred options."
},
"gettingStartedNewUser": {
"message": "Вы новый пользователь? Посмотрите ссылки на нашу документацию.",
"message": "Are you a new user? Check links to our documentation.",
"description": "Are you a new user? Check links to our documentation."
}
}

View file

@ -322,10 +322,6 @@
"message": "Strängfält",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Skicka-knapp",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Välj ett användarnamn-fält",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Välj sträng-fält",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Välj Skicka-knapp",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Bekräfta ditt val eller välj fler fält som strängfält.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Vänligen bekräfta ditt val eller välj fler fält som <em>Strängfält</em>.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "You can also use the numbers to choose the input fields from keyboard.",
@ -370,10 +362,6 @@
"message": "Strängfält",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Skicka-knapp",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- inget användarnamn -",
"description": "Shown when no username is set in the credentials."
@ -406,10 +394,6 @@
"message": "Förfallen",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Hämta autentiseringsuppgifter",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Fel:\nKan inte hitta fält att fylla i.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Sidinställningar",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Inställningar",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Om",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Exportera inställningar",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Återställ alla inställningar",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Default group for saving new passwords:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Kräver KeePassXC version: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Förinställt: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Nuvarande inställningar kommer att skrivas över. Vill du verkligen importera inställningsfilen?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "Alla inställningar kommer att återställas till standardinställning. Är du säker?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Följande KeePassXC-databaser är anslutna till KeePassXC-Browser.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "Sid-URL",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Funktioner",
"optionsColumnIgnore": {
"message": "Ignorera",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Skicka inte automatiskt",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Avaktivera passnycklar",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Inaktivera alla funktioner",
"description": "Site Preferences option selection."

View file

@ -1,6 +1,6 @@
{
"extensionDescription": {
"message": "Modern tarayıcılar için KeePassXC bütünleştirmesi",
"message": "Modern tarayıcılar için KeePassXC bütünleşmesi",
"description": "Name of the extension."
},
"addSitePreferenceButtonTitle": {
@ -319,13 +319,9 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "Yazı alanı",
"message": "Metin alanı",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Gönder düğmesi",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Kullanıcı adı alanını seçin",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -339,16 +335,12 @@
"description": "Choosing a TOTP field text when choosing Custom Login Fields."
},
"defineChooseStringFields": {
"message": "Yazı alanlarını seçin",
"message": "Metin alanlarını seçin",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Gönder düğmesini seçin",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Lütfen seçiminizi onaylayın ya da yazı alanları olarak ek alanlar seçin.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Lütfen seçiminizi onaylayın ya da dizge alanları olarak ek alanlar seçin.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Giriş alanlarını tuş takımıyla seçmek için sayıları kullanabilirsiniz.",
@ -367,13 +359,9 @@
"description": "General text for password."
},
"stringFields": {
"message": "Yazı alanları",
"message": "Metin alanları",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Gönder düğmesi",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- kullanıcı adı yok -",
"description": "Shown when no username is set in the credentials."
@ -383,7 +371,7 @@
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Güvenilmeyen bir iFrame nedeniyle kimlik doğrulama bilgilerinin doldurulması engellendi.",
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
@ -406,10 +394,6 @@
"message": "Süresi dolmuş",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Kimlik doğrulama bilgilerini al",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Hata: Doldurulacak alanlar bulunamadı.",
"description": "Alert message when no fields are found to fill in."
@ -419,7 +403,7 @@
"description": "Message shown when no password fields are found."
},
"fieldsPasswordFillNotAccepted": {
"message": "Parolanın bir düz yazı alanına yazılması engellenir.",
"message": "Parolanın bir düz metin alanına yazılması engellenir.",
"description": "Message shown when password fill to a plain text field is prevented."
},
"rememberNothingChanged": {
@ -694,10 +678,6 @@
"message": "Site ayarları",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Ayarlar",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Hakkında",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Ayarları dışa aktar",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Tüm ayarları sıfırla",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Yeni parolaların kaydedileceği varsayılan grup:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "KeePassXC $1 sürümünü gerektirir",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "Bu eklenti için KeePassXC $1 sürümü gereklidir. Önceki sürümler istenmeyen davranışlara yol açabilir.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Varsayılan: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Var olan ayarlar değiştirilecek. Ayarlar dosyasını içe aktarmak istediğinize emin misiniz?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "Tüm ayarları varsayılan değerlerine sıfırlamak istediğinize emin misiniz? ",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "KeePassXC-Browser ile bağlantısı kurulmuş KeePassXC veri tabanları.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "Sayfa adresi",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Özellikler",
"optionsColumnIgnore": {
"message": "Yok say",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Otomatik gönderim yapılmasın",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Geçiş anahtarları kullanılmasın",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Tüm özellikleri kapat",
"description": "Site Preferences option selection."

View file

@ -188,7 +188,7 @@
"description": "Credential is excluded."
},
"errorMessagePasskeysRequestCanceled": {
"message": "Запит на використання ключів доступу скасовано.",
"message": "Запит на Passkeys-ключі скасовано.",
"description": "Passkeys request canceled."
},
"errorMessagePasskeysInvalidUserVerification": {
@ -224,7 +224,7 @@
"description": "Wait for timer to expire."
},
"errorMessagePasskeysUnknownError": {
"message": "Невідома помилка ключів доступу.",
"message": "Невідома помилка паролів.",
"description": "Unknown passkeys error."
},
"errorMessagePasskeysInvalidChallenge": {
@ -322,10 +322,6 @@
"message": "Поле рядка",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "Кнопка «Відправити»",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "Виберіть поле імені користувача",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "Оберіть поля рядків",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "Виберіть кнопку «Відправити»",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "Підтвердьте свій вибір або виберіть більше полів як рядкові поля.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "Підтвердьте свій вибір або виберіть більше полів як рядків.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "Ви також можете використовувати числа для вибору полів введення з клавіатури.",
@ -370,10 +362,6 @@
"message": "Поля рядків",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "Кнопка «Відправити»",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- без імені користувача -",
"description": "Shown when no username is set in the credentials."
@ -383,7 +371,7 @@
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Заповнення облікових даних заблоковано в ненадійному iframe.",
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
@ -406,10 +394,6 @@
"message": "Протерміновано",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "Отримати облікові дані",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "Помилка: Не вдалося знайти поля для заповнення.",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "Налаштування сайту",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Налаштування",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Про розширення",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "Експортувати налаштування",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Скинути всі налаштування",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Типова група для збереження нових паролів:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "Потребує версію KeePassXC: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "Для розширення потрібна версія KeePassXC: $1. Більш ранні версії можуть спричинити небажану поведінку.",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "Типово: $1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "Поточні налаштування будуть перевизначені. Ви дійсно хочете імпортувати файл налаштувань?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "Всі налаштування будуть скинуті. Ви впевнені?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "Під'єднані бази даних до KeePassXC-Browser.",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "URL-адреса сторінки",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "Особливості",
"optionsColumnIgnore": {
"message": "Ігнорувати",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "Вимкнути автовідправлення",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Вимкнути ключі доступу",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Вимкнути всі можливості",
"description": "Site Preferences option selection."
@ -1399,11 +1363,11 @@
"description": "Extension title in settings page"
},
"optionsPasskeysTitle": {
"message": "Ключі доступу",
"message": "Passkey-ключі",
"description": "Passkeys settings title in settings page."
},
"optionsPasskeysEnable": {
"message": "Увімкнути ключі доступу",
"message": "Увімкнути паролі",
"description": "Enabled passkeys option text."
},
"optionsPasskeysEnableHelpText": {
@ -1411,11 +1375,11 @@
"description": "Passkeys option help text."
},
"optionsPasskeysEnableFallback": {
"message": "Увімкнути резервний метод для ключів доступу",
"message": "Увімкнути резервний варіант паролів",
"description": "Enabled passkeys fallback option text."
},
"optionsPasskeysEnableFallbackHelpText": {
"message": "Якщо увімкнено, невдалий або скасований запит до KeePassXC спричинить власний внутрішній запит браузера на ключі доступу. Якщо вимкнено, необхідне підключення до KeePassXC, а скасований запит буде невдалим. За замовчуванням: увімкнено.",
"message": "Коли увімкнено, невдала або скасована спроба запиту до KeePassXC викликатиме запит на паролі через внутрішню систему браузера. Якщо вимкнено, для підключення до KeePassXC буде потрібно, і скасований запит зазнає невдачі. За замовчуванням: увімкнено.",
"description": "Passkeys fallback option help text."
},
"openNewTab": {

File diff suppressed because it is too large Load diff

View file

@ -322,10 +322,6 @@
"message": "字符串字段",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "提交按钮",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "选择用户名字段",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "选择字符串字段",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "选择提交按钮",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "请确认您的选择,或选择更多字段作为字符串字段。",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "您也可以使用键盘的数字键选择输入字段。",
@ -370,10 +362,6 @@
"message": "字符串字段",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "提交按钮",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- 没有用户名 -",
"description": "Shown when no username is set in the credentials."
@ -383,7 +371,7 @@
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "凭据填充在不受信任的 iframe 中被阻止。",
"message": "凭据填充在不可信 iframe 中被阻止。",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
@ -406,10 +394,6 @@
"message": "已过期",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "检索凭据",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "错误:无法找到可填写的字段。",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "网站设置",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "设置",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "关于",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "导出配置",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "重置所有设置",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "保存新密码的默认群组:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "需要 KeePassXC 版本:$1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "该扩展需要 KeePassXC 版本:$1。早期版本可能会导致意外行为。",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "默认值:$1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "当前配置将会被覆盖,确定导入?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "所有设置都将重置为默认。您确定吗?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "已连接到 KeePassXC-Browser 的数据库。",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "页面 URL",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "功能",
"optionsColumnIgnore": {
"message": "忽略",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "禁用自动提交",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "禁用通行密钥",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "禁用所有功能",
"description": "Site Preferences option selection."

View file

@ -303,7 +303,7 @@
"description": "More button text when choosing Custom Login Fields."
},
"defineReset": {
"message": "重",
"message": "重",
"description": "Reset button text when choosing Custom Login Fields."
},
"defineConfirm": {
@ -322,10 +322,6 @@
"message": "字串欄位",
"description": "Text for string field."
},
"defineSubmitButton": {
"message": "送出按鈕",
"description": "Text for Submit Button."
},
"defineChooseUsername": {
"message": "選擇用戶名稱欄位",
"description": "Choosing a username field text when choosing Custom Login Fields."
@ -342,13 +338,9 @@
"message": "選擇字串欄位",
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
},
"defineChooseSubmitButton": {
"message": "選擇送出按鈕",
"description": "Choose submit button selection text when choosing Custom Login Fields."
},
"defineHelpText": {
"message": "請確認您的選擇,或選擇更多欄位作為字串欄位",
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
"message": "請確認您的選擇,或是選擇更多欄位作為<em>字串欄位</em>。",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
},
"defineKeyboardText": {
"message": "您也可以使用鍵盤的數字鍵選擇輸入欄位。",
@ -370,10 +362,6 @@
"message": "字串欄位",
"description": "General text for a String Fields."
},
"submitButton": {
"message": "送出按鈕",
"description": "Custom Submit Button text."
},
"credentialsNoUsername": {
"message": "- 無用戶名稱 -",
"description": "Shown when no username is set in the credentials."
@ -383,7 +371,7 @@
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "憑證填入在不可信 iframe 而被阻止。",
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
@ -406,10 +394,6 @@
"message": "已過期",
"description": "If a credential is expired, this is appended to the title label."
},
"credentialsRetrieveButton": {
"message": "檢索憑證",
"description": "Manual retrieve credentials button text."
},
"fieldsFill": {
"message": "錯誤:\n找不到該填入的欄位。",
"description": "Alert message when no fields are found to fill in."
@ -694,10 +678,6 @@
"message": "網站設定",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "設定",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "關於",
"description": "About page header."
@ -750,10 +730,6 @@
"message": "導出配置",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "重設所有設定",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "儲存新密碼的預設群組:",
"description": "Default group options text."
@ -1058,10 +1034,6 @@
"message": "需要 KeePassXC 版本: $1",
"description": "KeePassXC version requirement text."
},
"optionsMinimumKeePassXCVersionRequired": {
"message": "此擴展需要 KeePassXC 版本:$1。更早的版本可能導致意外行為。",
"description": "KeePassXC minimum version requirement text."
},
"optionsDefault": {
"message": "預設值:$1",
"description": "Default setting text."
@ -1098,10 +1070,6 @@
"message": "目前設定將會被覆寫。真的要匯入設定檔案?",
"description": "Import settings confirmation dialog help text."
},
"optionsResetSettingsDialogText": {
"message": "所有設定都將被重置為預設值。您確定嗎?",
"description": "Reset all settings dialog text."
},
"optionsConnectedDatabasesText": {
"message": "已連接至 KeePassXC-Browser 的資料庫。",
"description": "Info text about connected databases."
@ -1146,8 +1114,8 @@
"message": "網頁 URL",
"description": "Site Preferences list column title."
},
"optionsColumnFeatures": {
"message": "特性",
"optionsColumnIgnore": {
"message": "忽略",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1178,10 +1146,6 @@
"message": "停用自動送出",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "停用通行密鑰",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "停用所有功能",
"description": "Site Preferences option selection."

View file

@ -83,7 +83,7 @@ browserAction.generateIconName = async function(iconType) {
style = page.settings.colorTheme;
}
}
const filetype = (page.isFirefox || page.isSafari) ? 'svg' : 'png';
const filetype = (isFirefox() ? 'svg' : 'png');
return `/icons/toolbar/${style}/${name}.${filetype}`;
};

View file

@ -400,7 +400,7 @@ function onDisconnected() {
page.clearAllLogins();
keepass.updatePopup('cross');
keepass.updateDatabaseHashToContent();
logError(`Failed to connect: ${(browser.runtime.lastError === null ? 'Unknown error' : browser.runtime.lastError?.message)}`);
logError(`Failed to connect: ${(browser.runtime.lastError === null ? 'Unknown error' : browser.runtime.lastError.message)}`);
}
keepassClient.onNativeMessage = function(response) {

View file

@ -241,10 +241,6 @@ kpxcEvent.sendBackToTabs = async function(tab, args = []) {
}
};
kpxcEvent.getFeaturesList = async function() {
return keepass.featuresList;
};
// All methods named in this object have to be declared BEFORE this!
kpxcEvent.messageHandlers = {
'add_credentials': keepass.addCredentials,
@ -253,6 +249,7 @@ kpxcEvent.messageHandlers = {
'banner_set_position': page.setBannerPosition,
'check_database_hash': keepass.checkDatabaseHash,
'check_update_keepassxc': kpxcEvent.onCheckUpdateKeePassXC,
'compare_versions': kpxcEvent.compareMultipleVersions,
'create_new_group': keepass.createNewGroup,
'enable_automatic_reconnect': keepass.enableAutomaticReconnect,
'disable_automatic_reconnect': keepass.disableAutomaticReconnect,
@ -264,7 +261,6 @@ kpxcEvent.messageHandlers = {
'get_database_hash': keepass.getDatabaseHash,
'get_database_groups': keepass.getDatabaseGroups,
'get_error_message': keepass.getErrorMessage,
'get_features_list': kpxcEvent.getFeaturesList,
'get_keepassxc_versions': kpxcEvent.onGetKeePassXCVersions,
'get_login_list': page.getLoginList,
'get_status': kpxcEvent.onGetStatus,
@ -300,7 +296,6 @@ kpxcEvent.messageHandlers = {
'reconnect': kpxcEvent.onReconnect,
'remove_credentials_from_tab_information': kpxcEvent.onRemoveCredentialsFromTabInformation,
'request_autotype': keepass.requestAutotype,
'reset_all_settings': page.resetAllSettings,
'retrieve_credentials': page.retrieveCredentials,
'show_default_browseraction': browserAction.showDefault,
'update_credentials': keepass.updateCredentials,

View file

@ -6,15 +6,10 @@ httpAuth.requests = [];
httpAuth.pendingCallbacks = [];
httpAuth.init = function() {
if (page.isSafari) {
debugLogMessage('HTTP Basic Auth implementation is not supported in Safari.');
return;
}
let handleReq = httpAuth.handleRequestPromise;
let reqType = 'blocking';
if (!page.isFirefox) {
if (!isFirefox()) {
handleReq = httpAuth.handleRequestCallback;
reqType = 'asyncBlocking';
}

View file

@ -10,6 +10,12 @@ const contextMenuItems = [
{ title: tr('contextMenuRequestGlobalAutoType'), action: 'request_autotype' }
];
const menuContexts = [ 'editable' ];
if (isFirefox()) {
menuContexts.push('password');
}
const initListeners = async function() {
/**
* Generate information structure for created tab and invoke all needed
@ -109,8 +115,7 @@ const initListeners = async function() {
if (contextMenuItems.some(e => e.action === command)
|| command === 'redetect_fields'
|| command === 'choose_credential_fields'
|| command === 'retrieve_credentials_forced'
|| command === 'reopen_database'
|| command === 'retrive_credentials_forced'
|| command === 'reload_extension') {
const tab = await getCurrentTab();
if (tab?.id) {
@ -155,32 +160,26 @@ const initListeners = async function() {
});
};
const initContextMenuItems = async function() {
page.menuContexts = [ 'editable' ];
if (page.isFirefox) {
page.menuContexts.push('password');
}
const initContextMenuItems = async function() {
// Create context menu items
await browser.contextMenus.removeAll();
for (const item of contextMenuItems) {
try {
await browser.contextMenus.create({
title: item.title,
contexts: page.menuContexts,
contexts: menuContexts,
visible: item.visible,
id: item.id || item.action
});
} catch (e) {
logError(e);
}
}
}
};
(async () => {
try {
await keepass.migrateKeyRing();
await page.initBrowser();
await page.initSettings();
await page.initSitePreferences();
await page.initOpenedTabs();
@ -190,7 +189,7 @@ const initContextMenuItems = async function() {
await keepass.reconnect(null, 5000); // 5 second timeout for the first connect
await keepass.enableAutomaticReconnect();
await keepass.updateDatabase();
} catch (_e) {
} catch (e) {
logError('init.js failed');
}
})();

View file

@ -2,31 +2,20 @@
const keepass = {};
keepass.associated = { 'value': false, 'hash': null };
keepass.featuresList = {
downloadFaviconAfterSave: false,
newTotp: false,
passwordGenerator: false,
passkeys: false,
passkeysDefaultGroup: false,
requiredKeePassXCVersionFound: false,
};
keepass.cacheTimeout = 30 * 1000; // Milliseconds
keepass.keyPair = { publicKey: null, secretKey: null };
keepass.serverPublicKey = '';
keepass.clientID = '';
keepass.currentKeePassXC = '';
keepass.databaseHash = '';
keepass.isConnected = false;
keepass.isDatabaseClosed = true;
keepass.isEncryptionKeyUnrecognized = false;
keepass.isKeePassXCAvailable = false;
keepass.keyPair = { publicKey: null, secretKey: null };
keepass.isEncryptionKeyUnrecognized = false;
keepass.currentKeePassXC = '';
keepass.requiredKeePassXC = '2.3.1';
keepass.latestVersionUrl = 'https://api.github.com/repos/keepassxreboot/keepassxc/releases/latest';
keepass.cacheTimeout = 30 * 1000; // Milliseconds
keepass.databaseHash = '';
keepass.previousDatabaseHash = '';
keepass.reconnectLoop = null;
keepass.requiredKeePassXC = '2.6.0';
keepass.serverPublicKey = '';
const DEFAULT_FETCH_TIMEOUT = 5000; // ms
const MAX_RELATED_ORIGIN_LABELS = 60;
const kpActions = {
SET_LOGIN: 'set-login',
@ -183,7 +172,7 @@ keepass.generatePassword = async function(tab) {
return '';
}
if (!keepass.featuresList.passwordGenerator) {
if (!compareVersion(keepass.requiredKeePassXC, keepass.currentKeePassXC)) {
return '';
}
@ -240,7 +229,9 @@ keepass.associate = async function(tab) {
const response = await keepassClient.sendMessage(kpAction, tab, messageData, nonce, false, true);
if (response) {
keepass.setCryptoKey(response.id, idKey);
// Use public key as identification key with older KeePassXC releases
const savedKey = compareVersion('2.3.4', keepass.currentKeePassXC) ? idKey : key;
keepass.setCryptoKey(response.id, savedKey); // Save the new identification public key as id key for the database
keepass.associated.value = true;
keepass.associated.hash = response.hash || 0;
@ -344,13 +335,7 @@ keepass.getDatabaseHash = async function(tab, args = []) {
}
try {
const request = keepassClient.buildRequest(
kpAction,
keepassClient.encrypt(messageData, nonce),
nonce,
keepass.clientID,
triggerUnlock,
);
const request = keepassClient.buildRequest(kpAction, keepassClient.encrypt(messageData, nonce), nonce, keepass.clientID, triggerUnlock);
const response = await keepassClient.sendNativeMessage(request, enableTimeout);
if (response.message && response.nonce) {
const res = keepassClient.decrypt(response.message, response.nonce);
@ -427,7 +412,6 @@ keepass.changePublicKeys = async function(tab, enableTimeout = false, connection
try {
const response = await keepassClient.sendNativeMessage(request, enableTimeout, connectionTimeout);
keepass.setcurrentKeePassXCVersion(response.version);
keepass.updateFeaturesList(response.version);
if (!keepassClient.verifyKeyResponse(response, key, incrementedNonce)) {
if (tab && page.tabs[tab.id]) {
@ -560,7 +544,7 @@ keepass.createNewGroup = async function(tab, args = []) {
keepass.getTotp = async function(tab, args = []) {
const [ uuid, oldTotp ] = args;
if (!keepass.featuresList.newTotpSupported) {
if (!compareVersion('2.6.1', keepass.currentKeePassXC, true)) {
return oldTotp;
}
@ -625,14 +609,11 @@ keepass.passkeysRegister = async function(tab, args = []) {
const kpAction = kpActions.PASSKEYS_REGISTER;
const nonce = keepassClient.getNonce();
const [ publicKey, origin ] = args;
const passkeyPublicKey = JSON.parse(JSON.stringify(publicKey));
const relatedOrigins = await keepass.getPasskeysRelatedOrigins(passkeyPublicKey?.rp?.id);
const messageData = {
action: kpAction,
publicKey: passkeyPublicKey,
publicKey: JSON.parse(JSON.stringify(publicKey)),
origin: origin,
relatedOrigins: relatedOrigins,
groupName: page?.settings?.defaultPasskeyGroup,
keys: keepass.getCryptoKeys()
};
@ -660,15 +641,13 @@ keepass.passkeysGet = async function(tab, args = []) {
const kpAction = kpActions.PASSKEYS_GET;
const nonce = keepassClient.getNonce();
const [ publicKey, origin ] = args;
const passkeyPublicKey = JSON.parse(JSON.stringify(publicKey));
const relatedOrigins = await keepass.getPasskeysRelatedOrigins(passkeyPublicKey?.rp?.id);
const publicKey = args[0];
const origin = args[1];
const messageData = {
action: kpAction,
publicKey: passkeyPublicKey,
publicKey: JSON.parse(JSON.stringify(publicKey)),
origin: origin,
relatedOrigins: relatedOrigins,
keys: keepass.getCryptoKeys()
};
@ -804,9 +783,13 @@ keepass.getCryptoKeys = function() {
keepass.enableAutomaticReconnect = async function() {
// Disable for Windows if KeePassXC is older than 2.3.4
if (!page.settings.autoReconnect) {
if (!page.settings.autoReconnect
|| (navigator.platform.toLowerCase().includes('win')
&& keepass.currentKeePassXC
&& !compareVersion('2.3.4', keepass.currentKeePassXC))) {
return;
}
if (keepass.reconnectLoop === null) {
keepass.reconnectLoop = setInterval(async () => {
if (!keepass.isKeePassXCAvailable) {
@ -824,9 +807,7 @@ keepass.disableAutomaticReconnect = function() {
keepass.reconnect = async function(tab = null, connectionTimeout = 1500) {
keepassClient.connectToNative();
keepass.generateNewKeyPair();
const keyChangeResult = await keepass
.changePublicKeys(tab, !!connectionTimeout, connectionTimeout)
.catch(() => false);
const keyChangeResult = await keepass.changePublicKeys(tab, !!connectionTimeout, connectionTimeout).catch(() => false);
// Change public keys timeout
if (!keyChangeResult) {
@ -882,9 +863,7 @@ keepass.setcurrentKeePassXCVersion = function(version) {
keepass.keePassXCUpdateAvailable = async function() {
const checkUpdate = Number(page.settings.checkUpdateKeePassXC);
if (checkUpdate !== CHECK_UPDATE_NEVER) {
const lastChecked = keepass.latestKeePassXC.lastChecked
? new Date(keepass.latestKeePassXC.lastChecked)
: new Date(1986, 11, 21);
const lastChecked = (keepass.latestKeePassXC.lastChecked) ? new Date(keepass.latestKeePassXC.lastChecked) : new Date(1986, 11, 21);
const daysSinceLastCheck = Math.floor(((new Date()).getTime() - lastChecked.getTime()) / 86400000);
if (daysSinceLastCheck >= checkUpdate) {
await keepass.checkForNewKeePassXCVersion();
@ -900,7 +879,7 @@ keepass.checkForNewKeePassXCVersion = async function() {
let version = -1;
try {
const response = await fetch(keepass.latestVersionUrl, { signal: AbortSignal.timeout(DEFAULT_FETCH_TIMEOUT) });
const response = await fetch(keepass.latestVersionUrl);
const jsonData = await response.json();
if (jsonData?.tag_name && jsonData?.prerelease === false) {
version = jsonData.tag_name;
@ -912,44 +891,6 @@ keepass.checkForNewKeePassXCVersion = async function() {
keepass.latestKeePassXC.lastChecked = new Date().valueOf();
};
// Implements retrieval of Related Origin Requests for passkeys
// https://www.w3.org/TR/webauthn-3/#sctn-related-origins
keepass.getPasskeysRelatedOrigins = async function(rpId) {
if (!rpId) {
return [];
}
try {
const response = await fetch(`https://${rpId}/.well-known/webauthn`, {
signal: AbortSignal.timeout(DEFAULT_FETCH_TIMEOUT),
});
// Basic reply validation, see: https://www.w3.org/TR/webauthn-3/#sctn-validating-relation-origin
const isJson = response?.headers?.get('content-type')?.includes('application/json');
if (!isJson) {
logError('getRelatedOrigins error: Content-Type is not JSON');
return [];
}
const jsonData = await response.json();
if (!Array.isArray(jsonData?.origins)
|| jsonData?.origins?.length === 0
|| jsonData?.origins?.length > MAX_RELATED_ORIGIN_LABELS
|| !jsonData?.origins?.every((origin) => typeof origin === 'string')) {
logError(
`getRelatedOrigins error: origins is not a list of strings, or it exceeds the maximum count of ${MAX_RELATED_ORIGIN_LABELS}`,
);
return [];
}
return jsonData.origins;
} catch (ex) {
logError(`getRelatedOrigins error: ${ex}`);
}
return [];
};
keepass.clearErrorMessage = function(tab) {
if (tab && page.tabs[tab.id]) {
page.tabs[tab.id].errorMessage = undefined;
@ -1004,25 +945,6 @@ keepass.updateDatabaseHashToContent = async function() {
}
};
keepass.updateFeaturesList = function (currentVersion) {
const versionResults = keepass.compareMultipleVersions([
keepass.requiredKeePassXC,
'2.6.1',
'2.7.0',
'2.7.7',
'2.7.10'
], currentVersion);
keepass.featuresList = {
downloadFaviconAfterSave: versionResults['2.7.0'],
newTotp: versionResults['2.6.1'],
passwordGenerator: versionResults['2.7.0'],
passkeys: versionResults['2.7.7'],
passkeysDefaultGroup: versionResults['2.7.10'],
requiredKeePassXCVersionFound: versionResults[keepass.requiredKeePassXC],
};
};
// Expects an array of versions to compare
keepass.compareMultipleVersions = function(versions, current, canBeEqual = true) {
if (!Array.isArray(versions)) {

View file

@ -24,7 +24,7 @@ const defaultSettings = {
downloadFaviconAfterSave: false,
passkeys: false,
passkeysFallback: true,
redirectAllowance: 3,
redirectAllowance: 1,
saveDomainOnly: true,
showGettingStartedGuideAlert: true,
showGroupNameInAutocomplete: true,
@ -49,10 +49,7 @@ page.blockedTabs = [];
page.clearCredentialsTimeout = null;
page.currentRequest = {};
page.currentTabId = -1;
page.isFirefox = false;
page.isSafari = false;
page.manualFill = ManualFill.NONE;
page.menuContexts = [ 'editable' ];
page.passwordFilled = false;
page.redirectCount = 0;
page.submitted = false;
@ -64,17 +61,12 @@ page.popupData = {
popup: 'popup'
};
page.initBrowser = async function() {
page.isFirefox = isFirefox();
page.isSafari = isSafari();
};
page.initSettings = async function() {
try {
const item = await browser.storage.local.get({ 'settings': {} });
// Load managed settings if found
if (page.isFirefox && typeof(browser.storage.managed) === 'object') {
if (isFirefox() && typeof(browser.storage.managed) === 'object') {
try {
const managedSettings = await browser.storage.managed.get('settings');
if (managedSettings?.settings) {
@ -82,16 +74,16 @@ page.initSettings = async function() {
item.settings = managedSettings.settings;
}
} catch (err) {
debugLogMessage('page.initSettings: ' + err);
logError('page.initSettings: ' + err);
}
} else if (!page.isSafari && typeof chrome.storage.managed === 'object') {
} else if (typeof(chrome.storage.managed) === 'object') {
chrome.storage.managed.get('settings').then((managedSettings) => {
if (managedSettings?.settings) {
debugLogMessage('Managed settings found.');
item.settings = managedSettings.settings;
}
}).catch((err) => {
debugLogMessage('page.initSettings: ' + err);
logError('page.initSettings: ' + err);
});
}
@ -146,16 +138,6 @@ page.initSitePreferences = async function() {
await browser.storage.local.set({ 'settings': page.settings });
};
page.resetAllSettings = async function() {
for (const [ key, value ] of Object.entries(defaultSettings)) {
page.settings[key] = value;
}
page.settings[DEFINED_CUSTOM_FIELDS] = {};
page.settings.sitePreferences = [];
await browser.storage.local.set({ 'settings': page.settings });
};
page.switchTab = async function(tab) {
// Clears Fill Attribute selection from context menu
page.setFillAttributeContextMenuItemVisible(false);
@ -275,11 +257,11 @@ page.retrieveCredentials = async function(tab, args = []) {
return credentials;
};
page.getLoginId = async function(tab, returnSingle = true) {
page.getLoginId = async function(tab) {
const currentTab = page.tabs[tab.id];
// If there's only one credential available and loginId is not set
if (currentTab && returnSingle && !currentTab.loginId && currentTab.credentials.length === 1) {
if (currentTab && !currentTab.loginId && currentTab.credentials.length === 1) {
return currentTab.credentials[0].uuid;
}
@ -353,15 +335,14 @@ page.fillHttpAuth = async function(tab, credentials) {
}
};
page.isSiteIgnored = async function(tab, args = []) {
const [ currentLocation, checkPasskeys ] = args;
page.isSiteIgnored = async function(tab, currentLocation) {
if (!page?.settings?.sitePreferences || !currentLocation) {
return false;
}
for (const site of page.settings.sitePreferences) {
if (siteMatch(site.url, currentLocation) || site.url === currentLocation) {
if (site.ignore === IGNORE_FULL || (checkPasskeys && site.ignore === IGNORE_PASSKEYS)) {
if (site.ignore === IGNORE_FULL) {
return true;
}
}
@ -484,7 +465,7 @@ page.getTopLevelDomainFromUrl = async function(domain, url) {
url: url
});
}
} catch (_e) {
} catch (e) {
return domain;
}
}
@ -517,7 +498,7 @@ page.getBaseDomainFromUrl = async function(hostname, url) {
const createContextMenuItem = function({ action, args, ...options }) {
return browser.contextMenus.create({
contexts: page.menuContexts,
contexts: menuContexts,
id: action,
...options
});

File diff suppressed because one or more lines are too long

View file

@ -1,13 +1,11 @@
'use strict';
const EXTENSION_NAME = 'KeePassXC-Browser';
const DEFINED_CUSTOM_FIELDS = 'defined-custom-fields';
// Site Preferences ignore options
const IGNORE_NOTHING = 'ignoreNothing';
const IGNORE_NORMAL = 'ignoreNormal';
const IGNORE_AUTOSUBMIT = 'ignoreAutoSubmit';
const IGNORE_PASSKEYS = 'ignorePasskeys';
const IGNORE_FULL = 'ignoreFull';
// Credential sorting options
@ -28,6 +26,23 @@ const URL_WILDCARD = '1kpxcwc1';
const schemeSegment = '(\\*|http|https|ws|wss|ftp)';
const hostSegment = '(\\*|(?:\\*\\.)?(?:[^/*]+))?';
const isFirefox = function() {
return navigator.userAgent.indexOf('Firefox') !== -1 || navigator.userAgent.indexOf('Gecko/') !== -1;
};
const isEdge = function() {
return navigator.userAgent.indexOf('Edg') !== -1;
};
const showNotification = function(message) {
browser.notifications.create({
'type': 'basic',
'iconUrl': browser.runtime.getURL('icons/keepassxc_64x64.png'),
'title': 'KeePassXC-Browser',
'message': message
});
};
const AssociatedAction = {
NOT_ASSOCIATED: 0,
ASSOCIATED: 1,
@ -46,47 +61,6 @@ const ManualFill = {
BOTH: 2
};
const SitePreferences = {
ALLOW_IFRAMES: 'allowIframes',
IMPROVED_FIELD_DETECTION: 'improvedFieldDetection',
USERNAME_ONLY: 'usernameOnly',
};
const isFirefox = function() {
return browser.runtime.getURL('')?.startsWith('moz-extension');
};
const isSafari = function() {
return browser.runtime.getURL('')?.startsWith('safari-web-extension');
};
const isEdge = function() {
return navigator.userAgent.indexOf('Edg') !== -1;
};
const getIconClass = function(className) {
if (isFirefox()) {
return className + '-moz';
} else if (isSafari()) {
return className + '-safari';
}
return className;
};
const showNotification = function(message) {
browser.notifications.create({
'type': 'basic',
'iconUrl': browser.runtime.getURL('icons/keepassxc_64x64.png'),
'title': 'KeePassXC-Browser',
'message': message
});
};
// Returns a string with 'px' for CSS styles
const Pixels = function(value) {
return String(value) + 'px';
};
const compareVersion = function(minimum, current, canBeEqual = true) {
if (!minimum || !current || minimum?.indexOf('.') === -1 || current?.indexOf('.') === -1) {
return false;
@ -183,10 +157,10 @@ const trimURL = function(url) {
};
const debugLogMessage = function(message, extra) {
console.debug(`[Debug ${getFileAndLine()}] ${EXTENSION_NAME} - ${message}`);
console.log(`[Debug ${getFileAndLine()}] ${EXTENSION_NAME} - ${message}`);
if (extra) {
console.debug(extra);
console.log(extra);
}
};
@ -208,13 +182,11 @@ const getCurrentTab = async function() {
return tabs?.length > 0 ? tabs[0] : undefined;
};
// Check if element b is inside a
const isElementInside = (a, b) => (b.x >= a.x && b.right <= a.right) && (b.y >= a.y && b.bottom <= a.bottom);
// Check if two elements overlap
const elementsOverlap = function(rect1, rect2) {
const isInside = (a, b) => (b.x >= a.x || b.right <= a.right) && (b.y >= a.y || b.bottom <= a.bottom);
const overlaps = (a, b) => !(a.right < b.left || a.left > b.right || a.bottom < b.top || a.top > b.bottom);
return isElementInside(rect1, rect2) || overlaps(rect1, rect2);
return isInside(rect1, rect2) || overlaps(rect1, rect2);
};
// Exports for tests

View file

@ -71,7 +71,7 @@ kpxcSites.detectUsernameFromPage = function() {
* @returns {boolean} True if an Element has a match with the identifier and document location
*/
kpxcSites.exceptionFound = function(identifier, field) {
if ((!identifier || identifier.length === 0) && !field) {
if (!identifier || identifier.length === 0) {
return;
}
@ -80,7 +80,7 @@ kpxcSites.exceptionFound = function(identifier, field) {
|| (typeof identifier === 'object' && [ 'password', 'form-row', 'show-password' ].every(c => identifier.contains(c))))) {
return true;
} else if (document.location.origin.startsWith('https://signin.ebay.')
&& (identifier === 'null' || identifier?.value === 'null' || identifier === 'pass')) {
&& (identifier === 'null' || identifier.value === 'null' || identifier === 'pass')) {
return true;
} else if (document.location.origin.startsWith('https://www.fidelity.com')) {
if (typeof identifier === 'string') {
@ -100,42 +100,6 @@ kpxcSites.exceptionFound = function(identifier, field) {
} else if (document.location.origin === 'https://id.atlassian.com' &&
Array.isArray(identifier) && identifier?.contains('password-field')) {
return true;
} else if (document.location.origin === 'https://app.fastmail.com'
&& identifier?.contains('u-space-y-5') && field?.id === 'v25') {
return true;
} else if (document.location.origin === 'https://login.dei.gr' &&
identifier?.value?.includes('show-reveal-password')) {
return true;
} else if (document.location.origin === 'https://accounts.google.com' && field?.id === 'password') {
return true;
} else if (document.location.origin === 'https://www.epicgames.com'
&& ((field?.style?.opacity === '1' && field?.style?.willChange === 'auto') || identifier === 'password')) {
return true;
} else if (document.location.origin === 'https://www.paypal.com' && field?.id === 'splitPassword') {
return true;
}
return false;
};
// Handles exceptions when returning or modifying existing combinations
kpxcSites.combinationExceptionFound = function(existingCombination) {
if (!existingCombination) {
return false;
}
// Exception for Google. They replace the username input with password input using identical className.
// If detected, remove the username from the combination.
if (document.location.origin === 'https://accounts.google.com'
&& existingCombination?.username?.className?.length > 0
&& existingCombination?.password?.className?.length > 0
&& existingCombination?.username?.className === existingCombination?.password?.className) {
return true;
}
if (document.location.origin === 'https://www.paypal.com'
&& existingCombination.password?.className?.includes('pin-password')) {
return true;
}
return false;
@ -250,8 +214,6 @@ kpxcSites.formSubmitButtonExceptionFound = function(form) {
return form.querySelector('button[class*=_primary_]');
} else if (!form && document.location.origin === 'https://www.reddit.com') {
return $('button.login');
} else if (form?.action === 'https://webapp.dubverse.ai/') {
return $('button[class^=chakra]');
}
return undefined;
@ -270,20 +232,6 @@ kpxcSites.popupExceptionFound = function(combinations) {
return false;
};
/**
* Handles exceptions where a certain element is set as a popover, and it prevents input field detections.
* @param {object} elem Popover element
* @returns {boolean} True if exception found
*/
kpxcSites.overlayExceptionFound = function(elem) {
if (document.location.href?.startsWith('https://github.com/login')
&& elem?.nodeName === 'TOOL-TIP' && elem?.baseURI === 'https://github.com/login') {
return true;
}
return false;
};
/**
* Handles a few exceptions for certain sites where Username Icon is not placed properly.
* @param {number} left Absolute left position of the icon

View file

@ -100,9 +100,7 @@ class Autocomplete {
styleSheet.addEventListener('load', () => (this.wrapper.style.display = 'block'));
this.container = kpxcUI.createElement('div', 'kpxcAutocomplete-container', {
id: 'kpxcAutocomplete-container',
popover: 'manual',
});
this.container.style.margin = 0;
// Apply compact mode class
if (kpxc.settings.useCompactMode) {
@ -130,9 +128,6 @@ class Autocomplete {
this.updateList();
this.container.classList.add('kpxcAutocomplete-container--visible');
if (kpxcFields.popoverSupported) {
this.container.showPopover({ source: inputField });
}
this.updatePosition();
}
@ -240,9 +235,6 @@ class Autocomplete {
}
this.container.classList.remove('kpxcAutocomplete-container--visible');
if (kpxcFields.popoverSupported) {
this.container.hidePopover();
}
}
getAllItems() {
@ -346,7 +338,7 @@ class Autocomplete {
// Get body zoom radio
const zoom = kpxcUI.bodyStyle.zoom || 1;
// Calculate Y offset if menu does not fit to the bottom of the screen -> show it at the top of the input field
const menuRect = this.container.getBoundingClientRect();
const totalHeight = menuRect.height + rect.height;

View file

@ -28,7 +28,7 @@ kpxcBanner.destroy = async function() {
} else {
window.parent.document.body.removeChild(window.parent.document.body.querySelector('#kpxc-banner'));
}
} catch(_e) {
} catch(e) {
kpxcBanner.wrapper.style.display = 'hidden';
}
@ -62,10 +62,10 @@ kpxcBanner.create = async function(credentials = {}) {
const bannerInfo = kpxcUI.createElement('div', 'banner-info');
const bannerButtons = kpxcUI.createElement('div', 'banner-buttons');
const className = getIconClass('kpxc-banner-icon');
const className = (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon');
const icon = kpxcUI.createElement('span', className, { 'alt': 'logo' });
const infoText = kpxcUI.createElement('span', 'banner-info-text', {}, tr('rememberInfoText'));
const infoText = kpxcUI.createElement('span', '', {}, tr('rememberInfoText'));
const usernameText = kpxcUI.createElement('span', 'small', {}, tr('popupUsername') + ' ');
const usernameSpan = kpxcUI.createElement('span', 'small info information-username', {}, credentials.username);

View file

@ -5,10 +5,10 @@ const STEP_SELECT_USERNAME = 1;
const STEP_SELECT_PASSWORD = 2;
const STEP_SELECT_TOTP = 3;
const STEP_SELECT_STRING_FIELDS = 4;
const STEP_SELECT_SUBMIT_BUTTON = 5;
const CHECKBOX_OVERLAY_SIZE = 20;
const DEFINED_CUSTOM_FIELDS = 'defined-custom-fields';
const FIXED_FIELD_CLASS = 'kpxcDefine-fixed-field';
const DARK_FIXED_FIELD_CLASS = 'kpxcDefine-fixed-field-dark';
const HOVER_FIELD_CLASS = 'kpxcDefine-fixed-hover-field';
@ -19,10 +19,9 @@ const PASSWORD_FIELD_CLASS = 'kpxcDefine-fixed-password-field';
const TOTP_FIELD_CLASS = 'kpxcDefine-fixed-totp-field';
const STRING_FIELD_CLASS = 'kpxcDefine-fixed-string-field';
const INPUT_BUTTON_QUERY_PATTERN = 'button, input[type=submit]';
const INPUT_QUERY_PATTERNS_START = 'input';
const INPUT_QUERY_PATTERN_NOT_CHECKBOX = ':not([type=checkbox])';
const INPUT_QUERY_PATTERN = ':not([disabled]):not([type=button]):not([type=radio]):not([type=color]):not([type=date]):not([type=datetime-local]):not([type=file]):not([type=hidden]):not([type=image]):not([type=month]):not([type=range]):not([type=reset]):not([type=submit]):not([type=time]):not([type=week]), select, textarea';
const inputQueryPatternStart = 'input';
const inputQueryPatternNotCheckbox = ':not([type=checkbox])';
const inputQueryPattern = ':not([disabled]):not([type=button]):not([type=radio]):not([type=color]):not([type=date]):not([type=datetime-local]):not([type=file]):not([type=hidden]):not([type=image]):not([type=month]):not([type=range]):not([type=reset]):not([type=submit]):not([type=time]):not([type=week]), select, textarea';
const kpxcCustomLoginFieldsBanner = {};
kpxcCustomLoginFieldsBanner.banner = undefined;
@ -31,9 +30,8 @@ kpxcCustomLoginFieldsBanner.created = false;
kpxcCustomLoginFieldsBanner.dataStep = STEP_NONE;
kpxcCustomLoginFieldsBanner.infoText = undefined;
kpxcCustomLoginFieldsBanner.wrapper = undefined;
kpxcCustomLoginFieldsBanner.inputQueryPatternNormal =
INPUT_QUERY_PATTERNS_START + INPUT_QUERY_PATTERN_NOT_CHECKBOX + INPUT_QUERY_PATTERN;
kpxcCustomLoginFieldsBanner.inputQueryPatternStringFields = INPUT_QUERY_PATTERNS_START + INPUT_QUERY_PATTERN;
kpxcCustomLoginFieldsBanner.inputQueryPatternNormal = inputQueryPatternStart + inputQueryPatternNotCheckbox + inputQueryPattern;
kpxcCustomLoginFieldsBanner.inputQueryPatternStringFields = inputQueryPatternStart + inputQueryPattern;
kpxcCustomLoginFieldsBanner.markedFields = [];
kpxcCustomLoginFieldsBanner.nonSelectedElementsPattern = `div.${FIXED_FIELD_CLASS}:not(.${USERNAME_FIELD_CLASS}):not(.${PASSWORD_FIELD_CLASS}):not(.${TOTP_FIELD_CLASS}):not(.${STRING_FIELD_CLASS})`;
@ -46,7 +44,6 @@ kpxcCustomLoginFieldsBanner.selection = {
totpElement: undefined,
fields: [],
fieldElements: [],
submitButton: undefined,
};
kpxcCustomLoginFieldsBanner.buttons = {
@ -93,9 +90,9 @@ kpxcCustomLoginFieldsBanner.create = async function() {
const bannerInfo = kpxcUI.createElement('div', 'banner-info');
const bannerButtons = kpxcUI.createElement('div', 'banner-buttons');
const iconClassName = getIconClass('kpxc-banner-icon');
const iconClassName = isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon';
const icon = kpxcUI.createElement('span', iconClassName);
const infoText = kpxcUI.createElement('span', 'banner-info-text', {}, tr('defineChooseCustomLoginFieldText'));
const infoText = kpxcUI.createElement('span', '', {}, tr('defineChooseCustomLoginFieldText'));
const separator = kpxcUI.createElement('div', 'kpxc-separator');
const secondSeparator = kpxcUI.createElement('div', 'kpxc-separator');
@ -104,7 +101,6 @@ kpxcCustomLoginFieldsBanner.create = async function() {
const passwordButton = kpxcUI.createButton(RED_BUTTON, tr('password'), kpxcCustomLoginFieldsBanner.passwordButtonClicked);
const totpButton = kpxcUI.createButton(GREEN_BUTTON, 'TOTP', kpxcCustomLoginFieldsBanner.totpButtonClicked);
const stringFieldsButton = kpxcUI.createButton(BLUE_BUTTON, tr('stringFields'), kpxcCustomLoginFieldsBanner.stringFieldsButtonClicked);
const submitButton = kpxcUI.createButton(BLUE_BUTTON, tr('submitButton'), kpxcCustomLoginFieldsBanner.submitButtonClicked);
const clearDataButton = kpxcUI.createButton(RED_BUTTON, tr('defineClearData'), kpxcCustomLoginFieldsBanner.clearData);
const confirmButton = kpxcUI.createButton(GREEN_BUTTON, tr('defineConfirm'), kpxcCustomLoginFieldsBanner.confirm);
const closeButton = kpxcUI.createButton(RED_BUTTON, tr('defineClose'), kpxcCustomLoginFieldsBanner.closeButtonClicked);
@ -121,11 +117,10 @@ kpxcCustomLoginFieldsBanner.create = async function() {
kpxcCustomLoginFieldsBanner.buttons.password = passwordButton;
kpxcCustomLoginFieldsBanner.buttons.totp = totpButton;
kpxcCustomLoginFieldsBanner.buttons.stringFields = stringFieldsButton;
kpxcCustomLoginFieldsBanner.buttons.submitButton = submitButton;
bannerInfo.appendMultiple(icon, infoText);
bannerButtons.appendMultiple(resetButton, separator, usernameButton, passwordButton, totpButton,
stringFieldsButton, submitButton, secondSeparator, clearDataButton, confirmButton, closeButton);
bannerButtons.appendMultiple(resetButton, separator, usernameButton,
passwordButton, totpButton, stringFieldsButton, secondSeparator, clearDataButton, confirmButton, closeButton);
banner.appendMultiple(bannerInfo, bannerButtons);
kpxcUI.makeBannerDraggable(banner);
@ -195,8 +190,7 @@ kpxcCustomLoginFieldsBanner.usernameButtonClicked = function(e) {
// Reset username field selection if already set
if (kpxcCustomLoginFieldsBanner.selection.username) {
kpxcCustomLoginFieldsBanner.removeSelection(kpxcCustomLoginFieldsBanner.selection.username,
`div.${USERNAME_FIELD_CLASS}`);
kpxcCustomLoginFieldsBanner.removeSelection(kpxcCustomLoginFieldsBanner.selection.username, `div.${USERNAME_FIELD_CLASS}`);
kpxcCustomLoginFieldsBanner.selection.username = undefined;
}
@ -214,8 +208,7 @@ kpxcCustomLoginFieldsBanner.passwordButtonClicked = function(e) {
// Reset password field selection if already set
if (kpxcCustomLoginFieldsBanner.selection.password) {
kpxcCustomLoginFieldsBanner.removeSelection(kpxcCustomLoginFieldsBanner.selection.password,
`div.${PASSWORD_FIELD_CLASS}`);
kpxcCustomLoginFieldsBanner.removeSelection(kpxcCustomLoginFieldsBanner.selection.password, `div.${PASSWORD_FIELD_CLASS}`);
kpxcCustomLoginFieldsBanner.selection.password = undefined;
}
@ -233,8 +226,7 @@ kpxcCustomLoginFieldsBanner.totpButtonClicked = function(e) {
// Reset TOTP field selection if already set
if (kpxcCustomLoginFieldsBanner.selection.totp) {
kpxcCustomLoginFieldsBanner.removeSelection(kpxcCustomLoginFieldsBanner.selection.totp,
`div.${TOTP_FIELD_CLASS}`);
kpxcCustomLoginFieldsBanner.removeSelection(kpxcCustomLoginFieldsBanner.selection.totp, `div.${TOTP_FIELD_CLASS}`);
kpxcCustomLoginFieldsBanner.selection.totp = undefined;
}
@ -265,25 +257,6 @@ kpxcCustomLoginFieldsBanner.stringFieldsButtonClicked = function(e) {
sendMessageToFrames(e, 'string_field_button_clicked');
};
kpxcCustomLoginFieldsBanner.submitButtonClicked = function(e) {
if (!e.isTrusted || kpxcCustomLoginFieldsBanner.dataStep === STEP_SELECT_SUBMIT_BUTTON) {
kpxcCustomLoginFieldsBanner.backToStart();
return;
}
// Reset TOTP field selection if already set
if (kpxcCustomLoginFieldsBanner.selection.submitButton) {
kpxcCustomLoginFieldsBanner.removeSelection(kpxcCustomLoginFieldsBanner.selection.submitButton,
`div.${STRING_FIELD_CLASS}`);
kpxcCustomLoginFieldsBanner.selection.submitButton = undefined;
}
kpxcCustomLoginFieldsBanner.prepareSubmitButtonSelection();
kpxcCustomLoginFieldsBanner.buttons.confirm.disabled = true;
sendMessageToFrames(e, 'submit_button_clicked');
};
kpxcCustomLoginFieldsBanner.closeButtonClicked = function(e) {
if (!e.isTrusted) {
return;
@ -310,8 +283,6 @@ kpxcCustomLoginFieldsBanner.updateFieldSelections = function() {
kpxcCustomLoginFieldsBanner.prepareTOTPSelection();
} else if (kpxcCustomLoginFieldsBanner.dataStep === STEP_SELECT_STRING_FIELDS) {
kpxcCustomLoginFieldsBanner.prepareStringFieldSelection();
} else if (kpxcCustomLoginFieldsBanner.dataStep === STEP_SELECT_SUBMIT_BUTTON) {
kpxcCustomLoginFieldsBanner.prepareSubmitButtonSelection();
}
};
@ -344,8 +315,6 @@ kpxcCustomLoginFieldsBanner.confirm = async function(e) {
kpxc.settings[DEFINED_CUSTOM_FIELDS][location].password = undefined;
} else if (currentSite.totp?.[0] === path[0]) {
kpxc.settings[DEFINED_CUSTOM_FIELDS][location].totp = undefined;
} else if (currentSite.submitButton?.[0] === path[0]) {
kpxc.settings[DEFINED_CUSTOM_FIELDS][location].submitButton = undefined;
}
};
@ -353,11 +322,10 @@ kpxcCustomLoginFieldsBanner.confirm = async function(e) {
const passwordPath = kpxcCustomLoginFieldsBanner.selection.password;
const totpPath = kpxcCustomLoginFieldsBanner.selection.totp;
const stringFieldsPaths = kpxcCustomLoginFieldsBanner.selection.fields;
const submitButtonPath = kpxcCustomLoginFieldsBanner.selection.submitButton;
const location = kpxc.getDocumentLocation();
const currentSettings = kpxc.settings[DEFINED_CUSTOM_FIELDS][location];
if (usernamePath || passwordPath || totpPath || stringFieldsPaths.length > 0 || submitButtonPath) {
if (usernamePath || passwordPath || totpPath || stringFieldsPaths.length > 0) {
if (currentSettings) {
// Update the single selection to current settings
if (usernamePath) {
@ -378,19 +346,13 @@ kpxcCustomLoginFieldsBanner.confirm = async function(e) {
if (stringFieldsPaths.length > 0) {
kpxc.settings[DEFINED_CUSTOM_FIELDS][location].fields = stringFieldsPaths;
}
if (submitButtonPath) {
clearIdenticalField(submitButtonPath, location);
kpxc.settings[DEFINED_CUSTOM_FIELDS][location].submitButton = submitButtonPath;
}
} else {
// Override all fields (default, because there's no currentSettings available)
kpxc.settings[DEFINED_CUSTOM_FIELDS][location] = {
username: usernamePath,
password: passwordPath,
totp: totpPath,
fields: stringFieldsPaths,
submitButton: submitButtonPath,
fields: stringFieldsPaths
};
}
@ -420,8 +382,7 @@ kpxcCustomLoginFieldsBanner.resetSelection = function() {
username: undefined,
password: undefined,
totp: undefined,
fields: [],
submitButton: undefined,
fields: []
};
kpxcCustomLoginFieldsBanner.removeMarkedFields();
@ -463,24 +424,15 @@ kpxcCustomLoginFieldsBanner.prepareStringFieldSelection = function() {
kpxcCustomLoginFieldsBanner.selectStringFields();
};
kpxcCustomLoginFieldsBanner.prepareSubmitButtonSelection = function() {
kpxcCustomLoginFieldsBanner.infoText.textContent = tr('defineChooseSubmitButton');
kpxcCustomLoginFieldsBanner.dataStep = STEP_SELECT_SUBMIT_BUTTON;
kpxcCustomLoginFieldsBanner.buttons.submitButton.classList.remove(GRAY_BUTTON_CLASS);
kpxcCustomLoginFieldsBanner.selectField('submitButton');
};
kpxcCustomLoginFieldsBanner.isFieldSelected = function(field) {
const currentFieldId = kpxcFields.setId(field);
const selection = kpxcCustomLoginFieldsBanner.selection;
if (kpxcCustomLoginFieldsBanner.markedFields.some(f => f === field)) {
return (
(selection.username && selection.usernameElement === field)
|| (selection.password && selection.passwordElement === field)
|| (selection.totp && selection.totpElement === field)
|| (selection.submitButton && selection.submitButton === field)
|| selection.fields.some(f => f[0] === currentFieldId[0])
(kpxcCustomLoginFieldsBanner.selection.username && kpxcCustomLoginFieldsBanner.selection.usernameElement === field)
|| (kpxcCustomLoginFieldsBanner.selection.password && kpxcCustomLoginFieldsBanner.selection.passwordElement === field)
|| (kpxcCustomLoginFieldsBanner.selection.totp && kpxcCustomLoginFieldsBanner.selection.totpElement === field)
|| kpxcCustomLoginFieldsBanner.selection.fields.some(f => f[0] === currentFieldId[0])
);
}
@ -509,7 +461,7 @@ kpxcCustomLoginFieldsBanner.setSelectedField = function(elem) {
kpxcCustomLoginFieldsBanner.buttons.close.textContent = tr('optionsButtonCancel');
};
// Expects 'username', 'password', 'totp' or 'submitButton'
// Expects 'username', 'password' or 'totp'
kpxcCustomLoginFieldsBanner.selectField = function(fieldType) {
kpxcCustomLoginFieldsBanner.eventFieldClick = function(e) {
const field = kpxcCustomLoginFieldsBanner.getSelectedField(e);
@ -565,10 +517,9 @@ kpxcCustomLoginFieldsBanner.selectStringFields = function() {
kpxcCustomLoginFieldsBanner.markFields = function() {
let firstInput;
const inputs = document.querySelectorAll(
kpxcCustomLoginFieldsBanner.dataStep === STEP_SELECT_SUBMIT_BUTTON ? INPUT_BUTTON_QUERY_PATTERN :
(STEP_SELECT_STRING_FIELDS
? kpxcCustomLoginFieldsBanner.inputQueryPatternStringFields
: kpxcCustomLoginFieldsBanner.inputQueryPatternNormal));
kpxcCustomLoginFieldsBanner.dataStep === STEP_SELECT_STRING_FIELDS
? kpxcCustomLoginFieldsBanner.inputQueryPatternStringFields
: kpxcCustomLoginFieldsBanner.inputQueryPatternNormal);
const zoom = kpxcUI.bodyStyle.zoom || 1;
for (const i of inputs) {
@ -594,7 +545,7 @@ kpxcCustomLoginFieldsBanner.markFields = function() {
if (kpxcCustomLoginFieldsBanner.dataStep !== STEP_SELECT_STRING_FIELDS) {
field.textContent = dataStepToString();
}
// Static size for the checkbox overlay
if (i?.getLowerCaseAttribute('type') === 'checkbox') {
field.style.width = Pixels(CHECKBOX_OVERLAY_SIZE / zoom);
@ -724,10 +675,7 @@ kpxcCustomLoginFieldsBanner.handleTopWindowMessage = function(args) {
kpxcCustomLoginFieldsBanner.selection.totp = selection;
kpxcCustomLoginFieldsBanner.setSelectedField();
} else if (message === 'string_field_selected') {
kpxcCustomLoginFieldsBanner.selection.fields = selection;
kpxcCustomLoginFieldsBanner.setSelectedField();
} else if (message === 'submitButton_selected') {
kpxcCustomLoginFieldsBanner.selection.submitButton = selection;
kpxcCustomLoginFieldsBanner.selection.stringFields = selection;
kpxcCustomLoginFieldsBanner.setSelectedField();
} else if (message === 'enable_clear_data_button') {
kpxcCustomLoginFieldsBanner.buttons.clearData.style.display = 'inline-block';
@ -752,8 +700,6 @@ kpxcCustomLoginFieldsBanner.handleParentWindowMessage = function(args) {
kpxcCustomLoginFieldsBanner.totpButtonClicked(e);
} else if (message === 'string_field_button_clicked') {
kpxcCustomLoginFieldsBanner.stringFieldsButtonClicked(e);
} else if (message === 'submit_button_clicked') {
kpxcCustomLoginFieldsBanner.submitButtonClicked(e);
} else if (message === 'reset_button_clicked') {
kpxcCustomLoginFieldsBanner.reset();
} else if (message === 'close_button_clicked') {
@ -821,7 +767,5 @@ const dataStepToString = function() {
return tr('totp');
} else if (kpxcCustomLoginFieldsBanner.dataStep === STEP_SELECT_STRING_FIELDS) {
return tr('defineStringField');
} else if (kpxcCustomLoginFieldsBanner.dataStep === STEP_SELECT_SUBMIT_BUTTON) {
return tr('defineSubmitButton');
}
};

View file

@ -9,7 +9,6 @@ const MAX_SEGMENTED_FIELD_LENGTH = 100;
* Provides methods for input field handling.
*/
const kpxcFields = {};
kpxcFields.popoverSupported = true;
// Returns all username & password combinations detected from the inputs.
// After username field is detected, first password field found after that will be saved as a combination.
@ -30,7 +29,7 @@ kpxcFields.getAllCombinations = async function(inputs) {
form: input.form
};
combinations.push(kpxcFields.getExistingCombination(combination));
combinations.push(combination);
usernameField = null;
} else if (kpxcTOTPIcons.isValid(input)) {
// Dynamically added TOTP field
@ -94,32 +93,6 @@ kpxcFields.getCombinationFromAllInputs = function() {
return kpxc.combinations[0];
};
// Checks if existing combination is found and recognized fields are added to it
kpxcFields.getExistingCombination = function(combination) {
// Lookup existing combinations that use the same form
const existingCombination = kpxc.combinations?.find(c => c.form === combination?.form);
if (existingCombination) {
// Replace values to the existing combination
existingCombination.username ??= combination.username;
existingCombination.password ??= combination.password;
if (existingCombination.passwordInputs?.length === 0) {
existingCombination.passwordInputs = combination.passwordInputs;
} else if (combination?.password) {
// If password field is found in the current combination, force assign it to the existing combination
existingCombination.password = combination.password;
}
// Remove username field from combination with certain sites (replaced by password input)
if (kpxcSites.combinationExceptionFound(existingCombination)) {
existingCombination.username = null;
}
return existingCombination;
}
return combination;
};
// Adds segmented TOTP fields to the combination if found
kpxcFields.getSegmentedTOTPFields = function(inputs, combinations) {
if (!kpxc.settings.showOTPIcon) {
@ -438,92 +411,28 @@ kpxcFields.isSearchField = function(target) {
return false;
};
// :popover-open selector is supported only with Firefox >= 125 and Chrome >= 114
kpxcFields.discoverOverlays = function() {
try {
kpxcFields.overlays = document.querySelectorAll(':popover-open, [popover]');
} catch (e) {
// Ignore SyntaxError (e.g., unsupported selector)
if (!(e instanceof SyntaxError)) {
kpxcFields.popoverSupported = false;
logError(e);
}
}
};
// Checks if element has an overlay
kpxcFields.hasOverlay = function(elem) {
try {
return elem?.hasAttribute('popover') || elem?.matches(':popover-open');
} catch (e) {
// Ignore SyntaxError (e.g., unsupported selector)
if (!(e instanceof SyntaxError)) {
kpxcFields.popoverSupported = false;
logError(e);
}
}
};
// Check the visibility of existing fields
kpxcFields.checkExistingFields = function() {
if (kpxc.inputs?.some(input => !kpxcFields.isVisible(input))) {
kpxc.clearAllFromPage();
kpxc.combinations = [];
}
};
// Check for popup overlays
kpxcFields.isOverlayOnTop = function(rect) {
for (const overlay of kpxcFields.overlays ?? []) {
if (kpxcSites.overlayExceptionFound(overlay)) {
continue;
}
const overlayRect = overlay?.getBoundingClientRect();
if (overlayRect && elementsOverlap(rect, overlayRect)) {
return true;
}
}
return false;
};
/**
* Check if element is the topmost element
* @param {HTMLElement} elem Element to be checked
* @param {DOMRect} rect Precalculated DOMRect of the element
* @returns {boolean} True if element is the topmost
*/
kpxcFields.isTopElement = function(elem, rect) {
if (!elem || !rect) {
return false;
}
const rootNode = elem.getRootNode() ?? document;
// Returns the topmost element from point x, height/2
// If the input has a label as the top element and it's inside the input, allow it.
const getTopmostElement = (element, x, elementRect) => {
const topElement = rootNode.elementFromPoint(x, elementRect.top + (elementRect.height / 2));
return element?.labels &&
element.labels[0] === topElement &&
elementsOverlap(elementRect, topElement.getBoundingClientRect())
? element
: topElement;
};
// Check topmost element from three points inside the input
const verticalMiddle = rect.top + (rect.height / 2);
if (matchesWithNodeName(elem, 'INPUT') && [
getTopmostElement(elem, rect.left + (rect.width / 4), rect), // First third
getTopmostElement(elem, rect.left + (rect.width / 2), rect), // Middle
getTopmostElement(elem, rect.left + (rect.width / 1.33), rect), // Last third
document.elementFromPoint(rect.left + (rect.width / 4), verticalMiddle), // First third
document.elementFromPoint(rect.left + (rect.width / 2), verticalMiddle), // Middle
document.elementFromPoint(rect.left + (rect.width / 1.33), verticalMiddle), // Last third
].some((e) => e !== elem)) {
return false;
}
// Check if element has an overlay
if (kpxcFields.isOverlayOnTop(rect)) {
return false;
// Check for popup overlays
const overlays = document.querySelectorAll(':popover-open');
for (const overlay of overlays) {
const overlayRect = overlay?.getBoundingClientRect();
if (overlayRect && elementsOverlap(rect, overlayRect)) {
return false;
}
}
return true;
@ -594,14 +503,14 @@ kpxcFields.traverseParents = function(element, predicate, resultFn = () => true,
kpxcFields.useCustomLoginFields = async function() {
const location = kpxc.getDocumentLocation();
const creds = kpxc.settings['defined-custom-fields'][location];
if (!creds.username && !creds.password && !creds.totp && creds.fields.length === 0 && !creds.submitButton) {
if (!creds.username && !creds.password && !creds.totp && creds.fields.length === 0) {
return;
}
// Finds the element based on the stored ID
const findElement = async function(fields, idArray) {
// Finds the input field based on the stored ID
const findInputField = async function(inputFields, idArray) {
if (idArray) {
const input = fields.find(e => e === kpxcFields.getId(idArray, e));
const input = inputFields.find(e => e === kpxcFields.getId(idArray, e));
if (input) {
return input;
}
@ -613,29 +522,21 @@ kpxcFields.useCustomLoginFields = async function() {
// Get all input fields from the page without any extra filters
const inputFields = [];
document.body.querySelectorAll('input, select, textarea').forEach(e => {
if (e.type !== 'hidden' && !e.disabled) {
if (e.type !== 'hidden' && !e.disabled && kpxcFields.isTopElement(e, e?.getBoundingClientRect())) {
inputFields.push(e);
}
});
const buttons = [];
document.body.querySelectorAll('button, input[type=submit]').forEach(e => {
if (e.type !== 'hidden' && !e.disabled) {
buttons.push(e);
}
});
const [ username, password, totp, submitButton ] = await Promise.all([
await findElement(inputFields, creds.username),
await findElement(inputFields, creds.password),
await findElement(inputFields, creds.totp),
await findElement(buttons, creds.submitButton),
const [ username, password, totp ] = await Promise.all([
await findInputField(inputFields, creds.username),
await findInputField(inputFields, creds.password),
await findInputField(inputFields, creds.totp)
]);
// Handle StringFields
const stringFields = [];
for (const sf of creds.fields) {
const field = await findElement(inputFields, sf);
const field = await findInputField(inputFields, sf);
if (field) {
stringFields.push(field);
}
@ -647,19 +548,13 @@ kpxcFields.useCustomLoginFields = async function() {
kpxcTOTPIcons.newIcon(totp, kpxc.databaseState);
}
// No values found
if (!username && !password && !totp && !submitButton && stringFields?.length === 0) {
return [];
}
const combinations = [];
combinations.push({
username: username,
password: password,
passwordInputs: [ password ],
totp: totp,
fields: stringFields,
submitButton: submitButton
fields: stringFields
});
return combinations;

View file

@ -18,7 +18,7 @@ kpxcFill.fillAttributeToActiveElementWith = async function(attr) {
return;
}
await kpxcFill.setValue(el, value[0]);
kpxc.setValue(el, value[0]);
};
// Fill requested from the context menu. Active element is used for combination detection
@ -152,20 +152,20 @@ kpxcFill.fillTOTPFromUuid = async function(el, uuid) {
return;
}
await kpxcFill.setTOTPValue(el, totp);
kpxcFill.setTOTPValue(el, totp);
} else if (user.stringFields?.length > 0) {
const stringFields = user.stringFields;
for (const s of stringFields) {
const val = s['KPH: {TOTP}'];
if (val) {
await kpxcFill.setTOTPValue(el, val);
kpxcFill.setTOTPValue(el, val);
}
}
}
};
// Set normal or segmented TOTP value
kpxcFill.setTOTPValue = async function(elem, val) {
kpxcFill.setTOTPValue = function(elem, val) {
if (kpxc.credentials.length === 0) {
logDebug('Error: Credential list is empty.');
return;
@ -173,22 +173,22 @@ kpxcFill.setTOTPValue = async function(elem, val) {
for (const comb of kpxc.combinations) {
if (comb.totpInputs?.length > 0) {
await kpxcFill.fillSegmentedTotp(elem, val, comb.totpInputs);
kpxcFill.fillSegmentedTotp(elem, val, comb.totpInputs);
return;
}
}
await kpxcFill.setValue(elem, val);
kpxc.setValue(elem, val);
};
// Fill TOTP in parts
kpxcFill.fillSegmentedTotp = async function(elem, val, totpInputs) {
kpxcFill.fillSegmentedTotp = function(elem, val, totpInputs) {
if (!totpInputs.includes(elem) || val.length < totpInputs.length) {
return;
}
for (let i = 0; i < totpInputs.length; ++i) {
await kpxcFill.setValue(totpInputs[i], val[i]);
kpxc.setValue(totpInputs[i], val[i]);
}
};
@ -245,17 +245,6 @@ kpxcFill.fillInCredentials = async function(combination, predefinedUsername, uui
skipAutoSubmit = selectedCredentials.skipAutoSubmit === 'true';
}
// Update the password field if form has been updated with a new identical element (Moodle).
// If found, replace the new input field to the combination.
if (combination?.form && combination?.password && !combination.form.contains(combination.password)) {
const formInputs = kpxcObserverHelper.getInputs(combination.form);
const newPasswordField = formInputs?.find((formInput) =>
formInput?.getLowerCaseAttribute('type') === 'password');
if (newPasswordField && areNamedNodeMapsEqual(combination.password.attributes, newPasswordField.attributes)) {
combination.password = newPasswordField;
}
}
// Fill password
if (combination.password && matchesWithNodeName(combination.password, 'INPUT')) {
// Show a notification if password length exceeds the length defined in input
@ -271,28 +260,28 @@ kpxcFill.fillInCredentials = async function(combination, predefinedUsername, uui
return;
}
await kpxcFill.setValueWithChange(combination.password, selectedCredentials.password);
kpxc.setValueWithChange(combination.password, selectedCredentials.password);
await kpxc.setPasswordFilled(true);
}
// Fill username
if (combination.username && usernameValue && combination.username !== combination.password &&
if (combination.username && usernameValue &&
(!combination.username.value || combination.username.value !== usernameValue)) {
if (!passOnly) {
await kpxcFill.setValueWithChange(combination.username, usernameValue);
kpxc.setValueWithChange(combination.username, usernameValue);
}
}
// Fill StringFields
if (selectedCredentials.stringFields?.length > 0) {
await kpxcFill.fillInStringFields(combination.fields, selectedCredentials.stringFields);
kpxcFill.fillInStringFields(combination.fields, selectedCredentials.stringFields);
}
// Fill TOTP
if (kpxc.settings.autoFillSingleTotp && kpxc.entryHasTotp(selectedCredentials)) {
const totpCombination = combination?.totp || kpxc.combinations?.find(c => c.totp);
if (totpCombination?.totp) {
await kpxcFill.fillTOTPFromUuid(totpCombination.totp, selectedCredentials.uuid);
kpxcFill.fillTOTPFromUuid(totpCombination.totp, selectedCredentials.uuid);
}
}
@ -306,7 +295,7 @@ kpxcFill.fillInCredentials = async function(combination, predefinedUsername, uui
};
// Fills StringFields defined in Custom Fields
kpxcFill.fillInStringFields = async function(fields, stringFields) {
kpxcFill.fillInStringFields = function(fields, stringFields) {
const filledInFields = [];
if (fields && stringFields && fields?.length > 0 && stringFields?.length > 0) {
for (let i = 0; i < fields.length; i++) {
@ -318,7 +307,7 @@ kpxcFill.fillInStringFields = async function(fields, stringFields) {
const currentField = fields[i];
if (currentField && stringFieldValue[0]) {
await kpxcFill.setValue(currentField, stringFieldValue[0], true);
kpxc.setValue(currentField, stringFieldValue[0], true);
filledInFields.push(currentField);
}
}
@ -340,8 +329,7 @@ kpxcFill.performAutoSubmit = async function(combination, skipAutoSubmit) {
if (!skipAutoSubmit && !autoSubmitIgnoredForSite) {
await sendMessage('page_set_autosubmit_performed');
// Use submit button from Custom Login Fields or detect it from the form
const submitButton = combination?.submitButton ?? kpxcForm.getFormSubmitButton(combination.form);
const submitButton = kpxcForm.getFormSubmitButton(combination.form);
if (submitButton !== undefined) {
submitButton.click();
} else if (combination.form) {
@ -352,79 +340,6 @@ kpxcFill.performAutoSubmit = async function(combination, skipAutoSubmit) {
}
};
// Special handling for setting value to select and checkbox elements
kpxcFill.setValue = async function(field, value, forced = false) {
if (field.matches('select')) {
value = value.toLowerCase().trim();
const options = field.querySelectorAll('option');
for (const o of options) {
if (o.textContent.toLowerCase().trim() === value) {
await kpxcFill.setValueWithChange(field, o.value);
return false;
}
}
return;
} else if (field.getLowerCaseAttribute('type') === 'checkbox' && value?.toLowerCase() === 'true') {
field.checked = true;
}
// Make sure the input is not wrapped inside another element (custom INPUT element)
if (field?.nodeName !== 'INPUT' && field?.nodeName?.includes('INPUT')) {
const childInput = field?.querySelector('input');
const fieldsFromShadowDOM = kpxcObserverHelper.findInputsFromShadowDOM(field);
field = childInput ?? fieldsFromShadowDOM[0];
}
await kpxcFill.setValueWithChange(field, value, forced);
};
// Sets a new value to input field and triggers necessary events
kpxcFill.setValueWithChange = async function(field, value, forced = false) {
if (!field || (field?.readOnly && !forced)) {
return;
}
// Check for overlays before fill
kpxcFields.discoverOverlays();
const rect = field.getBoundingClientRect();
if (kpxcFields.isOverlayOnTop(rect)) {
return;
}
const dispatchLegacyEvent = function(elem, eventName) {
const legacyEvent = elem.ownerDocument.createEvent('Event');
legacyEvent.initEvent(eventName, true, false);
elem.dispatchEvent(legacyEvent);
};
field.focus();
// Use a delay to allow focus events to trigger and to give some
// breathing room to frameworks that rely on promises to update their
// state, like React. Not doing so can break OTP input, see issue #2215.
await Promise.resolve();
field.dispatchEvent(new FocusEvent('focus', { bubbles: false, cancelable: false }));
field.dispatchEvent(new FocusEvent('focusin', { bubbles: true, cancelable: false }));
// https://w3c.github.io/uievents/#keypress-event-order
field.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: false, key: value }));
field.dispatchEvent(new InputEvent('beforeinput', { bubbles: true, cancelable: false, inputType: 'insertText', data: value }));
field.dispatchEvent(new KeyboardEvent('keypress', { bubbles: true, cancelable: false, key: value }));
field.value = value;
field.dispatchEvent(new Event('input', { bubbles: true, cancelable: false }));
field.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, cancelable: false, key: value }));
field.dispatchEvent(new Event('change', { bubbles: true, cancelable: false }));
// Some pages will not accept the value change without dispatching events directly to the document
dispatchLegacyEvent(field, 'input');
dispatchLegacyEvent(field, 'change');
};
// Check if password fill is done to a plain text field
const passwordFillIsAllowed = function(elem) {
const elementIsPasswordField =
@ -441,7 +356,7 @@ const passwordFillIsAllowed = function(elem) {
// Show a specific error notification if current database is not connected
const showErrorNotification = async function(errorMessage, notificationType = 'error') {
const connectedDatabase = await sendMessage('get_connected_database');
if (!connectedDatabase?.identifier && kpxc.databaseState === DatabaseState.UNLOCKED) {
if (!connectedDatabase?.identifier) {
kpxcUI.createNotification('error', tr('errorCurrentDatabaseNotConnected'));
} else if (!await isIframeAllowed()) {
// Special error if we are blocking due to iframe
@ -450,26 +365,3 @@ const showErrorNotification = async function(errorMessage, notificationType = 'e
kpxcUI.createNotification(notificationType, errorMessage);
}
};
// Checks if two NamedNodeMaps (attribute lists) are equal
const areNamedNodeMapsEqual = function(currentNodeMap, newNodeMap) {
if (!currentNodeMap || !newNodeMap) {
return false;
}
const fieldAttributes = Array.from(currentNodeMap);
const newFieldAttributes = Array.from(newNodeMap);
if (fieldAttributes.length !== newFieldAttributes.length) {
return false;
}
for (const attr of fieldAttributes) {
const newAttr = newFieldAttributes.find((newAttr) => newAttr?.name === attr?.name);
if (!newAttr || newAttr?.value !== attr?.value) {
return false;
}
}
return true;
};

View file

@ -27,7 +27,7 @@ kpxcForm.activateCredentialBanner = async function(usernameValue, passwordInputs
return;
}
if (passwordField && kpxcFields.isVisible(passwordField)) {
if (passwordField) {
await kpxc.setPasswordFilled(true);
}
@ -66,9 +66,6 @@ kpxcForm.getFormSubmitButton = function(form) {
return;
}
const hasPasswordClassOrId = (button) => button
&& (button?.classList.value?.toLowerCase()?.includes('password')
|| button?.id?.toLowerCase()?.includes('password'));
const action = kpxc.submitUrl || form.action;
// Check if the site needs a special handling for retrieving the form submit button
@ -91,19 +88,13 @@ kpxcForm.getFormSubmitButton = function(form) {
b => !b.getAttribute('formAction')
);
if (buttons.length > 0) {
const lastButton = buttons.at(-1);
// Accept button if it has no indication for password
if (!hasPasswordClassOrId(lastButton)) {
return buttons.at(-1);
}
return buttons.at(-1);
}
// Try to find similar buttons outside the form which are added via 'form' property
for (const e of form.elements) {
const isSubmitButton = matchesWithNodeName(e, 'BUTTON')
&& (e.type === 'button' || e.type === 'submit' || e.type === '');
const isInputButton = matchesWithNodeName(e, 'INPUT') && (e.type === 'button' || e.type === 'submit');
if ((isSubmitButton || isInputButton) && !hasPasswordClassOrId(e)) {
if ((matchesWithNodeName(e, 'BUTTON') && (e.type === 'button' || e.type === 'submit' || e.type === ''))
|| (matchesWithNodeName(e, 'INPUT') && (e.type === 'button' || e.type === 'submit'))) {
return e;
}
}
@ -125,9 +116,9 @@ kpxcForm.getNewPassword = function(passwordInputs = []) {
}
// Choose the last three password fields. The first ones are almost always for something else
const current = passwordInputs[passwordInputs.length - 3]?.value;
const newPass = passwordInputs[passwordInputs.length - 2]?.value;
const repeatNew = passwordInputs[passwordInputs.length - 1]?.value;
const current = passwordInputs[passwordInputs.length - 3].value;
const newPass = passwordInputs[passwordInputs.length - 2].value;
const repeatNew = passwordInputs[passwordInputs.length - 1].value;
if ((newPass === repeatNew && current !== newPass && current !== repeatNew)
|| (current === newPass && repeatNew !== newPass && repeatNew !== current)) {

View file

@ -1,248 +0,0 @@
'use strict';
/**
* @Object kpxcIcons
* Icon handling.
*/
const kpxcIcons = {};
kpxcIcons.icons = [];
kpxcIcons.iconTypes = {
DEFAULT: 0, // Username icon
PASSWORD: 1,
TOTP: 2
};
// Adds an icon to input field
kpxcIcons.addIcon = async function(field, iconType) {
if (!field || !Object.values(kpxcIcons.iconTypes).includes(iconType)) {
return;
}
let iconSet = false;
if (iconType === kpxcIcons.iconTypes.DEFAULT && kpxcUsernameIcons.isValid(field)) {
kpxcUsernameIcons.newIcon(field, kpxc.databaseState);
iconSet = true;
} else if (iconType === kpxcIcons.iconTypes.PASSWORD && kpxcPasswordIcons.isValid(field)) {
kpxcPasswordIcons.newIcon(field, kpxc.databaseState);
iconSet = true;
} else if (iconType === kpxcIcons.iconTypes.TOTP && kpxcTOTPIcons.isValid(field)) {
kpxcTOTPIcons.newIcon(field, kpxc.databaseState);
iconSet = true;
}
if (iconSet) {
kpxcIcons.icons.push({
field: field,
iconType: iconType
});
}
};
// Adds all icons from a form struct
kpxcIcons.addIconsFromForm = async function(form) {
const addUsernameIcons = async function(c) {
if (kpxc.settings.showLoginFormIcon && await kpxc.passwordFilledWithExceptions(c) === false) {
// Special case where everything else has been hidden, but a single password field is now displayed.
// For example PayPal and Amazon is handled like this.
if (c.username && !c.password && c.passwordInputs.length === 1) {
// Use password input directly from form if found (Epicgames)
const passwordField = c.form?.querySelector('input[type=password]');
if (c.form && passwordField) {
kpxcIcons.addIcon(passwordField, kpxcIcons.iconTypes.DEFAULT);
} else {
kpxcIcons.addIcon(c.passwordInputs[0], kpxcIcons.iconTypes.DEFAULT);
}
}
if (c.username && !c.username.readOnly) {
kpxcIcons.addIcon(c.username, kpxcIcons.iconTypes.DEFAULT);
} else if (c.password && (!c.username || (c.username && c.username.readOnly))) {
// Single password field
kpxcIcons.addIcon(c.password, kpxcIcons.iconTypes.DEFAULT);
}
}
};
const addPasswordIcons = async function(c) {
// Show password icons also with forms without any username field
if (kpxc.settings.usePasswordGeneratorIcons
&& ((c.username && c.password) || (!c.username && c.passwordInputs.length > 0))) {
for (const input of c.passwordInputs) {
kpxcIcons.addIcon(input, kpxcIcons.iconTypes.PASSWORD);
}
}
};
const addTOTPIcons = async function(c) {
if (c.totp && kpxc.settings.showOTPIcon) {
kpxcIcons.addIcon(c.totp, kpxcIcons.iconTypes.TOTP);
}
};
await Promise.all([
await addUsernameIcons(form),
await addPasswordIcons(form),
await addTOTPIcons(form)
]);
};
kpxcIcons.calculateIconOffset = function(field, size) {
const offset = Math.floor((field.offsetHeight / 2) - (size / 2) - 1);
return (offset < 0) ? 0 : offset;
};
// Delete all icons that have been hidden from the page view
kpxcIcons.deleteAllHiddenIcons = function() {
kpxcIcons.deleteIcons(kpxcUsernameIcons.icons);
kpxcIcons.deleteIcons(kpxcPasswordIcons.icons);
kpxcIcons.deleteIcons(kpxcTOTPIcons.icons);
};
// Delete hidden icons from the list
kpxcIcons.deleteIcons = function(iconList) {
const deletedIcons = [];
for (const icon of iconList) {
if (icon.inputField && !kpxcFields.isVisible(icon.inputField)) {
const index = iconList.indexOf(icon);
icon.removeIcon();
iconList.splice(index, 1);
deletedIcons.push(icon.inputField);
// Delete the input field from detected fields so the icon can be detected again
const inputFieldIndex = kpxc.inputs.indexOf(icon.inputField);
if (inputFieldIndex >= 0) {
kpxc.inputs.splice(inputFieldIndex, 1);
}
}
}
// Remove the same icons from kpxcIcons.icons array
for (const input of deletedIcons) {
const index = kpxcIcons.icons.findIndex(e => e.field === input);
if (index >= 0) {
kpxcIcons.icons.splice(index, 1);
}
}
};
// Initializes all icons needed to be shown
kpxcIcons.initIcons = async function(combinations = []) {
if (combinations.length === 0) {
return;
}
for (const form of kpxcForm.savedForms) {
await kpxcIcons.addIconsFromForm(form);
}
// Check for other combinations that are not in any form,
// or there's a form that wasn't present in savedForms (and it's not null)
for (const c of combinations) {
if (!c.form || (c.form && !kpxcForm.savedForms.some(sf => sf.form === c.form))) {
await kpxcIcons.addIconsFromForm(c);
}
}
};
kpxcIcons.hasIcon = function(field) {
return !field ? false : kpxcIcons.icons.some(i => i.field === field);
};
kpxcIcons.monitorIconPosition = function(iconClass) {
// Handle icon position on resize
window.addEventListener('resize', function(e) {
kpxcIcons.updateIconPosition(iconClass);
});
// Handle icon position on scroll
window.addEventListener('scroll', function(e) {
kpxcIcons.updateIconPosition(iconClass);
});
window.addEventListener('transitionend', function(e) {
if (matchesWithNodeName(e.target, 'INPUT') || matchesWithNodeName(e.target, 'TEXTAREA')) {
kpxcIcons.updateIconPosition(iconClass);
}
});
};
kpxcIcons.setIconPosition = function(icon, field, rtl = false, segmented = false) {
const rect = field.getBoundingClientRect();
const size = Number(icon.getAttribute('size'));
const offset = kpxcIcons.calculateIconOffset(field, size);
const zoom = kpxcUI.bodyStyle.zoom || 1;
let left = kpxcUI.getRelativeLeftPosition(rect) / zoom;
let top = kpxcUI.getRelativeTopPosition(rect) / zoom;
// Add more space for the icon to show it at the right side of the field if TOTP fields are segmented
if (segmented) {
left += size + 10;
}
// Adjusts the icon offset for certain sites
const iconOffset = kpxcSites.iconOffset(left, top, size, field?.getLowerCaseAttribute('type'));
if (iconOffset) {
left = iconOffset[0];
top = iconOffset[1];
}
const scrollTop = kpxcUI.getScrollTop() / zoom;
const scrollLeft = kpxcUI.getScrollLeft() / zoom;
icon.style.top = Pixels(top + scrollTop + offset + 1);
icon.style.left = rtl
? Pixels(left + scrollLeft + offset)
: Pixels(left + scrollLeft + field.offsetWidth - size - offset);
};
// Sets the icons to corresponding database lock status
kpxcIcons.switchIcons = async function() {
const uuid = await sendMessage('page_get_login_id');
kpxcUsernameIcons.switchIcon(kpxc.databaseState, uuid);
kpxcPasswordIcons.switchIcon(kpxc.databaseState, uuid);
kpxcTOTPIcons.switchIcon(kpxc.databaseState, uuid);
};
/**
* Detects if the input field appears or disappears -> show/hide the icon
* - boundingClientRect with slightly (< -10) negative values -> hidden
* - intersectionRatio === 0 -> hidden
* - isIntersecting === false -> hidden
* - intersectionRatio > 0 -> shown
* - isIntersecting === true -> shown
*/
kpxcIcons.updateFromIntersectionObserver = function(iconClass, entries) {
for (const entry of entries) {
const rect = DOMRectToArray(entry.boundingClientRect);
if ((entry.intersectionRatio === 0 && !entry.isIntersecting) || (rect.some(x => x < -10))) {
iconClass.icon.style.display = 'none';
} else if (entry.intersectionRatio > 0 && entry.isIntersecting) {
iconClass.icon.style.display = 'block';
// Wait for possible DOM animations
setTimeout(() => {
kpxcIcons.setIconPosition(iconClass.icon, entry.target, iconClass.rtl, iconClass.segmented);
}, 400);
}
}
};
kpxcIcons.updateIconPosition = function(iconClass) {
if (iconClass.inputField && iconClass.icon) {
kpxcIcons.setIconPosition(iconClass.icon, iconClass.inputField, iconClass.rtl, iconClass.segmented);
}
};
const DOMRectToArray = function (domRect) {
return [
domRect.bottom,
domRect.height,
domRect.left,
domRect.right,
domRect.top,
domRect.width,
domRect.x,
domRect.y,
];
};

View file

@ -1,67 +0,0 @@
'use strict';
const MIN_ICON_SIZE = 14;
const MAX_ICON_SIZE = 24;
// Basic icon class
class Icon {
constructor(field, databaseState = DatabaseState.DISCONNECTED, segmented = false) {
this.databaseState = databaseState;
this.icon = null;
this.inputField = null;
this.rtl = kpxcUI.isRTL(field);
this.segmented = segmented;
try {
this.observer = new IntersectionObserver((entries) => {
kpxcIcons.updateFromIntersectionObserver(this, entries);
});
} catch (err) {
logError(err);
}
}
// Size the icon dynamically, but not greater than 24 or smaller than 14
calculateIconSize(field) {
return Math.max(Math.min(MAX_ICON_SIZE, field.offsetHeight - 4), MIN_ICON_SIZE);
}
// Creates a wrapper div that has the icon in Shadow DOM
createWrapper(styleSheetFilename) {
const styleSheet = createStylesheet(styleSheetFilename);
const wrapper = document.createElement('div');
wrapper.style.all = 'unset';
wrapper.style.display = 'none';
// Make sure the wrapper is positioned correctly without CSS styles affecting to it
wrapper.style.position = 'absolute';
wrapper.style.top = Pixels(0);
wrapper.style.left = Pixels(0);
// Waits for stylesheet to load before displaying the element
styleSheet.addEventListener('load', () => wrapper.style.display = 'block');
this.shadowRoot = wrapper.attachShadow({ mode: 'closed' });
this.shadowRoot.append(styleSheet);
this.shadowRoot.append(this.icon);
document.body.append(wrapper);
kpxcUI.observeWrapper(wrapper);
}
removeIcon() {
this.shadowRoot.removeChild(this.icon);
document.body.removeChild(this.shadowRoot.host);
}
switchIcon(state, uuid) {
if (!this.icon) {
return;
}
if (state === DatabaseState.UNLOCKED) {
this.icon.style.filter = kpxc.credentials.length === 0 && !uuid ? 'saturate(0%)' : 'saturate(100%)';
} else {
this.icon.style.filter = 'saturate(0%)';
}
}
}

View file

@ -0,0 +1,116 @@
'use strict';
/**
* @Object kpxcIcons
* Icon handling.
*/
const kpxcIcons = {};
kpxcIcons.icons = [];
kpxcIcons.iconTypes = { USERNAME: 0, PASSWORD: 1, TOTP: 2 };
// Adds an icon to input field
kpxcIcons.addIcon = async function(field, iconType) {
if (!field || iconType < 0 || iconType > 2) {
return;
}
let iconSet = false;
if (iconType === kpxcIcons.iconTypes.USERNAME && kpxcUsernameIcons.isValid(field)) {
kpxcUsernameIcons.newIcon(field, kpxc.databaseState);
iconSet = true;
} else if (iconType === kpxcIcons.iconTypes.PASSWORD && kpxcPasswordIcons.isValid(field)) {
kpxcPasswordIcons.newIcon(field, kpxc.databaseState);
iconSet = true;
} else if (iconType === kpxcIcons.iconTypes.TOTP && kpxcTOTPIcons.isValid(field)) {
kpxcTOTPIcons.newIcon(field, kpxc.databaseState);
iconSet = true;
}
if (iconSet) {
kpxcIcons.icons.push({
field: field,
iconType: iconType
});
}
};
// Adds all icons from a form struct
kpxcIcons.addIconsFromForm = async function(form) {
const addUsernameIcons = async function(c) {
if (kpxc.settings.showLoginFormIcon && await kpxc.passwordFilledWithExceptions(c) === false) {
// Special case where everything else has been hidden, but a single password field is now displayed.
// For example PayPal and Amazon is handled like this.
if (c.username && !c.password && c.passwordInputs.length === 1) {
kpxcIcons.addIcon(c.passwordInputs[0], kpxcIcons.iconTypes.USERNAME);
}
if (c.username && !c.username.readOnly) {
kpxcIcons.addIcon(c.username, kpxcIcons.iconTypes.USERNAME);
} else if (c.password && (!c.username || (c.username && c.username.readOnly))) {
// Single password field
kpxcIcons.addIcon(c.password, kpxcIcons.iconTypes.USERNAME);
}
}
};
const addPasswordIcons = async function(c) {
// Show password icons also with forms without any username field
if (kpxc.settings.usePasswordGeneratorIcons
&& ((c.username && c.password) || (!c.username && c.passwordInputs.length > 0))) {
for (const input of c.passwordInputs) {
kpxcIcons.addIcon(input, kpxcIcons.iconTypes.PASSWORD);
}
}
};
const addTOTPIcons = async function(c) {
if (c.totp && kpxc.settings.showOTPIcon) {
kpxcIcons.addIcon(c.totp, kpxcIcons.iconTypes.TOTP);
}
};
await Promise.all([
await addUsernameIcons(form),
await addPasswordIcons(form),
await addTOTPIcons(form)
]);
};
// Delete all icons that have been hidden from the page view
kpxcIcons.deleteHiddenIcons = function() {
kpxcUsernameIcons.deleteHiddenIcons();
kpxcPasswordIcons.deleteHiddenIcons();
kpxcTOTPIcons.deleteHiddenIcons();
};
// Initializes all icons needed to be shown
kpxcIcons.initIcons = async function(combinations = []) {
if (combinations.length === 0) {
return;
}
for (const form of kpxcForm.savedForms) {
await kpxcIcons.addIconsFromForm(form);
}
// Check for other combinations that are not in any form,
// or there's a form that wasn't present in savedForms (and it's not null)
for (const c of combinations) {
if (!c.form || (c.form && !kpxcForm.savedForms.some(sf => sf.form === c.form))) {
await kpxcIcons.addIconsFromForm(c);
}
}
};
kpxcIcons.hasIcon = function(field) {
return !field ? false : kpxcIcons.icons.some(i => i.field === field);
};
// Sets the icons to corresponding database lock status
kpxcIcons.switchIcons = async function() {
const uuid = await sendMessage('page_get_login_id');
kpxcUsernameIcons.switchIcon(kpxc.databaseState, uuid);
kpxcPasswordIcons.switchIcon(kpxc.databaseState, uuid);
kpxcTOTPIcons.switchIcon(kpxc.databaseState, uuid);
};

View file

@ -32,7 +32,7 @@ kpxc.addToSitePreferences = async function(optionName, addWildcard = false) {
let site;
try {
site = trimURL(window.top.location.href);
} catch (_err) {
} catch (err) {
logDebug('Adding to Site Preferences denied from iframe.');
return;
}
@ -62,10 +62,10 @@ kpxc.addToSitePreferences = async function(optionName, addWildcard = false) {
await sendMessage('save_settings', kpxc.settings);
if (optionName === SitePreferences.ALLOW_IFRAMES) {
if (optionName === 'allowIframes') {
await sendMessage('page_set_allow_iframes', [ true, site ]);
await sendMessage('iframe_detected', false);
} else if (optionName === SitePreferences.USERNAME_ONLY) {
} else if (optionName === 'usernameOnly') {
await sendMessage('username_field_detected', false);
}
};
@ -208,8 +208,6 @@ kpxc.getSite = function(sites) {
kpxc.identifyFormInputs = async function() {
const forms = [];
const documentForms = document.forms; // Cache the value just in case
// Used for overlay security detection
kpxcFields.discoverOverlays();
for (const form of documentForms) {
if (!kpxcFields.isVisible(form)) {
@ -361,7 +359,7 @@ kpxc.initCredentialFields = async function() {
// Search all remaining inputs from the page, ignore the previous input fields
const pageInputs = await kpxcFields.getAllPageInputs(formInputs);
if (formInputs.length === 0 && pageInputs.length === 0) {
if (formInputs.length === 0 && pageInputs.length === 0 && !kpxcFields.isCustomLoginFieldsUsed()) {
// Run 'redetect_credentials' manually if no fields are found after a page load
setTimeout(async function() {
if (_called.automaticRedetectCompleted) {
@ -519,7 +517,7 @@ kpxc.prepareCredentials = async function() {
kpxc.initAutocomplete();
if (kpxc.settings.autoFillRelevantCredential) {
const pageUuid = await sendMessage('page_get_login_id', false);
const pageUuid = await sendMessage('page_get_login_id');
if (pageUuid) {
const relevantCredential = kpxc.credentials.find(c => c.uuid === pageUuid);
const combination = kpxc.combinations?.at(-1);
@ -653,10 +651,7 @@ kpxc.retrieveCredentials = async function(force = false) {
}
kpxc.url = document.location.href;
// Search for first combination that has username or password input set
const firstCombination = kpxc.combinations?.find((combination) => combination?.username || combination?.password);
kpxc.submitUrl = kpxc.getFormActionUrl(firstCombination);
kpxc.submitUrl = kpxc.getFormActionUrl(kpxc.combinations[0]);
if (kpxc.settings.autoRetrieveCredentials && kpxc.url && kpxc.submitUrl) {
await kpxc.retrieveCredentialsCallback(
@ -721,6 +716,52 @@ kpxc.setPasswordFilled = async function(state) {
await sendMessage('password_set_filled', state);
};
// Special handling for setting value to select and checkbox elements
kpxc.setValue = function(field, value, forced = false) {
if (field.matches('select')) {
value = value.toLowerCase().trim();
const options = field.querySelectorAll('option');
for (const o of options) {
if (o.textContent.toLowerCase().trim() === value) {
kpxc.setValueWithChange(field, o.value);
return false;
}
}
return;
} else if (field.getLowerCaseAttribute('type') === 'checkbox' && value?.toLowerCase() === 'true') {
field.checked = true;
}
kpxc.setValueWithChange(field, value, forced);
};
// Sets a new value to input field and triggers necessary events
kpxc.setValueWithChange = function(field, value, forced = false) {
if (!forced && field.readOnly) {
return;
}
const dispatchLegacyEvent = function(elem, eventName) {
const legacyEvent = elem.ownerDocument.createEvent('Event');
legacyEvent.initEvent(eventName, true, false);
elem.dispatchEvent(legacyEvent);
};
field.focus();
field.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: false }));
field.dispatchEvent(new KeyboardEvent('keypress', { bubbles: true, cancelable: false }));
field.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, cancelable: false }));
field.dispatchEvent(new Event('input', { bubbles: true, cancelable: false }));
field.dispatchEvent(new Event('change', { bubbles: true, cancelable: false }));
field.value = value;
// Some pages will not accept the value change without dispatching events directly to the document
dispatchLegacyEvent(field, 'input');
dispatchLegacyEvent(field, 'change');
};
kpxc.showGroupNameInAutocomplete = function() {
return !kpxc.settings.useCompactMode
|| (kpxc.settings.showGroupNameInAutocomplete && kpxc.getUniqueGroupCount(kpxc.credentials) > 1);
@ -732,7 +773,7 @@ kpxc.siteIgnored = async function(condition) {
let currentLocation;
try {
currentLocation = window.top.location.href;
} catch (_err) {
} catch (err) {
// Cross-domain security error inspecting window.top.location.href.
// This catches an error when an iframe is being accessed from another (sub)domain
// -> use the iframe URL instead.
@ -839,7 +880,7 @@ kpxc.usePredefinedSites = function(currentLocation) {
* Content script initialization.
*/
const initContentScript = async function() {
try {
try {
if (document?.documentElement?.ownerDocument?.contentType !== 'text/html'
&& document?.documentElement?.ownerDocument?.contentType !== 'application/xhtml+xml'
) {
@ -853,7 +894,6 @@ const initContentScript = async function() {
}
kpxc.settings = settings;
kpxc.isFirefox = await sendMessage('is_firefox');
if (await kpxc.siteIgnored()) {
logDebug('This site is ignored in Site Preferences.');
@ -910,9 +950,9 @@ browser.runtime.onMessage.addListener(async function(req, sender) {
if (req.action === 'activated_tab') {
kpxc.triggerActivatedTab();
} else if (req.action === 'add_allow_iframes_option') {
kpxc.addToSitePreferences(SitePreferences.ALLOW_IFRAMES);
kpxc.addToSitePreferences('allowIframes');
} else if (req.action === 'add_username_only_option') {
kpxc.addToSitePreferences(SitePreferences.USERNAME_ONLY, true);
kpxc.addToSitePreferences('usernameOnly', true);
} else if (req.action === 'check_database_hash' && 'hash' in req) {
kpxc.detectDatabaseChange(req);
} else if (req.action === 'choose_credential_fields') {
@ -941,14 +981,9 @@ browser.runtime.onMessage.addListener(async function(req, sender) {
kpxcFill.fillAttributeToActiveElementWith(req.args);
} else if (req.action === 'frame_message') {
if (req.args?.[0] === 'frame_request_to_frames' && window.self !== window.top) {
// Handle message from top window in iframe
kpxcCustomLoginFieldsBanner.handleParentWindowMessage(req.args);
} else if (req.args?.[0] === 'frame_request_to_parent' && window.self === window.top) {
// Handle message from iframe in top window
kpxcCustomLoginFieldsBanner.handleTopWindowMessage(req.args);
} else if (req.args?.[0] === 'notification_from_frame' && window.self === window.top) {
// Handle notification from iframe in top window
kpxcUI.createNotification(req?.args[1], req?.args[2]);
}
} else if (req.action === 'ignore_site') {
kpxc.ignoreSite(req.args);
@ -962,14 +997,9 @@ browser.runtime.onMessage.addListener(async function(req, sender) {
kpxc.initCredentialFields();
} else if (req.action === 'reload_extension') {
sendMessage('reconnect');
} else if (req.action === 'reopen_database') {
sendMessage(
'get_status',
[ false, true ] // Set forcePopup to true
);
} else if (req.action === 'save_credentials') {
kpxc.rememberCredentialsFromContextMenu();
} else if (req.action === 'retrieve_credentials_forced') {
} else if (req.action === 'retrive_credentials_forced') {
await kpxc.retrieveCredentials(true);
} else if (req.action === 'show_password_generator') {
kpxcPasswordGenerator.showPasswordGenerator();
@ -1010,7 +1040,7 @@ const isIframeAllowed = async function() {
// Check for Cross-domain security error when inspecting window.top.location.href
const currentLocation = window.top.location.href;
return true;
} catch (_err) {
} catch (err) {
// Inspect iframe using TLD and the tab's original URL
const allowed = await sendMessage('is_iframe_allowed', [ window.location.href, window.location.hostname ]);
if (allowed) {

View file

@ -18,18 +18,11 @@ kpxcObserverHelper.ignoredNodeNames = [
'A',
'HEAD',
'HTML',
'IMG',
'LINK',
'SCRIPT',
'VIDEO',
];
kpxcObserverHelper.ignoredPartialNodeNames = [
'REDDIT-PDP',
'RENDER-TEMPLATE',
'SHREDDIT',
];
kpxcObserverHelper.ignoredNodeTypes = [
Node.ATTRIBUTE_NODE,
Node.TEXT_NODE,
@ -79,22 +72,15 @@ kpxcObserverHelper.initObserver = async function() {
continue;
}
if (kpxcFields.hasOverlay(mut.target)) {
kpxcFields.discoverOverlays();
kpxcFields.checkExistingFields();
continue;
}
// Cache style mutations. We only need the last style mutation of the target.
kpxcObserverHelper.cacheStyle(mut, styleMutations, mutations.length);
if (mut.type === 'childList') {
mut.addedNodes.forEach(function (node) {
kpxcObserverHelper.handleObserverAdd(node);
});
mut.removedNodes.forEach(function (node) {
kpxcObserverHelper.handleObserverRemove(node);
});
if (mut.addedNodes.length > 0) {
kpxcObserverHelper.handleObserverAdd(mut.addedNodes[0]);
} else if (mut.removedNodes.length > 0) {
kpxcObserverHelper.handleObserverRemove(mut.removedNodes[0]);
}
} else if (mut.type === 'attributes' && (mut.attributeName === 'class' || mut.attributeName === 'style')) {
// Only accept targets with forms
const forms = matchesWithNodeName(mut.target, 'FORM')
@ -244,31 +230,6 @@ kpxcObserverHelper.findInputsFromShadowDOM = function(target) {
return inputFields;
};
// Detects animations and transitions. Triggers handleObserverAdd() again on animationend/transitionend.
kpxcObserverHelper.handleTransitions = function(target) {
const classList = target?.classList?.toString();
const targetHasAnimations = classList?.includes('animate');
const targetHasDurations = classList?.includes('duration') || classList?.includes('transform');
if (targetHasAnimations || targetHasDurations) {
const animations = target.getAnimations();
const transitionTime = animations[0]?.currentTime ?? 0;
// Animation found, but transition has not finished
if (animations && transitionTime === 0) {
target.addEventListener(
targetHasAnimations ? 'animationend' : 'transitionend',
() => {
kpxcObserverHelper.handleObserverAdd(target);
},
{
once: true,
},
);
}
}
};
// Adds elements to a monitor array. Identifies the input fields.
kpxcObserverHelper.handleObserverAdd = async function(target) {
if (kpxcObserverHelper.ignoredElement(target)) {
@ -281,8 +242,6 @@ kpxcObserverHelper.handleObserverAdd = async function(target) {
return;
}
kpxcObserverHelper.handleTransitions(target);
const inputs = kpxcObserverHelper.getInputs(target);
if (inputs.length === 0) {
return;
@ -300,7 +259,7 @@ kpxcObserverHelper.handleObserverAdd = async function(target) {
kpxc.prepareCredentials();
}
kpxcIcons.deleteAllHiddenIcons();
kpxcIcons.deleteHiddenIcons();
};
// Removes monitored elements
@ -314,7 +273,7 @@ kpxcObserverHelper.handleObserverRemove = function(target) {
return;
}
kpxcIcons.deleteAllHiddenIcons();
kpxcIcons.deleteHiddenIcons();
};
// Handles CSS transitionend event
@ -346,7 +305,6 @@ kpxcObserverHelper.ignoredNode = function(target) {
if (!target
|| kpxcObserverHelper.ignoredNodeTypes.some(e => e === target.nodeType)
|| kpxcObserverHelper.ignoredNodeNames.some(e => e === target.nodeName)
|| kpxcObserverHelper.ignoredPartialNodeNames.some(e => target.nodeName?.includes(e))
|| target.nodeName.startsWith('YTMUSIC')
|| target.nodeName.startsWith('YT-')) {
return true;
@ -363,7 +321,7 @@ const getShadowDOM = function(elem) {
try {
return elem.openOrClosedShadowRoot ? elem.openOrClosedShadowRoot : browser.dom.openOrClosedShadowRoot(elem);
} catch (_e) {
} catch (e) {
return elem.shadowRoot;
}
};
@ -372,9 +330,7 @@ const getShadowDOM = function(elem) {
const treeWalkerFilter = function(node) {
return !node ||
node?.disabled ||
(node instanceof Element
&& typeof node?.getAttribute === 'function'
&& node?.getLowerCaseAttribute('type') === 'hidden')
(typeof node?.getAttribute !== 'undefined' && node?.getLowerCaseAttribute('type') === 'hidden')
? NodeFilter.FILTER_REJECT
: NodeFilter.FILTER_ACCEPT;
};

View file

@ -8,14 +8,13 @@ const PASSKEYS_WAIT_FOR_LIFETIMER = 30;
const enablePasskeys = async function() {
const passkeysLogDebug = function(message, extra) {
if (kpxcPasskeysUtils.debugLogging) {
debugLogMessage(message, extra);
debugLogMessage(message, extra);
}
};
const passkeys = document.createElement('script');
passkeys.src = chrome.runtime.getURL('content/passkeys.js');
document.documentElement.appendChild(passkeys);
passkeys.remove();
const startTimer = function(timeout) {
return setTimeout(() => {
@ -63,14 +62,6 @@ const enablePasskeys = async function() {
}
};
const isSameOriginWithAncestors = function () {
try {
return window.origin === window.top.origin;
} catch (_err) {
return false;
}
};
document.addEventListener('kpxc-passkeys-request', async (ev) => {
if (!window.isSecureContext) {
kpxcUI.createNotification('error', tr('errorMessagePasskeysContextIsNotSecure'));
@ -80,14 +71,14 @@ const enablePasskeys = async function() {
if (ev.detail.action === 'passkeys_create') {
const publicKey = kpxcPasskeysUtils.buildCredentialCreationOptions(
ev.detail.publicKey,
isSameOriginWithAncestors(),
ev.detail.sameOriginWithAncestors,
);
passkeysLogDebug('Passkey request', publicKey);
await sendResponse('passkeys_register', publicKey);
} else if (ev.detail.action === 'passkeys_get') {
const publicKey = kpxcPasskeysUtils.buildCredentialRequestOptions(
ev.detail.publicKey,
isSameOriginWithAncestors(),
ev.detail.sameOriginWithAncestors,
);
passkeysLogDebug('Passkey request', publicKey);
await sendResponse('passkeys_get', publicKey);
@ -108,7 +99,7 @@ const initContent = async () => {
return;
}
if (await chrome.runtime.sendMessage({ action: 'is_site_ignored', args: [ window.self.location.href, true ] })) {
if (await chrome.runtime.sendMessage({ action: 'is_site_ignored', args: window.self.location.href })) {
console.log('This site is ignored in Site Preferences.');
return;
}

View file

@ -26,58 +26,13 @@
return kpxcStringToArrayBuffer(window.atob(str?.replaceAll('-', '+').replaceAll('_', '/')));
};
// From ArrayBuffer to URL encoded base64 string
const kpxcArrayBufferToBase64 = function(buf) {
const str = [ ...new Uint8Array(buf) ].map(c => String.fromCharCode(c)).join('');
return window.btoa(str).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');
};
// Returns the PublicKeyCredential as JSON
// See: https://w3c.github.io/webauthn/#dom-publickeycredential-tojson
const kpxcPublicKeyCredentialJson = function (credential, publicKey) {
const clientExtensionResults = credential.getClientExtensionResults();
const type = credential.type;
const authenticatorAttachment = credential.authenticatorAttachment;
let response;
if (credential.response instanceof AuthenticatorAttestationResponse) {
const responsePublicKey = credential.response.getPublicKey();
response = {
clientDataJSON: publicKey.response.clientDataJSON,
authenticatorData: publicKey.response.authenticatorData,
transports: credential.response.getTransports(),
publicKey: responsePublicKey ? kpxcArrayBufferToBase64(responsePublicKey) : null,
publicKeyAlgorithm: credential.response.getPublicKeyAlgorithm(),
attestationObject: publicKey.response.attestationObject,
};
}
if (credential.response instanceof AuthenticatorAssertionResponse) {
response = {
clientDataJSON: publicKey.response.clientDataJSON,
authenticatorData: publicKey.response.authenticatorData,
signature: publicKey.response.signature,
userHandle: publicKey.response?.userHandle || undefined,
};
}
return {
id: publicKey.id,
rawId: publicKey.id,
response,
authenticatorAttachment,
clientExtensionResults,
type,
};
};
// Wraps response to AuthenticatorAttestationResponse object
const createAttestationResponse = function(publicKey) {
const response = {
attestationObject: kpxcBase64ToArrayBuffer(publicKey.response.attestationObject),
clientDataJSON: kpxcBase64ToArrayBuffer(publicKey.response.clientDataJSON),
getAuthenticatorData: () => kpxcBase64ToArrayBuffer(publicKey.response?.authenticatorData),
getPublicKey: () => publicKey.response?.publicKey ? publicKey.response?.publicKey : null,
getPublicKey: () => null,
getPublicKeyAlgorithm: () => publicKey.response?.publicKeyAlgorithm,
getTransports: () => [ 'internal' ]
};
@ -108,8 +63,7 @@
response: authenticatorResponse,
type: publicKey.type,
clientExtensionResults: () => publicKey?.response?.clientExtensionResults || {},
getClientExtensionResults: () => publicKey?.response?.clientExtensionResults || {},
toJSON: () => kpxcPublicKeyCredentialJson(publicKeyCredential, publicKey)
getClientExtensionResults: () => publicKey?.response?.clientExtensionResults || {}
};
return Object.setPrototypeOf(publicKeyCredential, PublicKeyCredential.prototype);
@ -137,6 +91,14 @@
});
};
const isSameOriginWithAncestors = function() {
try {
return window.self.origin === window.top.origin;
} catch (err) {
return false;
}
};
// Throws errors to a correct exceptions
const throwError = function(errorCode, errorMessage) {
if ((!errorCode && !errorMessage) || errorCode === PASSKEYS_REQUEST_CANCELED) {
@ -185,9 +147,11 @@
return null;
}
const sameOriginWithAncestors = isSameOriginWithAncestors();
const response = await postMessageToExtension({
action: 'passkeys_create',
publicKey: options.publicKey,
sameOriginWithAncestors: sameOriginWithAncestors,
});
if (!response.publicKey) {
@ -208,9 +172,11 @@
return originalCredentials.get(options);
}
const sameOriginWithAncestors = isSameOriginWithAncestors();
const response = await postMessageToExtension({
action: 'passkeys_get',
publicKey: options.publicKey,
sameOriginWithAncestors: sameOriginWithAncestors,
});
if (!response.publicKey) {
@ -235,14 +201,12 @@
// select a software authenticator. This could be removed in the future.
try {
Object.defineProperty(navigator, 'credentials', { value: passkeysCredentials });
if (window.PublicKeyCredential) {
Object.defineProperty(window.PublicKeyCredential, 'isConditionalMediationAvailable', {
value: isConditionalMediationAvailable,
});
Object.defineProperty(window.PublicKeyCredential, 'isUserVerifyingPlatformAuthenticatorAvailable', {
value: isUserVerifyingPlatformAuthenticatorAvailable,
});
}
Object.defineProperty(window.PublicKeyCredential, 'isConditionalMediationAvailable', {
value: isConditionalMediationAvailable,
});
Object.defineProperty(window.PublicKeyCredential, 'isUserVerifyingPlatformAuthenticatorAvailable', {
value: isUserVerifyingPlatformAuthenticatorAvailable,
});
} catch (err) {
console.log('Cannot override navigator.credentials: ', err);
}

View file

@ -11,6 +11,10 @@ kpxcPasswordIcons.switchIcon = function(state) {
kpxcPasswordIcons.icons.forEach(u => u.switchIcon(state));
};
kpxcPasswordIcons.deleteHiddenIcons = function() {
kpxcUI.deleteHiddenIcons(kpxcPasswordIcons.icons);
};
kpxcPasswordIcons.isValid = function(field) {
if (!field
|| field.readOnly
@ -30,7 +34,7 @@ class PasswordIcon extends Icon {
this.nextFieldExists = false;
this.initField(field);
kpxcIcons.monitorIconPosition(this);
kpxcUI.monitorIconPosition(this);
}
}
@ -45,22 +49,19 @@ PasswordIcon.prototype.initField = function(field) {
};
PasswordIcon.prototype.createIcon = function(field) {
const className = getIconClass('key');
const size = this.calculateIconSize(field);
const className = (isFirefox() ? 'key-moz' : 'key');
const size = (field.offsetHeight > 28) ? 24 : 16;
const offset = kpxcUI.calculateIconOffset(field, size);
const icon = kpxcUI.createElement('div', 'kpxc kpxc-pwgen-icon ' + className,
{
'title': tr('passwordGeneratorGenerateText'),
'size': size,
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id'),
'popover': 'manual'
'offset': offset,
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id') // Needed?
});
if (kpxcFields.popoverSupported) {
icon.style.margin = 0;
} else {
icon.style.zIndex = '10000000';
}
icon.style.zIndex = '10000000';
icon.style.width = Pixels(size);
icon.style.height = Pixels(size);
@ -74,11 +75,7 @@ PasswordIcon.prototype.createIcon = function(field) {
}
if (e.shiftKey) {
if (kpxcFields.popoverSupported) {
icon.hidePopover();
} else {
icon.style.display = 'none';
}
icon.style.display = 'none';
return;
}
@ -89,12 +86,9 @@ PasswordIcon.prototype.createIcon = function(field) {
icon.addEventListener('mousedown', ev => ev.stopPropagation());
icon.addEventListener('mouseup', ev => ev.stopPropagation());
kpxcIcons.setIconPosition(icon, field, this.rtl);
kpxcUI.setIconPosition(icon, field, this.rtl);
this.icon = icon;
this.createWrapper('css/pwgen.css');
if (kpxcFields.popoverSupported) {
icon.showPopover();
}
};
@ -157,7 +151,13 @@ kpxcPasswordGenerator.fill = function(elem, password) {
const isPasswordGeneratorSupported = async function() {
const response = await browser.runtime.sendMessage({
action: 'get_features_list'
action: 'get_keepassxc_versions'
});
return response?.passwordGenerator;
const result = await browser.runtime.sendMessage({
action: 'compare_versions',
args: [ [ '2.7.0' ], response.current ]
});
return result['2.7.0'] || false;
};

View file

@ -1,6 +1,6 @@
'use strict';
const ignoreRegex = /(bank|coupon|post(al)?|user|zip|promo).*code|(en|de)code(d|r)*|comment|author|error/i;
const ignoreRegex = /(bank|coupon|postal|user|zip).*code|(en|de)code(d|r)*|comment|author|error/i;
const ignoredTypes = [ 'email', 'password', 'username' ];
const allowedInputTypes = [ 'number', 'password', 'tel', 'text' ];
@ -13,14 +13,12 @@ const acceptedOTPFields = [
'idvpin',
'mfa',
'one_time_password',
'otc-confirmation-input',
'otp',
'token',
'twofa',
'two-factor',
'twofactor',
'verification_pin',
'mfaCode'
'verification_pin'
];
const acceptedParents = [
@ -38,6 +36,10 @@ kpxcTOTPIcons.switchIcon = function(state, uuid) {
kpxcTOTPIcons.icons.forEach(u => u.switchIcon(state, uuid));
};
kpxcTOTPIcons.deleteHiddenIcons = function() {
kpxcUI.deleteHiddenIcons(kpxcTOTPIcons.icons);
};
kpxcTOTPIcons.autoCompleteIsOneTimeCode = function(field) {
if (!field) {
return false;
@ -107,7 +109,7 @@ class TOTPFieldIcon extends Icon {
super(field, databaseState, segmented);
this.initField(field, segmented);
kpxcIcons.monitorIconPosition(this);
kpxcUI.monitorIconPosition(this);
}
}
@ -137,21 +139,19 @@ TOTPFieldIcon.prototype.initField = async function(field, segmented) {
};
TOTPFieldIcon.prototype.createIcon = function(field, segmented = false) {
const className = getIconClass('kpxc-totp-icon');
const size = this.calculateIconSize(field);
const className = (isFirefox() ? 'moz' : 'default');
const icon = kpxcUI.createElement('div', 'kpxc ' + className,
// Size the icon dynamically, but not greater than 24 or smaller than 14
const size = Math.max(Math.min(24, field.offsetHeight - 4), 14);
const offset = kpxcUI.calculateIconOffset(field, size);
const icon = kpxcUI.createElement('div', 'kpxc kpxc-totp-icon ' + className,
{
'title': tr('totpFieldText'),
'size': size,
'popover': 'manual'
'offset': offset
});
if (kpxcFields.popoverSupported) {
icon.style.margin = 0;
} else {
icon.style.zIndex = '10000000';
}
icon.style.zIndex = '10000000';
icon.style.width = Pixels(size);
icon.style.height = Pixels(size);
@ -167,11 +167,7 @@ TOTPFieldIcon.prototype.createIcon = function(field, segmented = false) {
}
if (e.shiftKey) {
if (kpxcFields.popoverSupported) {
icon.hidePopover();
} else {
icon.style.display = 'none';
}
icon.style.display = 'none';
return;
}
@ -183,10 +179,7 @@ TOTPFieldIcon.prototype.createIcon = function(field, segmented = false) {
icon.addEventListener('mousedown', ev => ev.stopPropagation());
icon.addEventListener('mouseup', ev => ev.stopPropagation());
kpxcIcons.setIconPosition(icon, field, this.rtl, segmented);
kpxcUI.setIconPosition(icon, field, this.rtl, segmented);
this.icon = icon;
this.createWrapper('css/totp.css');
if (kpxcFields.popoverSupported) {
icon.showPopover();
}
};

View file

@ -13,12 +13,6 @@ const ORANGE_BUTTON = 'kpxc-button kpxc-orange-button';
const RED_BUTTON = 'kpxc-button kpxc-red-button';
const GRAY_BUTTON_CLASS = 'kpxc-gray-button';
const ALLOWED_OBSERVER_NODETYPES = [
Node.ELEMENT_NODE,
Node.DOCUMENT_NODE,
Node.DOCUMENT_FRAGMENT_NODE
];
const OBSERVER_OPTIONS = { attributes: true, attributeFilter: [ 'style' ] };
const DatabaseState = {
@ -35,15 +29,74 @@ const $ = function(elem) {
return document.querySelector(elem);
};
// Returns a string with 'px' for CSS styles
const Pixels = function(value) {
return String(value) + 'px';
};
// Basic icon class
class Icon {
constructor(field, databaseState = DatabaseState.DISCONNECTED, segmented = false) {
this.databaseState = databaseState;
this.icon = null;
this.inputField = null;
this.rtl = kpxcUI.isRTL(field);
this.segmented = segmented;
try {
this.observer = new IntersectionObserver((entries) => {
kpxcUI.updateFromIntersectionObserver(this, entries);
});
} catch (err) {
logError(err);
}
}
// Creates a wrapper div that has the icon in Shadow DOM
createWrapper(styleSheetFilename) {
const styleSheet = createStylesheet(styleSheetFilename);
const wrapper = document.createElement('div');
wrapper.style.all = 'unset';
wrapper.style.display = 'none';
// Make sure the wrapper is positioned correctly without CSS styles affecting to it
wrapper.style.position = 'absolute';
wrapper.style.top = Pixels(0);
wrapper.style.left = Pixels(0);
// Waits for stylesheet to load before displaying the element
styleSheet.addEventListener('load', () => wrapper.style.display = 'block');
this.shadowRoot = wrapper.attachShadow({ mode: 'closed' });
this.shadowRoot.append(styleSheet);
this.shadowRoot.append(this.icon);
document.body.append(wrapper);
kpxcUI.observeWrapper(wrapper);
}
switchIcon(state, uuid) {
if (!this.icon) {
return;
}
if (state === DatabaseState.UNLOCKED) {
this.icon.style.filter = kpxc.credentials.length === 0 && !uuid ? 'saturate(0%)' : 'saturate(100%)';
} else {
this.icon.style.filter = 'saturate(0%)';
}
}
removeIcon() {
this.shadowRoot.removeChild(this.icon);
document.body.removeChild(this.shadowRoot.host);
}
}
const kpxcUI = {};
kpxcUI.mouseDown = false;
if (document.body) {
const bodyRect = document.body.getBoundingClientRect();
kpxcUI.bodyRect = {
left: bodyRect.left + window.pageXOffset,
top: bodyRect.top + window.pageYOffset
};
kpxcUI.bodyRect = document.body.getBoundingClientRect();
kpxcUI.bodyStyle = getComputedStyle(document.body);
}
@ -71,6 +124,63 @@ kpxcUI.createElement = function(type, classes, attributes, textContent) {
return element;
};
kpxcUI.monitorIconPosition = function(iconClass) {
// Handle icon position on resize
window.addEventListener('resize', function(e) {
kpxcUI.updateIconPosition(iconClass);
});
// Handle icon position on scroll
window.addEventListener('scroll', function(e) {
kpxcUI.updateIconPosition(iconClass);
});
window.addEventListener('transitionend', function(e) {
if (matchesWithNodeName(e.target, 'INPUT') || matchesWithNodeName(e.target, 'TEXTAREA')) {
kpxcUI.updateIconPosition(iconClass);
}
});
};
kpxcUI.updateIconPosition = function(iconClass) {
if (iconClass.inputField && iconClass.icon) {
kpxcUI.setIconPosition(iconClass.icon, iconClass.inputField, iconClass.rtl, iconClass.segmented);
}
};
kpxcUI.calculateIconOffset = function(field, size) {
const offset = Math.floor((field.offsetHeight / 2) - (size / 2) - 1);
return (offset < 0) ? 0 : offset;
};
kpxcUI.setIconPosition = function(icon, field, rtl = false, segmented = false) {
const rect = field.getBoundingClientRect();
const size = Number(icon.getAttribute('size'));
const offset = kpxcUI.calculateIconOffset(field, size);
const zoom = kpxcUI.bodyStyle.zoom || 1;
let left = kpxcUI.getRelativeLeftPosition(rect) / zoom;
let top = kpxcUI.getRelativeTopPosition(rect) / zoom;
// Add more space for the icon to show it at the right side of the field if TOTP fields are segmented
if (segmented) {
left += size + 10;
}
// Adjusts the icon offset for certain sites
const iconOffset = kpxcSites.iconOffset(left, top, size, field?.getLowerCaseAttribute('type'));
if (iconOffset) {
left = iconOffset[0];
top = iconOffset[1];
}
const scrollTop = kpxcUI.getScrollTop() / zoom;
const scrollLeft = kpxcUI.getScrollLeft() / zoom;
icon.style.top = Pixels(top + scrollTop + offset + 1);
icon.style.left = rtl
? Pixels(left + scrollLeft + offset)
: Pixels(left + scrollLeft + field.offsetWidth - size - offset);
};
kpxcUI.getScrollTop = function() {
return document.defaultView?.scrollY ?? document.scrollingElement?.scrollTop ?? 0;
};
@ -87,6 +197,32 @@ kpxcUI.getRelativeTopPosition = function(rect) {
return kpxcUI.bodyStyle.position.toLowerCase() === 'relative' ? rect.top - kpxcUI.bodyRect.top : rect.top;
};
kpxcUI.deleteHiddenIcons = function(iconList) {
const deletedIcons = [];
for (const icon of iconList) {
if (icon.inputField && !kpxcFields.isVisible(icon.inputField)) {
const index = iconList.indexOf(icon);
icon.removeIcon();
iconList.splice(index, 1);
deletedIcons.push(icon.inputField);
// Delete the input field from detected fields so the icon can be detected again
const inputFieldIndex = kpxc.inputs.indexOf(icon.inputField);
if (inputFieldIndex >= 0) {
kpxc.inputs.splice(inputFieldIndex, 1);
}
}
}
// Remove the same icons from kpxcIcons.icons array
for (const input of deletedIcons) {
const index = kpxcIcons.icons.findIndex(e => e.field === input);
if (index >= 0) {
kpxcIcons.icons.splice(index, 1);
}
}
};
kpxcUI.isRTL = function(field) {
if (!field) {
return false;
@ -157,29 +293,48 @@ kpxcUI.makeBannerDraggable = function(banner) {
});
};
/**
* Detects if the input field appears or disappears -> show/hide the icon
* - boundingClientRect with slightly (< -10) negative values -> hidden
* - intersectionRatio === 0 -> hidden
* - isIntersecting === false -> hidden
* - intersectionRatio > 0 -> shown
* - isIntersecting === true -> shown
*/
kpxcUI.updateFromIntersectionObserver = function(iconClass, entries) {
for (const entry of entries) {
const rect = DOMRectToArray(entry.boundingClientRect);
if ((entry.intersectionRatio === 0 && !entry.isIntersecting) || (rect.some(x => x < -10))) {
iconClass.icon.style.display = 'none';
} else if (entry.intersectionRatio > 0 && entry.isIntersecting) {
iconClass.icon.style.display = 'block';
// Wait for possible DOM animations
setTimeout(() => {
kpxcUI.setIconPosition(iconClass.icon, entry.target, iconClass.rtl, iconClass.segmented);
}, 400);
}
}
};
/**
* Creates a self-disappearing notification banner to DOM
* @param {string} type Notification type: (success, info, warning, error)
* @param {string} message The message shown
*/
kpxcUI.createNotification = async function(type, message) {
kpxcUI.createNotification = function(type, message) {
if (!kpxc.settings.showNotifications || !type || !message) {
return;
}
// Send the notification to top window from iframe
if (window.self !== window.top) {
await sendMessage('frame_message', [ 'notification_from_frame', type, message ]);
return;
}
// Removes notification from the body element
const removeNotification = function() {
// Catch cross-domain exception
let parentBody;
try {
parentBody = window.parent.document.body;
} catch(_e) {
} catch(e) {
parentBody = window.document.body;
}
@ -201,7 +356,7 @@ kpxcUI.createNotification = async function(type, message) {
const notification = kpxcUI.createElement('div', 'kpxc-notification kpxc-notification-' + type, {});
type = type.charAt(0).toUpperCase() + type.slice(1) + '!';
const className = getIconClass('kpxc-banner-icon');
const className = (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon');
const icon = kpxcUI.createElement('span', className, { 'alt': 'logo' });
const label = kpxcUI.createElement('span', 'kpxc-label', {}, type);
const msg = kpxcUI.createElement('span', '', {}, message);
@ -269,12 +424,12 @@ kpxcUI.createPageObserver = function() {
}
});
if (document?.documentElement && ALLOWED_OBSERVER_NODETYPES.includes(document.documentElement.nodeType)) {
kpxcUI.pageObserver.observe(document.documentElement, OBSERVER_OPTIONS);
}
if (document?.body && ALLOWED_OBSERVER_NODETYPES.includes(document.body.nodeType)) {
kpxcUI.pageObserver.observe(document.body, OBSERVER_OPTIONS);
}
kpxcUI.pageObserver.observe(document.documentElement, OBSERVER_OPTIONS);
kpxcUI.pageObserver.observe(document.body, OBSERVER_OPTIONS);
};
const DOMRectToArray = function(domRect) {
return [ domRect.bottom, domRect.height, domRect.left, domRect.right, domRect.top, domRect.width, domRect.x, domRect.y ];
};
const initColorTheme = function(elem) {

View file

@ -12,6 +12,10 @@ kpxcUsernameIcons.switchIcon = function(state) {
kpxcUsernameIcons.icons.forEach(u => u.switchIcon(state));
};
kpxcUsernameIcons.deleteHiddenIcons = function() {
kpxcUI.deleteHiddenIcons(kpxcUsernameIcons.icons);
};
kpxcUsernameIcons.isValid = function(field) {
if (!field
|| field.offsetWidth < MIN_INPUT_FIELD_OFFSET_WIDTH
@ -30,7 +34,7 @@ class UsernameFieldIcon extends Icon {
super(field, databaseState);
this.initField(field);
kpxcIcons.monitorIconPosition(this);
kpxcUI.monitorIconPosition(this);
}
switchIcon(state) {
@ -64,7 +68,9 @@ UsernameFieldIcon.prototype.initField = function(field) {
UsernameFieldIcon.prototype.createIcon = function(field) {
const className = getIconClassName(this.databaseState);
const size = this.calculateIconSize(field);
// Size the icon dynamically, but not greater than 24 or smaller than 14
const size = Math.max(Math.min(24, field.offsetHeight - 4), 14);
// Don't create the icon if the input field is too small
if (field.offsetWidth < (size * 1.5) || field.offsetHeight < size) {
@ -72,19 +78,16 @@ UsernameFieldIcon.prototype.createIcon = function(field) {
return;
}
const offset = kpxcUI.calculateIconOffset(field, size);
const icon = kpxcUI.createElement('div', 'kpxc kpxc-username-icon ' + className,
{
'title': getIconText(this.databaseState),
'size': size,
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id'),
'popover': 'manual'
'offset': offset,
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id')
});
if (kpxcFields.popoverSupported) {
icon.style.margin = 0;
} else {
icon.style.zIndex = '10000000';
}
icon.style.zIndex = '10000000';
icon.style.width = Pixels(size);
icon.style.height = Pixels(size);
@ -94,11 +97,7 @@ UsernameFieldIcon.prototype.createIcon = function(field) {
}
if (e.shiftKey) {
if (kpxcFields.popoverSupported) {
icon.hidePopover();
} else {
icon.style.display = 'none';
}
icon.style.display = 'none';
return;
}
@ -109,12 +108,9 @@ UsernameFieldIcon.prototype.createIcon = function(field) {
icon.addEventListener('mousedown', ev => ev.stopPropagation());
icon.addEventListener('mouseup', ev => ev.stopPropagation());
kpxcIcons.setIconPosition(icon, field, this.rtl);
kpxcUI.setIconPosition(icon, field, this.rtl);
this.icon = icon;
this.createWrapper('css/username.css');
if (kpxcFields.popoverSupported) {
icon.showPopover();
}
};
const iconClicked = async function(field, icon) {
@ -143,12 +139,12 @@ const iconClicked = async function(field, icon) {
const getIconClassName = function(state = DatabaseState.UNLOCKED) {
if (state === DatabaseState.LOCKED) {
return getIconClass('lock');
return (isFirefox() ? 'lock-moz' : 'lock');
} else if (state === DatabaseState.DISCONNECTED) {
return getIconClass('disconnected');
return (isFirefox() ? 'disconnected-moz' : 'disconnected');
}
return getIconClass('unlock');
return (isFirefox() ? 'unlock-moz' : 'unlock');
};
const getIconText = function(state) {

View file

@ -13,7 +13,7 @@ div.kpxc-banner {
margin: 0 auto !important;
min-height: 2em !important;
max-height: 4em !important;
overflow-x: auto !important;
overflow-x: hidden !important;
padding: 4px !important;
position: fixed !important;
width: 100% !important;
@ -49,10 +49,6 @@ div.kpxc-banner .banner-info > span {
margin: 4px;
}
div.kpxc-banner .banner-info-text {
text-wrap: nowrap;
}
div.kpxc-banner div.banner-info, div.banner-buttons {
display: inline-flex;
align-items: center;
@ -81,14 +77,6 @@ div.kpxc-banner .kpxc-banner-icon-moz {
background-size: contain;
}
div.kpxc-banner .kpxc-banner-icon-safari {
width: 24px;
height: 24px;
overflow: hidden;
background: url('safari-web-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat;
background-size: contain;
}
div.kpxc-banner .kpxc-help-icon {
width: 24px;
height: 24px;
@ -105,14 +93,6 @@ div.kpxc-banner .kpxc-help-icon-moz {
background-size: contain;
}
div.kpxc-banner .kpxc-help-icon-safari {
width: 24px;
height: 24px;
overflow: hidden;
background: url('safari-web-extension://__MSG_@@extension_id__/icons/help.svg') right no-repeat;
background-size: contain;
}
.kpxc-separator {
border-left: 1px solid #ccc;
height: 100% !important;
@ -133,7 +113,6 @@ div.kpxc-banner label {
font-size: 12px !important;
font-weight: normal !important;
margin: 4px !important;
text-wrap: nowrap;
}
div.kpxc-banner-dialog {

View file

@ -8,7 +8,6 @@
height: 28px !important;
margin: .2em !important;
padding: 1px 7px 2px !important;
text-wrap: nowrap;
}
.kpxc-white-button {

View file

@ -4,7 +4,6 @@
--kpxc-autocomplete-menu-border: 1px solid #ddd;
--kpxc-background-color: #fff;
--kpxc-box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
--kpxc-box-shadow-strong: 0 20px 25px -5px rgb(0 0 0 / 0.8), 0 8px 10px -6px rgb(0 0 0 / 0.8);
--kpxc-card-background-color: #fff;
--kpxc-card-border-color: rgba(0, 0, 0, .125);
--kpxc-card-header-color: #fafafa;
@ -30,6 +29,7 @@
}
@media (prefers-color-scheme: dark) {
:root,
:host {
--kpxc-autocomplete-footer-color: #2b2a2a;
@ -114,3 +114,4 @@
--kpxc-lighter-text-color: rgba(0, 0, 0, 0.8);
--kpxc-light-text-color: rgba(0, 0, 0, 0.5);
}
}

View file

@ -43,16 +43,6 @@
background-size: contain;
}
.kpxc-notification .kpxc-banner-icon-safari {
width: 24px;
height: 24px;
padding: 10px;
margin-right: 4px;
overflow: hidden;
background: url('safari-web-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat;
background-size: contain;
}
.kpxc-notification .kpxc-label {
font-weight: bold;
}

View file

@ -1,7 +1,5 @@
.kpxc-pwgen-icon {
border: 0;
cursor: pointer;
padding: 0 !important;
position: absolute;
}
@ -14,8 +12,3 @@
background: url('moz-extension://__MSG_@@extension_id__/icons/key.svg') right no-repeat;
background-size: contain;
}
.kpxc-pwgen-icon.key-safari {
background: url('safari-web-extension://__MSG_@@extension_id__/icons/key.svg') right no-repeat;
background-size: contain;
}

View file

@ -1,21 +1,14 @@
.kpxc-totp-icon {
border: 0;
cursor: pointer;
padding: 0 !important;
position: absolute;
cursor: pointer;
}
.kpxc-totp-icon {
.kpxc-totp-icon.default {
background: url('chrome-extension://__MSG_@@extension_id__/icons/otp.svg') right no-repeat;
background-size: contain;
}
.kpxc-totp-icon-moz {
.kpxc-totp-icon.moz {
background: url('moz-extension://__MSG_@@extension_id__/icons/otp.svg') right no-repeat;
background-size: contain;
}
.kpxc-totp-icon.safari {
background: url('safari-web-extension://__MSG_@@extension_id__/icons/otp.svg') right no-repeat;
background-size: contain;
}
}

View file

@ -1,8 +1,6 @@
.kpxc-username-icon {
border: 0;
cursor: pointer;
padding: 0 !important;
position: absolute;
cursor: pointer;
}
.kpxc-username-icon.disconnected {
@ -15,11 +13,6 @@
background-size: contain;
}
.kpxc-username-icon.disconnected-safari {
background: url('safari-web-extension://__MSG_@@extension_id__/icons/disconnected.svg') right no-repeat;
background-size: contain;
}
.kpxc-username-icon.lock {
background: url('chrome-extension://__MSG_@@extension_id__/icons/locked.svg') right no-repeat;
background-size: contain;
@ -30,11 +23,6 @@
background-size: contain;
}
.kpxc-username-icon.lock-safari {
background: url('safari-web-extension://__MSG_@@extension_id__/icons/locked.svg') right no-repeat;
background-size: contain;
}
.kpxc-username-icon.unlock {
background: url('chrome-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat;
background-size: contain;
@ -44,8 +32,3 @@
background: url('moz-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat;
background-size: contain;
}
.kpxc-username-icon.unlock-safari {
background: url('safari-web-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat;
background-size: contain;
}

View file

@ -1,9 +1,9 @@
{
"manifest_version": 3,
"name": "KeePassXC-Browser",
"version": "1.10.0.1",
"version_name": "1.10.0.1",
"minimum_chrome_version": "124",
"version": "1.9.9.3",
"version_name": "1.9.9.3",
"minimum_chrome_version": "93",
"description": "__MSG_extensionDescription__",
"author": "KeePassXC Team",
"icons": {
@ -53,8 +53,7 @@
"content/fields.js",
"content/fill.js",
"content/form.js",
"content/icon.js",
"content/icon-handler.js",
"content/icons.js",
"content/keepassxc-browser.js",
"content/observer-helper.js",
"content/pwgen.js",
@ -119,15 +118,12 @@
"choose_credential_fields": {
"description": "__MSG_popupChooseCredentialsText__"
},
"retrieve_credentials_forced": {
"description": "__MSG_credentialsRetrieveButton__"
"retrive_credentials_forced": {
"description": "__MSG_popupReopenButton__"
},
"request_autotype": {
"description": "__MSG_contextMenuRequestGlobalAutoType__"
},
"reopen_database": {
"description": "__MSG_popupReopenButton__"
},
"reload_extension": {
"description": "__MSG_popupReloadButton__"
}
@ -170,7 +166,8 @@
"tabs",
"webNavigation",
"webRequest",
"webRequestAuthProvider"
"webRequestAuthProvider",
"webRequestBlocking"
],
"optional_permissions": [
"privacy"

View file

@ -82,10 +82,6 @@ pre {
white-space: pre-line;
}
.no-transitions .form-check-input {
transition: none !important;
}
.table > caption {
color: var(--kpxc-text-color);
}
@ -109,6 +105,12 @@ tbody {
color: var(--kpxc-text-color);
}
.table-striped > tbody > tr:nth-of-type(odd) input[type="checkbox"] {
background-color: var(--kpxc-card-background-color) !important;
border-color: var(--kpxc-input-main-border-color) !important;
color: var(--kpxc-text-color) !important;
}
.table-striped > tbody > tr:nth-of-type(odd) .form-check-input:checked {
background-color: var(--kpxc-checkbox-background-color) !important;
}
@ -134,14 +136,14 @@ table tbody tr.empty:not(:nth-last-child(2)) {
display: none;
}
#tab-site-preferences td:nth-of-type(3),
#tab-site-preferences td:nth-of-type(3) select {
#tab-site-preferences td:nth-of-type(2),
#tab-site-preferences td:nth-of-type(2) select {
width: 200px;
}
#tab-site-preferences td:nth-of-type(2),
#tab-site-preferences td:nth-of-type(3),
#tab-site-preferences td:nth-of-type(4),
#tab-site-preferences td:nth-of-type(5),
table td:last-of-type {
width: 1px;
}
@ -231,26 +233,6 @@ table td:last-of-type {
color: var(--kpxc-text-color);
}
.non-clickable {
pointer-events: none;
}
.settings-dropdown {
box-shadow: var(--kpxc-box-shadow-strong);
overflow: hidden;
position: absolute;
}
.settings-dropdown-body {
background-color: var(--bs-table-bg);
}
.dropdown-divider {
border-top: 1px solid var(--kpxc-table-odd-color);
height: 0.5rem;
margin-top: 0.3rem;
}
.site-preferences-input {
border: 0;
border-bottom-right-radius: 4px !important;

View file

@ -19,7 +19,7 @@
<script src="options.js"></script>
<script src="../common/translate.js" defer></script>
</head>
<body class="pt-3 pb-5 no-transitions">
<body class="pt-3 pb-5">
<div class="container-fluid">
<div class="row">
<nav class="col-md-3 col-lg-2 sidebar">
@ -31,7 +31,7 @@
<ul class="nav flex-column mb-2 text-uppercase">
<li class="active nav-item">
<a class="nav-link text-light rounded-sm" href="#general-settings">
<i class="fa fa-cog fa-lg pe-2" aria-hidden="true"></i>
<i class="fa fa-ellipsis-h fa-lg pe-2" aria-hidden="true"></i>
<span class="menu-item" data-i18n="optionsMenuGeneral"></span>
</a>
</li>
@ -49,7 +49,7 @@
</li>
<li class="nav-item">
<a class="nav-link text-light rounded-sm" href="#site-preferences">
<i class="fa fa-list-alt fa-lg pe-2" aria-hidden="true"></i>
<i class="fa fa-wrench fa-lg pe-2" aria-hidden="true"></i>
<span class="menu-item" data-i18n="optionsMenuSitePreferences"></span>
</a>
</li>
@ -81,12 +81,6 @@
</div>
<div class="card-body">
<!-- Version warning -->
<div class="alert alert-warning mt-3 col-lg-9" role="alert" id="minimumVersionAlert" style="display: none;">
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
<span data-i18n="optionsMinimumKeePassXCVersionRequired" data-i18n-placeholder="2.6.0"></span>
</div>
<!-- Color theme -->
<div class="form-group col-sm-3 pb-2">
<label for="colorTheme" class="form-label" data-i18n="optionsThemeSelectionHeader"></label>
@ -160,7 +154,7 @@
</div>
<!-- Keyboard shortcuts -->
<div class="card my-4 shadow" id="keyboardShortcuts">
<div class="card my-4 shadow">
<div class="card-header h6 rounded-0">
<i class="fa fa-keyboard-o" aria-hidden="true"></i>
<span data-i18n="optionsKeyboardShortcutsHeader"></span>
@ -266,7 +260,7 @@
<input class="form-check-input" type="checkbox" name="autoFillSingleEntry" id="autoFillSingleEntry" value="true">
<label class="form-check-label" for="autoFillSingleEntry" data-i18n="optionsCheckboxAutoFillSingleEntry"></label>
<div class="form-text" data-i18n="optionsAutoFillSingleEntryHelpText"></div>
<div class="alert alert-warning mt-3 col-lg-9" role="alert">
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
<span data-i18n="optionsAutoFillSingleEntryWarning"></span>
@ -284,7 +278,7 @@
</div>
<!-- Autofill HTTP Auth dialogs -->
<div class="form-group pb-1" id="autoFillHttpAuth">
<div class="form-group pb-1">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" name="autoFillAndSend" id="autoFillAndSend" value="true">
<label class="form-check-label" for="autoFillAndSend" data-i18n="optionsCheckboxAutoFillAndSend"></label>
@ -358,8 +352,8 @@
<!-- Number of allowed redirects -->
<div class="form-group w-50 pb-2">
<label id="redirectAllowanceLabel" class="font-weight-normal" for="redirectAllowance" data-i18n="optionsRedirectAllowance" data-i18n-placeholder="3"></label>
<input type="range" class="form-range" id="redirectAllowance" name="redirectAllowance" min="1" max="11" step="1" value="3">
<label id="redirectAllowanceLabel" class="font-weight-normal" for="redirectAllowance" data-i18n="optionsRedirectAllowance" data-i18n-placeholder="1"></label>
<input type="range" class="form-range" id="redirectAllowance" name="redirectAllowance" min="1" max="11" step="1" value="1">
<div class="form-text help-text" data-i18n="optionsRedirectAllowanceHelpText"></div>
</div>
@ -565,10 +559,6 @@
<i class="fa fa-arrow-up" aria-hidden="true"></i>
<span data-i18n="optionsButtonExport"></span>
</button>
<button class="btn btn-sm btn-danger ms-2" id="resetSettingsButton" data-i18n="[title]optionsButtonResetSettings">
<i class="fa fa-undo" aria-hidden="true"></i>
<span data-i18n="optionsButtonResetSettings"></span>
</button>
</div>
</div>
</div>
@ -596,30 +586,6 @@
</div>
</div>
</div>
<div id="dialogResetSettings" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="myModalLabel" data-i18n="optionsButtonResetSettings"></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p data-i18n="optionsResetSettingsDialogText"></p>
</div>
<div class="modal-footer">
<button class="btn btn-sm btn-secondary" data-bs-dismiss="modal" aria-hidden="true">
<i class="fa fa-remove" aria-hidden="true"></i>
<span data-i18n="optionsButtonCancel"></span>
</button>
<button class="btn btn-sm yes btn-danger">
<i class="fa fa-check" aria-hidden="true"></i>
<span data-i18n="optionsButtonReset"></span>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Connected Databases -->
@ -795,14 +761,16 @@
<thead>
<tr>
<th scope="col"><span data-i18n="optionsColumnPageURL"></span></th>
<th scope="col"></th>
<th scope="col"><span data-i18n="optionsColumnFeatures"></span></th>
<th scope="col"><span data-i18n="optionsColumnIgnore"></span></th>
<th scope="col"><span data-i18n="optionsColumnUsernameOnly"></span></th>
<th scope="col"><span data-i18n="optionsColumnImprovedInputFieldDetection"></span></th>
<th scope="col"><span data-i18n="optionsColumnAllowIframes"></span></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr class="empty">
<td colspan="4"><span data-i18n="optionsSitePreferencesNotFound"></span></td>
<td colspan="5"><span data-i18n="optionsSitePreferencesNotFound"></span></td>
</tr>
<tr class="clone d-none">
<td class="text-nowrap">
@ -822,20 +790,17 @@
</button>
</div>
</td>
<td class="text-nowrap">
<button type="button" class="btn btn-sm btn-primary container-fluid" id="settings-button">
<span class="non-clickable" data-i18n="optionsSitePreferencesSettings"></span>
</button>
</td>
<td>
<select class="form-select form-select-sm" id="ignore-select" name="ignore" data-i18n="[title]optionsSitePreferencesSelect">
<td>
<select class="form-select form-select-sm" name="ignore" data-i18n="[title]optionsSitePreferencesSelect">
<option value="ignoreNothing" data-i18n="optionsSelectionNothing"></option>
<option value="ignoreNormal" data-i18n="optionsSelectionNormal"></option>
<option value="ignoreAutoSubmit" data-i18n="optionsSelectionAutoSubmit"></option>
<option value="ignorePasskeys" data-i18n="optionsSelectionPasskeys"></option>
<option value="ignoreFull" data-i18n="optionsSelectionFull"></option>
</select>
</td>
<td><input class="form-check-input" type="checkbox" name="usernameOnly" value="false" data-i18n="[title]optionsColumnUsernameOnly"></td>
<td><input class="form-check-input" type="checkbox" name="improvedFieldDetection" value="false" data-i18n="[title]optionsColumnImprovedInputFieldDetection"></td>
<td><input class="form-check-input" type="checkbox" name="allowIframes" value="false" data-i18n="[title]optionsColumnAllowIframes"></td>
<td class="text-nowrap">
<button class="btn btn-sm delete btn-danger" data-i18n="[title]removeSitePreferencesButtonTitle">
<i class="fa fa-remove" aria-hidden="true"></i>
@ -873,32 +838,6 @@
</div>
</div>
</div>
<!-- Settings dropdown -->
<div class="card settings-dropdown" style="display: none;">
<div class="card-body settings-dropdown-body">
<!-- Username only detection -->
<div class="form-check">
<input class="form-check-input" type="checkbox" id="usernameOnly" name="usernameOnly" value="false" data-i18n="[title]optionsColumnUsernameOnly">
<label class="form-check-label" for="usernameOnly" data-i18n="optionsColumnUsernameOnly"></label>
</div>
<!-- Improved input field detection -->
<div class="form-check">
<input class="form-check-input" type="checkbox" id="improvedFieldDetection" name="improvedFieldDetection" value="false" data-i18n="[title]optionsColumnImprovedInputFieldDetection">
<label class="form-check-label" for="improvedFieldDetection" data-i18n="optionsColumnImprovedInputFieldDetection"></label>
</div>
<!-- Allow iFrames -->
<div class="form-check">
<input class="form-check-input" type="checkbox" id="allowIframes" name="allowIframes" value="false" data-i18n="[title]optionsColumnAllowIframes">
<label class="form-check-label" for="allowIframes" data-i18n="optionsColumnAllowIframes"></label>
</div>
<!-- Add new options below -->
<!--<hr class="dropdown-divider">-->
</div>
</div>
</div>
<!-- About -->

View file

@ -1,8 +1,6 @@
'use strict';
const options = {};
options.dropdownButton = null;
options.isFirefox = false;
const $ = function(elem) {
return document.querySelector(elem);
@ -208,7 +206,7 @@ options.initGeneralSettings = async function() {
});
$('#configureCommands').addEventListener('click', function() {
if (options.isFirefox) {
if (isFirefox()) {
if (typeof(browser.commands.openShortcutSettings) === 'function') {
browser.commands.openShortcutSettings();
} else {
@ -287,7 +285,7 @@ options.initGeneralSettings = async function() {
// Verify the import
temporarySettings = contents;
dialogImportSettingsModal.show();
} catch (_err) {
} catch (err) {
console.log('Error loading JSON settings file.');
}
};
@ -313,24 +311,6 @@ options.initGeneralSettings = async function() {
}
});
// Reset all settings modal
const dialogResetSettingsModal = new bootstrap.Modal('#dialogResetSettings',
{ keyboard: true, focus: false, backdrop: true });
$('#dialogResetSettings').addEventListener('shown.bs.modal', function(modalEvent) {
modalEvent.currentTarget.querySelector('.modal-footer button.yes').focus();
});
$('#resetSettingsButton').addEventListener('click', function() {
dialogResetSettingsModal.show();
});
$('#dialogResetSettings .modal-footer button.yes').addEventListener('click', function(e) {
dialogResetSettingsModal.hide();
browser.runtime.sendMessage({ action: 'reset_all_settings' });
location.reload();
});
$('#copyVersionToClipboard').addEventListener('click', function () {
const copyText = document.getElementById('versionInfo').innerText;
navigator.clipboard.writeText(copyText);
@ -366,23 +346,38 @@ options.showKeePassXCVersions = async function(response) {
$('#tab-about span.kpxcVersion').textContent = response.current;
$('#tab-general-settings button.checkUpdateKeePassXC').disabled = false;
const featureList = await browser.runtime.sendMessage({ action: 'get_features_list' });
if (featureList?.requiredKeePassXCVersionFound) {
const versionResults = await browser.runtime.sendMessage({
action: 'compare_versions',
args: [
[
'2.6.0',
'2.7.0',
'2.7.7',
'2.7.10'
],
response.current
],
});
// Hide/disable certain options with older KeePassXC versions than 2.6.0
if (versionResults['2.6.0']) {
$('#tab-general-settings #versionRequiredAlert').hide();
} else {
$('#tab-general-settings #showGroupNameInAutocomplete').disabled = true;
$('#tab-general-settings #minimumVersionAlert').show();
}
if (!featureList?.downloadFaviconAfterSave) {
// Hide certain options with older KeePassXC versions than 2.7.0
if (!versionResults['2.7.0']) {
$('#tab-general-settings #downloadFaviconAfterSaveFormGroup').hide();
}
if (!featureList?.passkeys) {
// Hide certain options with older KeePassXC versions than 2.7.7
if (!versionResults['2.7.7']) {
$('#tab-general-settings #passkeysOptionsCard').hide();
}
if (!featureList?.passkeysDefaultGroup) {
// Hide passkeys default group option with KeePassXC version < 2.7.10
if (!versionResults['2.7.10']) {
$('#tab-general-settings #passkeysDefaultGroup').hide();
}
};
@ -541,43 +536,6 @@ options.initSitePreferences = function() {
modalEvent.currentTarget.querySelector('.modal-footer button.yes').focus();
});
const settingsButtonClicked = function(e) {
e.preventDefault();
const closestTr = e.target.closest('tr');
const url = closestTr.getAttribute('url');
const sitePreferences = options.settings['sitePreferences']?.find((pref) => pref?.url === url);
const usernameOnly = sitePreferences.usernameOnly;
const improvedFieldDetection = sitePreferences.improvedFieldDetection;
const allowIframes = sitePreferences.allowIframes;
const dropdown = $('.settings-dropdown');
if (dropdown?.style?.display !== 'block'
|| (dropdown?.style?.display === 'block' && e.target !== options.dropdownButton)) {
if (options.dropdownButton) {
options.dropdownButton.classList.remove('active');
// Update number of enabled settings to the old Settings button
const checkboxValues = Array.from(dropdown?.querySelectorAll('input[type=checkbox]')).map(c => c.checked);
updateSettingsButtonText(options.dropdownButton, checkboxValues);
}
// Apply current settings to the dropdown
dropdown.querySelector('#usernameOnly').checked = usernameOnly;
dropdown.querySelector('#improvedFieldDetection').checked = improvedFieldDetection;
dropdown.querySelector('#allowIframes').checked = allowIframes;
dropdown?.show();
updateDropdownPosition(e, dropdown);
options.dropdownButton = e.target;
options.dropdownButton.classList.add('active');
} else {
dropdown?.hide();
options.dropdownButton.classList.remove('active');
updateSettingsButtonText(options.dropdownButton, [ usernameOnly, improvedFieldDetection, allowIframes ]);
options.dropdownButton = null;
}
};
const removeButtonClicked = function(e) {
e.preventDefault();
@ -643,18 +601,18 @@ options.initSitePreferences = function() {
}
};
const checkboxClicked = async function(e) {
const closestTr = options?.dropdownButton?.closest('tr');
const checkboxClicked = function() {
const closestTr = this.closest('tr');
const url = closestTr.getAttribute('url');
for (const site of options.settings['sitePreferences']) {
if (site.url === url) {
if (e.target.name === SitePreferences.USERNAME_ONLY) {
site.usernameOnly = e.target.checked;
} else if (e.target.name === SitePreferences.IMPROVED_FIELD_DETECTION) {
site.improvedFieldDetection = e.target.checked;
} else if (e.target.name === SitePreferences.ALLOW_IFRAMES) {
site.allowIframes = e.target.checked;
if (this.name === 'usernameOnly') {
site.usernameOnly = this.checked;
} else if (this.name === 'improvedFieldDetection') {
site.improvedFieldDetection = this.checked;
} else if (this.name === 'allowIframes') {
site.allowIframes = this.checked;
}
}
}
@ -675,11 +633,6 @@ options.initSitePreferences = function() {
options.saveSettings();
};
const dropdown = $('.settings-dropdown');
dropdown.querySelector('#usernameOnly').addEventListener('change', checkboxClicked);
dropdown.querySelector('#improvedFieldDetection').addEventListener('change',checkboxClicked);
dropdown.querySelector('#allowIframes').addEventListener('change', checkboxClicked);
const addNewRow = function(rowClone, newIndex, url, ignore, usernameOnly, improvedFieldDetection, allowIframes) {
const row = rowClone.cloneNode(true);
row.setAttribute('url', url);
@ -709,25 +662,19 @@ options.initSitePreferences = function() {
saveModifiedUrl(e, row, inputField, editButton, cancelButton, saveButton)
);
// Page URL
row.children[0].children[0].children[0].value = url;
row.children[0].children[0]?.addEventListener('dblclick', (e) =>
row.children[0].children[0]?.addEventListener('dblclick', (e) =>
enterEditMode(e, row, inputField, editButton, cancelButton, saveButton)
);
// Settings
const settings = row.children[1];
updateSettingsButtonText(settings.querySelector('#settings-button'),
[ usernameOnly, improvedFieldDetection, allowIframes ]);
settings.querySelector('#settings-button').addEventListener('click', (e) => settingsButtonClicked(e));
// Ignore
const ignoreSelect = row.children[2];
ignoreSelect.querySelector('#ignore-select').value = ignore;
ignoreSelect.querySelector('#ignore-select').addEventListener('change', selectionChanged);
// Remove button
row.children[3].addEventListener('click', removeButtonClicked);
row.children[1].children[0].value = ignore;
row.children[1].children[0].addEventListener('change', selectionChanged);
row.children[2].children['usernameOnly'].checked = usernameOnly;
row.children[2].children['usernameOnly'].addEventListener('change', checkboxClicked);
row.children[3].children['improvedFieldDetection'].checked = improvedFieldDetection;
row.children[3].children['improvedFieldDetection'].addEventListener('change', checkboxClicked);
row.children[4].children['allowIframes'].checked = allowIframes;
row.children[4].children['allowIframes'].addEventListener('change', checkboxClicked);
row.children[5].addEventListener('click', removeButtonClicked);
$('#tab-site-preferences table tbody').append(row);
};
@ -791,11 +738,11 @@ options.initSitePreferences = function() {
$('#tab-site-preferences table tbody tr.empty').hide();
options.settings['sitePreferences'].push({
allowIframes: false,
ignore: IGNORE_NOTHING,
improvedFieldDetection: false,
url: value,
ignore: IGNORE_NOTHING,
usernameOnly: false,
improvedFieldDetection: false,
allowIframes: false,
});
options.saveSettings();
manualUrl.value = '';
@ -830,7 +777,7 @@ options.initAbout = function() {
$('#tab-about span.versionCIP').textContent = version;
$('#tab-about span.kpxcbrVersion').textContent = version;
$('#tab-about span.kpxcbrOS').textContent = platform;
$('#tab-about span.kpxcbrBrowser').textContent = getBrowserId(navigator.userAgent);
$('#tab-about span.kpxcbrBrowser').textContent = getBrowserId();
};
options.updateTheme = function() {
@ -858,110 +805,24 @@ options.createWarning = function(elem, text) {
}, 5000);
};
options.hideUnsupportedFeatures = function() {
if (isSafari()) {
$('#tab-general-settings div#keyboardShortcuts').hide();
$('#tab-general-settings div#autoFillHttpAuth').hide();
}
};
const getBrowserId = function(userAgent) {
const browserQueries = [
{ findStr: 'Firefox', name: 'Mozilla Firefox' },
{ findStr: 'Edg', name: 'Microsoft Edge' },
{ findStr: 'OPR', name: 'Opera' },
{ findStr: 'Chrome', name: 'Chrome/Chromium' },
{ findStr: 'Version/', name: 'Safari' }
];
const getVersion = (agent, findStr) => {
const match = agent?.match(new RegExp(`(?:${findStr})\/([\\d.]+)`));
return match ? match[1] : 'Unknown version';
};
for (const query of browserQueries) {
if (userAgent?.indexOf(query.findStr) > -1) {
return `${query.name} ${getVersion(userAgent, query.findStr)}`;
}
const getBrowserId = function() {
if (navigator.userAgent.indexOf('Firefox') > -1) {
return 'Mozilla Firefox ' + navigator.userAgent.substr(navigator.userAgent.lastIndexOf('/') + 1);
} else if (navigator.userAgent.indexOf('Edg') > -1) {
let startPos = navigator.userAgent.indexOf('Edg');
startPos = navigator.userAgent.indexOf('/', startPos) + 1;
const version = navigator.userAgent.substring(startPos);
return 'Microsoft Edge ' + version;
} else if (navigator.userAgent.indexOf('Chrome') > -1) {
let startPos = navigator.userAgent.indexOf('Chrome');
startPos = navigator.userAgent.indexOf('/', startPos) + 1;
const version = navigator.userAgent.substring(startPos, navigator.userAgent.indexOf('Safari'));
return 'Chrome/Chromium ' + version;
}
return 'Other/Unknown';
};
// Update the number of enabled settings to the button text
const updateSettingsButtonText = function(buttonElement, enabledOptions = []) {
const numberOfEnabledOptions = enabledOptions.filter(o => o === true).length;
const buttonText = buttonElement.querySelector('span');
if (numberOfEnabledOptions > 0) {
buttonText.textContent =
`${browser.i18n.getMessage('optionsSitePreferencesSettings')} (${numberOfEnabledOptions})`;
} else {
buttonText.textContent = browser.i18n.getMessage('optionsSitePreferencesSettings');
}
};
// Updates settings dropdown menu position
const updateDropdownPosition = function(e, dropdown) {
if (!dropdown) {
dropdown = $('.settings-dropdown');
}
const settingsButton = e?.target ?? options.dropdownButton;
const rect = settingsButton?.getClientRects()?.[0];
if (!rect) {
return;
}
const zoom = getComputedStyle(document.body).zoom || 1;
const scrollTop = document.defaultView.scrollY / zoom;
const scrollLeft = document.defaultView?.scrollX / zoom;
// If dropdown does not fit to the bottom of the screen -> show it at the top of the settings button
const dropdownRect = dropdown.getBoundingClientRect();
const totalHeight = dropdownRect.height + rect.height;
const offset = (totalHeight + rect.y) / zoom > window.self.visualViewport.height ? totalHeight / zoom : 0;
dropdown.style.left = Pixels(rect.left / zoom + scrollLeft);
dropdown.style.top = Pixels(rect.bottom / zoom + scrollTop - offset);
};
// Hides the settings dropdown when clicked outside of it
document.addEventListener('mouseup', function(e) {
if (!e.isTrusted) {
return;
}
const dropdown = $('.settings-dropdown');
if (dropdown?.style?.display !== 'block') {
return;
}
const rect = dropdown?.getClientRects()?.[0];
if (!rect) {
return;
}
if ((e.x > rect.right || e.x < rect.x) || (e.y > rect.bottom || e.y < rect.y)
&& e.target.nodeName !== 'BUTTON') {
dropdown?.hide();
const checkboxValues = Array.from(dropdown?.querySelectorAll('input[type=checkbox]')).map(c => c.checked);
updateSettingsButtonText(options.dropdownButton, checkboxValues);
options.dropdownButton.classList.remove('active');
options.dropdownButton = null;
}
});
// Handle dropdown position on window resize
window.addEventListener('resize', function() {
updateDropdownPosition();
});
// Handle dropdown position on scroll
window.addEventListener('scroll', function() {
updateDropdownPosition();
});
(async() => {
try {
// We eagerly load the theme here to avoid a white flash
@ -976,20 +837,12 @@ window.addEventListener('scroll', function() {
const keyRing = await browser.runtime.sendMessage({ action: 'load_keyring' });
options.keyRing = keyRing;
options.isFirefox = isFirefox();
options.initMenu();
await options.initGeneralSettings();
options.initConnectedDatabases();
options.initCustomLoginFields();
options.initSitePreferences();
options.initAbout();
options.hideUnsupportedFeatures();
// The form-switch transitions should complete in 150 ms
setTimeout(() => {
document.body.classList.remove('no-transitions');
}, 200);
} catch (err) {
console.log('Error loading options page: ' + err);
}

View file

@ -153,15 +153,6 @@ code {
width: 2.5rem;
}
#choose-custom-login-fields-button-safari {
background-image: url('safari-web-extension://__MSG_@@extension_id__/icons/custom_login_fields.svg');
background-position: center;
background-repeat: no-repeat;
background-size: 70%;
height: 31px;
width: 2.5rem;
}
#lock-database-button {
display: none;
width: 2.5rem;

View file

@ -87,7 +87,7 @@ const sendMessageToTab = async function(message) {
});
// This does not work with Firefox because of https://bugzilla.mozilla.org/show_bug.cgi?id=1665380
await sendMessageToTab('retrieve_credentials_forced');
await sendMessageToTab('retrive_credentials_forced');
close();
});

View file

@ -16,7 +16,9 @@ async function initSettings() {
});
const customLoginFieldsButton = document.body.querySelector('#settings #choose-custom-login-fields-button');
customLoginFieldsButton.id = getIconClass('choose-custom-login-fields-button');
if (isFirefox()) {
customLoginFieldsButton.id = 'choose-custom-login-fields-button-moz';
}
customLoginFieldsButton.addEventListener('click', async () => {
const tab = await getCurrentTab();

1185
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,12 @@
{
"name": "keepassxc-browser",
"version": "1.10.0.1",
"version": "1.9.9.3",
"description": "KeePassXC-Browser",
"main": "build.js",
"devDependencies": {
"@eslint/js": "^9.39.1",
"@playwright/test": "^1.56.1",
"@playwright/test": "^1.45.1",
"@types/node": "^20.14.10",
"eslint": "^9.39.1"
"eslint": "^8.49.0"
},
"dependencies": {
"@npmcli/fs": "^2.1.0"
@ -16,7 +15,7 @@
"build": "node build.js",
"debug:chromium": "cp dist/manifest_chromium.json keepassxc-browser/manifest.json",
"debug:firefox": "cp dist/manifest_firefox.json keepassxc-browser/manifest.json",
"lint": "npx eslint --no-warn-ignored keepassxc-browser/**/*.js",
"lint": "npx eslint keepassxc-browser/**/*.js",
"tests": "npx playwright test"
},
"repository": {

8
tests/.eslintrc Normal file
View file

@ -0,0 +1,8 @@
{
"env": {
"node": true
},
"parserOptions": {
"sourceType": "module"
}
}

View file

@ -60,10 +60,6 @@ async function testTotpFields() {
[ '', { id: '2fa', type: 'text', maxLength: '6' }, 'Generic 2FA field', true ],
[ '', { id: '2fa', type: 'text', maxLength: '4' }, 'Ignore if field maxLength too small', false ],
[ '', { id: '2fa', type: 'text', maxLength: '12' }, 'Ignore if field maxLength too long', false ],
[ '', { id: 'promocode', type: 'text', }, 'promocode id is not acceptable', false ],
[ '', { id: 'Promotional Code', type: 'text', }, 'Promotional Code id is not acceptable', false ],
[ '', { id: 'postcode', type: 'text', }, 'postcode id is not acceptable', false ],
[ '', { id: 'postalcode', type: 'text', }, 'postalcode id is not acceptable', false ],
[ '', { id: 'encode', type: 'text', }, 'encode id is not acceptable', false ],
[ '', { id: 'encodedText', type: 'text', }, 'encodedText is not acceptable', false ],
[ '', { id: 'decoder', type: 'text', }, 'decoder id is not acceptable', false ],