Compare commits

...

108 commits

Author SHA1 Message Date
Sami Vänttinen
eab53c6a90
Update to 1.10.0.1 (#2895) 2026-03-08 10:03:31 +02:00
Sami Vänttinen
f1f4c1e0ff
Fix dynamic password input detection when replacing usename (#2896) 2026-03-08 09:25:45 +02:00
Sami Vänttinen
64fda92e27
Remove webRequestBlocking permission from V3 manifests (#2893) 2026-03-06 14:57:41 +02:00
Sami Vänttinen
dd9d3d0b47
Update to 1.10.0 (#2891) 2026-03-04 15:27:34 +02:00
dependabot[bot]
b8e382d79d
Bump minimatch from 3.1.2 to 3.1.5 (#2888)
Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.1.2 to 3.1.5.
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.5)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-version: 3.1.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-03 16:02:42 +02:00
Sami Vänttinen
02a3e441ba
Fix createObserver() check (#2873) 2026-03-03 06:48:32 +02:00
Sami Vänttinen
b01af22fd1
Fix detecting new password input in form (#2878) 2026-03-01 13:59:23 +02:00
Sami Vänttinen
a087c8dae0
Ignore password buttons on form submit button detection (#2882) 2026-03-01 08:41:53 +02:00
Sami Vänttinen
589b14a7cc
Improve retry on input field detection with Custom Login Fields (#2875) 2026-03-01 08:32:34 +02:00
Sami Vänttinen
bf0969aefe
Add support for Related Origin Requests with passkeys (#2828) 2026-02-16 18:08:26 +02:00
Sami Vänttinen
8d4e46882d
Preliminary support for Safari (#2800) 2026-02-16 18:07:49 +02:00
Sami Vänttinen
9d254d1ffe
Fix/epicgames and paypal password inputs (#2863)
Fix Epicgames, Paypal and Reddit password input detection
2026-02-16 08:21:19 +02:00
Sami Vänttinen
89cc1bb40d
Fix Google password input (#2861) 2026-02-07 06:58:44 +02:00
Sami Vänttinen
6414dd24b7
Fix detecting transitions (#2855) 2026-02-05 11:02:56 +02:00
Sami Vänttinen
497bdd4b8a
Add support for features list (#2848) 2026-02-05 11:02:31 +02:00
Stefan Sundin
58beb79927
Hide the form-switch animation when the options page loads the initial values (#2807)
Hide the form-switch transition/animation when the options page initializes the current values.
2026-02-05 11:01:57 +02:00
Aleksandr Kolbasov
8b71dbc30f
Fix the ability to fake same-origin in passkeys (#2849) 2026-02-05 10:58:48 +02:00
Jakob Hostnik
f66bf40287
Add 'mfaCode' to accepted token fields (#2804) 2026-02-05 10:57:24 +02:00
Sami Vänttinen
b46c1a1199
Fix TOTP field detection with Microsoft sites (#2832) 2026-01-18 20:35:38 +02:00
Sami Vänttinen
40c56d913d
Update webextension-polyfill library (#2822) 2026-01-07 20:45:15 +02:00
Sami Vänttinen
49b7ec72c7
Ignore partial nodeNames (#2745) 2026-01-04 14:07:39 +02:00
Sami Vänttinen
f522e819a5
Fix incorrect error message on keyboard fill (#2814) 2026-01-04 13:47:14 +02:00
Aleksandr Kolbasov
1dc3dcd38c
"Reopen database" shortcut (#2767)
"Reopen database" shortcut
2026-01-04 09:26:53 +02:00
Sami Vänttinen
e444eab33c
Refactor icon handling (#2791)
Refactor icon handling
2025-12-16 09:08:17 +02:00
Sami Vänttinen
f3e0111acc
Show notifications always on the top window (#2789) 2025-12-12 16:41:48 +02:00
SinnySupernova
abba71e419
Set default redirect allowance to 3. fixes #2796 (#2797) 2025-12-12 16:41:06 +02:00
Céleste Wouters
5dcc3c93d0
Fix TOTP fill missing digits on some sites (fixes #2215) (#2794)
Fixes Authelia and Epic Games OTP fill by addressing two underlying problems
2025-12-12 16:30:56 +02:00
Jack Dunn
c639ce8742
Ignore "postcode" for TOTP (#2793)
Ignore "postcode" for totp
2025-12-12 16:29:55 +02:00
Sami Vänttinen
fdbd077852
Passkeys: Add publicKey to response (#2782)
Passkeys: Add publicKey to response
2025-11-29 16:33:02 +02:00
varjolintu
43d0dc528f Fix using invalid parameters with MutationObserver 2025-11-29 16:23:05 +02:00
varjolintu
74dbb0d8e8 Update to 1.9.11 2025-11-26 06:48:36 +02:00
varjolintu
8c3df94db3 Fix layout issues with banners 2025-11-17 17:28:32 +02:00
varjolintu
8bc00cf194 Add the new selector for submit button 2025-11-17 17:28:07 +02:00
Sami Vänttinen
b1b6581d50
Upgrade eslint to latest (#2763)
Update ESLint to the latest version, and migrate to new configuration file
2025-11-17 11:34:52 +02:00
Sami Vänttinen
9591aa27ef
Merge pull request #2760 from keepassxreboot/fix/add_exception_to_dei_gr
Add exception for dei.gr
2025-11-15 18:06:09 +02:00
varjolintu
064ce12635 Add exception for dei.gr 2025-11-15 18:05:36 +02:00
Sami Vänttinen
a429b6b717
Merge pull request #2759 from keepassxreboot/fix/add_site_exception_for_dubverse
Add submit button site exception for Dubverse
2025-11-15 18:04:50 +02:00
varjolintu
83b7e33fb8 Add submit button site exception for Dubverse 2025-11-15 09:23:05 +02:00
Sami Vänttinen
901e58c72b
Merge pull request #2757 from a2kolbasov/passkeys/hide-injected-script
Remove injected passkeys script from DOM tree
2025-11-12 21:54:53 +02:00
Sami Vänttinen
eec4bb1873
Add CONTRIBUTING markdown document (#2755)
Add CONTRIBUTING markdown document
2025-11-12 14:03:18 +02:00
Aleksandr Kolbasov
37dca875a0 Remove injected passkeys script from DOM tree
It helps to hide it from `document.scripts`
2025-11-11 22:28:43 +03:00
Sami Vänttinen
2fb4b371e6
Merge pull request #2740 from keepassxreboot/fix/incorrect_fields_variable
Fix incorrect fields variable
2025-11-02 13:33:42 +02:00
varjolintu
693f48d588 Fix incorrect fields variable 2025-11-02 10:20:53 +02:00
Sami Vänttinen
9a32e8c0c4
Requery form if password input has changed (#2738)
Requery form if password input has changed
2025-11-01 15:27:44 +02:00
Sami Vänttinen
caf7c04a89
Bump playwright and @playwright/test (#2727)
Bumps [playwright](https://github.com/microsoft/playwright) to 1.56.1 and updates ancestor dependency [@playwright/test](https://github.com/microsoft/playwright). These dependencies need to be updated together.


Updates `playwright` from 1.45.1 to 1.56.1
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.45.1...v1.56.1)

Updates `@playwright/test` from 1.45.1 to 1.56.1
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.45.1...v1.56.1)

---
updated-dependencies:
- dependency-name: playwright
  dependency-version: 1.56.1
  dependency-type: indirect
- dependency-name: "@playwright/test"
  dependency-version: 1.56.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-29 18:17:55 +02:00
Sami Vänttinen
7e0eba463b
Implement toJSON() for createPublicKeyCredential (#2734)
Implement toJSON() for cratePublicKeyCredential
2025-10-26 16:02:30 +02:00
Sami Vänttinen
20f1a99356
Check overlays before fill (#2733)
Update and check overlays before fill
2025-10-26 15:14:11 +02:00
Sami Vänttinen
87b949a008
Merge pull request #2731 from keepassxreboot/fix/label_overlaps
Fix overlap checks with labels
2025-10-26 11:03:44 +02:00
varjolintu
bebf47783b Fix overlap checks with labels 2025-10-23 21:47:02 +03:00
dependabot[bot]
fb102cf3f5
Bump playwright and @playwright/test
Bumps [playwright](https://github.com/microsoft/playwright) to 1.56.1 and updates ancestor dependency [@playwright/test](https://github.com/microsoft/playwright). These dependencies need to be updated together.


Updates `playwright` from 1.45.1 to 1.56.1
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.45.1...v1.56.1)

Updates `@playwright/test` from 1.45.1 to 1.56.1
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.45.1...v1.56.1)

---
updated-dependencies:
- dependency-name: playwright
  dependency-version: 1.56.1
  dependency-type: indirect
- dependency-name: "@playwright/test"
  dependency-version: 1.56.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-20 18:59:02 +00:00
Sami Vänttinen
bb1da8c64d
Fix isFirefox() part 2 (#2725) 2025-10-12 13:32:27 +03:00
varjolintu
c0a587f89d Fix isFirefox() part 2 2025-10-12 09:17:34 +03:00
Sami Vänttinen
8155f5c5a0
Fix isFirefox() check (#2724)
Fix isFirefox() check
2025-10-12 08:23:56 +03:00
Sami Vänttinen
5ae33f0741
Merge pull request #2720 from keepassxreboot/feature/reset_all_settings
Add support for reset all settings
2025-10-07 17:15:52 +03:00
Sami Vänttinen
5b9c6aaf34
Merge pull request #2721 from ygoe/patch-1
Fix JavaScript error in console
2025-10-07 17:15:41 +03:00
Yves Goergen
443b715381
Fix JavaScript error in console
This extension causes an annoying log message that tells me about an error that was caught in the extension itself. It could easily be avoided in the first place by adding appropriate checks rather than running into the exception each time and happily telling me about it.

Error message:
Cannot override navigator.credentials:  TypeError: undefined is not a non-null object
in passkeys.js:211:17
    <anonymous> moz-extension://25853ed3-e2b0-47d6-ad17-2b172340068c/content/passkeys.js:204
    <anonymous> moz-extension://25853ed3-e2b0-47d6-ad17-2b172340068c/content/passkeys.js:213
2025-10-06 20:42:28 +02:00
varjolintu
ee6fc83d46 Add support for reset all settings 2025-10-06 20:03:34 +03:00
Sami Vänttinen
c2dd4cbb98
Merge pull request #2719 from keepassxreboot/update_to_1910
Update to 1.9.10
2025-10-05 21:57:35 +03:00
varjolintu
ec6cfdc101 Update to 1.9.10 2025-10-05 20:00:21 +03:00
Sami Vänttinen
3f41a8bdfc
Merge pull request #2698 from keepassxreboot/fix/handle_window_offsets
Handle window offsets for DOM content positioning
2025-10-05 15:51:23 +03:00
Sami Vänttinen
a7ae7533f3
Fix the logical error in isElementInside() (#2714) 2025-10-01 16:38:11 +03:00
varjolintu
1f184256dc Fix the logical error in isElementInside() 2025-10-01 07:09:25 +03:00
Eamon Nerbonne
f9147189bc
Update ignoreRegex to include promo codes (#2711)
Update ignoreRegex to include promo codes
2025-09-28 16:46:52 +03:00
Sami Vänttinen
b51f318dea
Refactor browser version information function (#2709)
Refactor browser version information function
2025-09-27 08:11:24 +03:00
Sami Vänttinen
2b69d31f32
Change settings icon (#2706)
Change General Settings and Site Preferences icons
2025-09-26 08:48:45 +03:00
varjolintu
17839fefe7
Handle window offsets for DOM content positioning 2025-09-24 23:34:01 -04:00
Sami Vänttinen
6fcd817e96
Fix filling wrapped input fields (#2699)
Fix filling Reddit 2FA field
2025-09-23 15:45:58 +03:00
Sami Vänttinen
a9a30e7625
Merge pull request #2700 from keepassxreboot/fix/dont_fill_username_to_password_field
Do not fill username to already filled password field
2025-09-23 07:22:49 +03:00
varjolintu
55cf8c05ad Do not fill username to already filled password field 2025-09-22 10:57:15 +03:00
Sami Vänttinen
ac35caaa35
Merge pull request #2694 from keepassxreboot/feature/add_disable_passkeys_for_site_option
Add Disable passkeys for Site Preferences features
2025-09-20 15:54:57 +03:00
Sami Vänttinen
84374d1d13
Merge pull request #2693 from keepassxreboot/feature/add_overlay_exceptions
Add overlay exceptions feature
2025-09-20 15:54:39 +03:00
Sami Vänttinen
10e5b91ece
Merge pull request #2691 from keepassxreboot/fix/fill_relevant_credential_entry
Fix filling relevant credential entries
2025-09-20 15:54:18 +03:00
varjolintu
b439017d0f Add Disable passkeys for Site Preferences features 2025-09-20 15:53:55 +03:00
varjolintu
d1f6f611a6 Add overlay exceptions feature 2025-09-20 10:09:06 +03:00
varjolintu
a610ff70bd Fix filling relevant credential entries 2025-09-15 20:24:01 +03:00
Sami Vänttinen
bbff228b40
Merge pull request #2690 from keepassxreboot/fix/popovers_with_firefox_esr
Fix using popovers with Firefox ESR 115
2025-09-15 18:11:29 +03:00
varjolintu
c71cff07b9 Fix using popovers with Firefox ESR 115 2025-09-15 17:29:49 +03:00
Sami Vänttinen
a6d11091d8
Refactor Site Preferences options (#2666)
Refactor Site Preferences options
2025-09-15 06:54:58 +03:00
Mihai Ionut Vilcu
39c86225e3
Update observer-helper.js to handle mutations with multiple elements (#2635)
Update observer-helper.js to handle mutations with multiple elements
2025-09-14 11:59:19 +03:00
Sami Vänttinen
e842e01cf3
Merge pull request #2687 from keepassxreboot/fix/overlay_elements
Use popovers for icons and Autocomplete Menu in DOM
2025-09-14 11:50:16 +03:00
varjolintu
a65694f8ca Use popovers for icons and Autocomplete Menu in DOM 2025-09-13 12:38:39 +03:00
Sami Vänttinen
5468c2058d
Merge pull request #2682 from keepassxreboot/fix/detect_existing_combination
Fix detecting existing combination
2025-09-10 21:26:59 +03:00
Sami Vänttinen
8042b71735
Update to 1.9.9.6 (#2683) 2025-09-09 19:16:33 +03:00
varjolintu
de22fa3d19 Update to 1.9.9.6 2025-09-09 18:47:20 +03:00
Jonathan White
362448eebc
Cache overlay discovery prior to checking top-level elements (#2670)
Cache overlay discovery prior to checking top-level elements
2025-09-09 18:41:17 +03:00
Sami Vänttinen
77a1f62979
Protect against form overlays (#2676)
Protect against form overlays
2025-09-06 14:34:39 +03:00
Sami Vänttinen
5f4133e8a3
Merge pull request #2677 from keepassxreboot/fix/check_element_in_treenode_walker
Fix using custom function with elements in TreeWalker
2025-09-04 16:35:00 +03:00
varjolintu
0aa321035c Fix using custom function witl elements in TreeWalker 2025-09-04 16:14:03 +03:00
Sami Vänttinen
1b547aca4d
Merge pull request #2675 from keepassxreboot/update_to_1995
Update to 1.9.9.5
2025-09-03 13:17:45 +03:00
varjolintu
57b3600f08 Update to 1.9.9.5 2025-09-03 12:01:19 +03:00
Sami Vänttinen
0bb0d97d2b
Merge pull request #2673 from keepassxreboot/fix/custom_login_fields_with_topmost_elem
Fix Custom Login Fields with topmost element check
2025-09-03 06:41:12 +03:00
varjolintu
99b5e31278 Fix Custom Login Fields with topmost element check 2025-09-02 22:27:53 +03:00
Sami Vänttinen
345b5e0bc9
Fix topmost input element check with labels (#2667)
Fix topmost input element check with labels
2025-09-02 15:14:48 +03:00
Sami Vänttinen
7f062de937
Merge pull request #2662 from keepassxreboot/update_to_1994
Update to 1.9.9.4
2025-08-29 13:30:35 +03:00
varjolintu
9f2d8972cf Update to 1.9.9.4 2025-08-29 08:13:36 +03:00
Sami Vänttinen
a4253447a1
Merge pull request #2659 from keepassxreboot/fix/topmost_element_shadow_dom
Fix topmost element check with Shadow DOM
2025-08-27 21:59:51 +03:00
varjolintu
f24c235692 Fix topmost element check with Shadow DOM 2025-08-27 17:12:03 +03:00
Sami Vänttinen
3e99239933
Merge pull request #2657 from keepassxreboot/varjolintu-patch-1
Update manifest.json
2025-08-27 13:34:40 +03:00
Sami Vänttinen
922e53de58
Update manifest.json
Fix missing version number update.
2025-08-27 11:09:24 +03:00
Sami Vänttinen
928004c035
Merge pull request #2655 from keepassxreboot/update_to_1993
Update to 1.9.9.3
2025-08-27 07:39:27 +03:00
varjolintu
9fbe0ebdea Update to 1.9.9.3 2025-08-27 06:46:22 +03:00
Sami Vänttinen
a1dc05ee74
Protect against overlays (#2651)
Protect against overlays
2025-08-27 06:13:57 +03:00
Sami Vänttinen
9f3a9ebc63
Merge pull request #2653 from keepassxreboot/fix/html_and_body_opacity
Clear credentials if html/body/form opacity limit is met
2025-08-26 13:34:05 +03:00
Sami Vänttinen
9ff98e8875
Merge pull request #2649 from keepassxreboot/fix/untrust-iframe
Add special error message for untrusted iframes
2025-08-26 07:37:00 +03:00
Sami Vänttinen
5d6c4834fd
Prevent style changes to wrapper divs (#2646)
Prevent style changes to wrapper divs
2025-08-24 07:47:54 +03:00
Jonathan White
6b360a9b14
Add special error message for untrusted iframes 2025-08-23 19:18:15 -04:00
Sami Vänttinen
991bdb8c95
Merge pull request #2648 from keepassxreboot/fix/dont_allow_sandboxed_iframes
Do not allow sandboxed iframes
2025-08-23 13:40:07 +03:00
varjolintu
780443b280 Do not allow sandboxed iframes 2025-08-23 08:59:29 +03:00
83 changed files with 5579 additions and 1813 deletions

195
.eslintrc
View file

@ -1,195 +0,0 @@
{
"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",
"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",
"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
}
}
]
}

196
.github/CONTRIBUTING.md vendored Normal file
View file

@ -0,0 +1,196 @@
# 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,3 +1,94 @@
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]
- Add protection against for sandboxed iframes [#2648]
- Add protection against overlays [#2651]
- Add protection against html, body and form opacity [#2653]
- Add error message for untrusted iframes during fill [#2649]
1.9.9.2 (2025-08-21)
=========================
- Fix parent element opacity check for better clickjacking attempt prevention [#2641]

View file

@ -1,15 +1,12 @@
# KeePassXC-Browser
Browser extension for [KeePassXC](https://keepassxc.org/) with [Native Messaging](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging).
Based on [pfn](https://github.com/pfn)'s [chromeIPass](https://github.com/pfn/passifox).
Some changes merged also from [smorks](https://github.com/smorks)' [KeePassHttp-Connector](https://github.com/smorks/keepasshttp-connector).
Browser extension for [KeePassXC](https://keepassxc.org/) with [native messaging](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging).
## Download and use
This browser extension was first supported in KeePassXC 2.3.0 (release end of 2017). In general it is advised to only use the latest available release.
Get the extension for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/) or [Chrome/Chromium](https://chromewebstore.google.com/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk) or [Microsoft Edge](https://microsoftedge.microsoft.com/addons/detail/pdffhmdngciaglkoonimfcmckehcpafo) (requires KeePassXC 2.5.3 or newer).
Get the extension for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/) or [Chrome/Chromium](https://chromewebstore.google.com/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk) or [Microsoft Edge](https://microsoftedge.microsoft.com/addons/detail/pdffhmdngciaglkoonimfcmckehcpafo).
Please see this [document](https://keepassxc.org/docs/KeePassXC_GettingStarted.html#_browser_integration) for instructions how to configure KeePassXC in order to connect the database correctly.
@ -52,6 +49,10 @@ 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.9.9.2",
"version_name": "1.9.9.2",
"minimum_chrome_version": "93",
"version": "1.10.0.1",
"version_name": "1.10.0.1",
"minimum_chrome_version": "124",
"description": "__MSG_extensionDescription__",
"author": "KeePassXC Team",
"icons": {
@ -53,7 +53,8 @@
"content/fields.js",
"content/fill.js",
"content/form.js",
"content/icons.js",
"content/icon.js",
"content/icon-handler.js",
"content/keepassxc-browser.js",
"content/observer-helper.js",
"content/pwgen.js",
@ -118,12 +119,15 @@
"choose_credential_fields": {
"description": "__MSG_popupChooseCredentialsText__"
},
"retrive_credentials_forced": {
"description": "__MSG_popupReopenButton__"
"retrieve_credentials_forced": {
"description": "__MSG_credentialsRetrieveButton__"
},
"request_autotype": {
"description": "__MSG_contextMenuRequestGlobalAutoType__"
},
"reopen_database": {
"description": "__MSG_popupReopenButton__"
},
"reload_extension": {
"description": "__MSG_popupReloadButton__"
}
@ -166,8 +170,7 @@
"tabs",
"webNavigation",
"webRequest",
"webRequestAuthProvider",
"webRequestBlocking"
"webRequestAuthProvider"
],
"optional_permissions": [
"privacy"

View file

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

216
eslint.config.mjs Normal file
View file

@ -0,0 +1,216 @@
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,9 +319,13 @@
"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."
@ -338,9 +342,13 @@
"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": "Също можете да използвате цифрите, за да избирате полета посредством клавиатурата.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Не са намерени данни за вход.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Попълването на регистрации е спряно в несигурна рамка.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "Повече от една регистрация е намерена в KeePassXC!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "Предпочитания за сайтове",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Настройки",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Относно",
"description": "About page header."
@ -726,6 +750,10 @@
"message": "Изнасяне на настройки",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Нулиране на настройките",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Група по подразбиране при добавяне на регистрации:",
"description": "Default group options text."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "Адрес на страница",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Пренебрегване",
"optionsColumnFeatures": {
"message": "Възможности",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"message": "Изключване на автоматично изпращане",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Изключване на Passkeys",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Изключване на всички функции",
"description": "Site Preferences option selection."

View file

@ -370,6 +370,10 @@
"message": "Chyba:\nNenalezena žádná uživatelská jména.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "Chyba:\nV KeePassXC nalezeno více než jedno uživateské jméno!\nKliknutím na ikonu KeePassXC-Browser zobrazíte další možnosti.",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."

View file

@ -319,9 +319,13 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "Strengfelt",
"message": "String Field",
"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."
@ -338,9 +342,13 @@
"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": "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."
"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": "Du kan også bruge nummeret til at vælge inputfelterne fra tastaturet.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Fandt ingen logins.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "Fandt mere end ét login i KeePassXC!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "Sideindstillinger",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Om",
"description": "About page header."
@ -726,6 +750,10 @@
"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."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "Sidens URL",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Ignorer",
"optionsColumnFeatures": {
"message": "Features",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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,6 +322,10 @@
"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."
@ -338,9 +342,13 @@
"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 <em>Zeichenkettenfelder</em> 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 Zeichenkettenfelder 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.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Keine Anmeldungen gefunden.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Ausfüllen der Anmeldedaten in nicht vertrauenswürdigen iframes blockiert.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "In KeePassXC wurde mehr als eine Anmeldung gefunden!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "Seiteneinstellungen",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Einstellungen",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Über",
"description": "About page header."
@ -726,6 +750,10 @@
"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."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "Seiten-URL",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Ignorieren",
"optionsColumnFeatures": {
"message": "Funktionen",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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,9 +319,13 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "Πεδίο String",
"message": "String Field",
"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."
@ -338,9 +342,13 @@
"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": "Επιβεβαιώστε την επιλογή σας ή επιλέξτε περισσότερα πεδία ως πεδία String.",
"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": "Μπορείτε επίσης να χρησιμοποιήσετε τους αριθμούς για να επιλέξετε τα πεδία εισαγωγής από το πληκτρολόγιο.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Δεν βρέθηκαν συνδέσεις.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "Περισσότερες από μία συνδέσεις βρέθηκαν στο KeePassXC!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "Site Preferences",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Σχετικά με",
"description": "About page header."
@ -726,6 +750,10 @@
"message": "Ρυθμίσεις εξαγωγής",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Reset all settings",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Προεπιλεγμένη ομάδα για την αποθήκευση νέων κωδικών πρόσβασης:",
"description": "Default group options text."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "Διεύθυνση URL σελίδας",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Αγνοήστε",
"optionsColumnFeatures": {
"message": "Features",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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,9 +319,13 @@
"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."
@ -338,9 +342,13 @@
"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.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "No logins found.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "More than one login was found in KeePassXC!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "Site Preferences",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "About",
"description": "About page header."
@ -726,6 +750,10 @@
"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."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "Page URL",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Ignore",
"optionsColumnFeatures": {
"message": "Features",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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,9 +319,13 @@
"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."
@ -338,9 +342,13 @@
"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.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "No logins found.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "More than one login was found in KeePassXC!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "Site Preferences",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "About",
"description": "About page header."
@ -726,6 +750,10 @@
"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."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "Page URL",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Ignore",
"optionsColumnFeatures": {
"message": "Features",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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,6 +322,10 @@
"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."
@ -338,9 +342,13 @@
"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.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "No se encontraron inicios de sesión.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Relleno de credenciales bloqueado en iframe que no es de confianza.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "¡Se encontró más de un inicio de sesión en KeePassXC!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"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."
@ -726,6 +750,10 @@
"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."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "URL de la página",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Ignorar",
"optionsColumnFeatures": {
"message": "Características",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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,9 +319,13 @@
"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."
@ -338,9 +342,13 @@
"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ä <em>lisämerkkijonokentiksi</em>.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
"message": "Vahvista valintasi tai merkitse lisää kenttiä lisämerkkijonokentiksi.",
"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.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Virhe:\nTunnuksia ei löydy.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Tietueiden täyttö estettiin epäluotettavaan iframe-upotukseen.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "Virhe:\nKeePassXC löysi useampia tilitietoja!\nPaina selainlaajennuksen ikonia nähdäksesi lisävalinnat.",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "Sivustokohtaiset asetukset",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Asetukset",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Tietoja",
"description": "About page header."
@ -726,6 +750,10 @@
"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."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "Sivun URL",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Älä huomioi",
"optionsColumnFeatures": {
"message": "Ominaisuudet",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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,9 +319,13 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "Champ de chaîne de caractères",
"message": "Champ de chaînes 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."
@ -338,9 +342,13 @@
"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.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Aucun identifiant na été trouvé.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "La saisie des identifiants a été bloquée par un cadre intégré non fiable",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "Plus dun identifiant a été trouvé dans KeePassXC",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"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."
@ -726,6 +750,10 @@
"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."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "URL de la page",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Ignorer",
"optionsColumnFeatures": {
"message": "Fonctions",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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,9 +319,13 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "שדה מחרוזת #",
"message": "String Field",
"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."
@ -338,9 +342,13 @@
"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": "נא לאשר את הבחירה או לבחור שדות נוספים כשדות מחרוזת.",
"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": "ניתן להשתמש גם במספרים לבחירת שדות הקלט מלוח המקשים.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "לא נמצאו התחברויות.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "נמצאה יותר מהתחברות אחת ל־KeePassXC!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "העדפות אתר",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "אודות",
"description": "About page header."
@ -726,6 +750,10 @@
"message": "ייצוא הגדרות",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Reset all settings",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "קבוצת ברירת מחדל לשמירת ססמאות חדשות:",
"description": "Default group options text."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "מען URL עמוד",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "להתעלם",
"optionsColumnFeatures": {
"message": "Features",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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": "Edit URL for this Site Preferences.",
"message": "URL-cím szerkesztése ezen webhely beállításai számára.",
"description": "Edit button title text on Site Preferences tab."
},
"allowIframeButtonTitle": {
@ -20,7 +20,7 @@
"description": "Connect button title text."
},
"copyDebugInfoButtonTitle": {
"message": "Copy debug info to clipboard",
"message": "Hibakeresési információk másolása a vágólapra",
"description": "Copy debug info button title text on About tab."
},
"dismissHttpAuthButtonTitle": {
@ -322,6 +322,10 @@
"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."
@ -338,9 +342,13 @@
"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.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Hiba:\nNem találhatók bejelentkezések.",
"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.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "Hiba:\nTöbb mint egy bejelentkezés található a KeePassXC programban!\nNyomja meg a KeePassXC-böngésző ikont a több lehetőségért.",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -627,11 +647,11 @@
"description": "Dismiss button text when in HTTP Authentication popup."
},
"optionsDefaultSettingsTitle": {
"message": "Default settings",
"message": "Alapértelmezett beállítások",
"description": "Default settings title for Getting Started page."
},
"optionsWelcomeTitle": {
"message": "KeePassXC-Browser",
"message": "KeePassXC-böngésző",
"description": "Main card title for Getting Started page."
},
"optionsTitle": {
@ -674,6 +694,10 @@
"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."
@ -699,7 +723,7 @@
"description": "Add button text."
},
"optionsButtonEdit": {
"message": "Edit",
"message": "Szerkesztés",
"description": "Edit button text."
},
"optionsButtonUpdate": {
@ -726,6 +750,10 @@
"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."
@ -783,7 +811,7 @@
"description": "Automatically fill-in single credential entry checkbox text."
},
"optionsCheckboxAutoFillRelevantCredential": {
"message": "Automatically fill in relevant credential entries",
"message": "Releváns hitelesítésiadat-bejegyzések automatikus kitöltése",
"description": "Automatically fill-in relevant credential entry checkbox text."
},
"optionsCheckboxAutoFillSingleTotp": {
@ -827,11 +855,11 @@
"description": "Show login notifications checkbox text."
},
"optionsDefaultPasswordManager": {
"message": "Set as default password manager",
"message": "Beállítás alapértelmezett jelszókezelőnek.",
"description": "Default password manager checkbox text."
},
"optionsDefaultPasswordManagerHelpText": {
"message": "Sets KeePassXC-Browser as the default password manager for the browser.",
"message": "A KeePassXC-böngészőt beállítja alapértelmezett jelszókezelőnek a böngészőben.",
"description": "Default password manager help text."
},
"optionsRedirectAllowance": {
@ -931,7 +959,7 @@
"description": "OTP field icon option help text."
},
"optionsHideIconsAlertText": {
"message": "You can also hide icons manually when needed with shift+click.",
"message": "Szükség esetén az ikonok manuálisan is elrejthetők Shift+kattintással.",
"description": "Hide icons alert text text."
},
"optionsAutoRetrieveCredentialsHelpText": {
@ -947,7 +975,7 @@
"description": "Auto-Fill Single Entry option help text."
},
"optionsAutoFillRelevantCredentialHelpText": {
"message": "Automatically fill in the relevant credential if a second fill is needed.",
"message": "Releváns hitelesítésiadat-bejegyzések automatikus kitöltése ha egy második kitöltés is szükséges.",
"description": "Auto-Fill Relevant Credential option help text."
},
"optionsAutoFillSingleEntryWarning": {
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "Oldal URL",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Mellőzés",
"optionsColumnFeatures": {
"message": "Képességek",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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."
@ -1395,31 +1435,31 @@
"description": "Lock database button title text."
},
"welcomeText": {
"message": "Welcome to KeePassXC-Browser!",
"message": "Üdvözöljük a KeePassXC-böngészőben!",
"description": "Main title of Getting Started page."
},
"documentationGettingStarted": {
"message": "Our Getting Started Guide will get you up and running quickly.",
"message": "A bevezető útmutatónk segítségével gyorsan elsajátíthatja a használatot.",
"description": "Getting Started document text."
},
"documentationUserGuide": {
"message": "Looking for more comprehensive documentation? Our User Guide is there to help.",
"message": "Átfogóbb dokumentációra lenne szüksége? A felhasználói útmutatónk segíthet.",
"description": "User Guide text."
},
"documentationTroubleshootingGuide": {
"message": "Need help troubleshooting the browser integration? Check the Troubleshooting Guide.",
"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.",
"description": "Troubleshooting Guide text."
},
"gettingStartedWelcomeText": {
"message": "Welcome to KeePassXC-Browser, the official browser extension for KeePassXC.",
"message": "Üdvözlet a KeePassXC-böngészőben, a KeePassXC hivatalos böngészőbővítményében.",
"description": "Welcome to KeePassXC-Browser, the official browser extension for KeePassXC."
},
"gettingStartedSecondWelcomeText": {
"message": "Please go through the default settings and check all your preferred options.",
"message": "Kérjük, nézze át az alapértelmezett beállításokat és ellenőrizze az összes kívánt opciót.",
"description": "Please go through the default settings and check all your preferred options."
},
"gettingStartedNewUser": {
"message": "Are you a new user? Check links to our documentation.",
"message": "Új felhasználó? Nézze meg a hivatkozott dokumentációt!",
"description": "Are you a new user? Check links to our documentation."
}
}

View file

@ -319,9 +319,13 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "Ruas lema",
"message": "String Field",
"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."
@ -338,9 +342,13 @@
"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": "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."
"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": "Anda juga dapat menggunakan angka untuk memilih bidang input dari keyboard.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Login tidak ditemukan.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "Lebih dari satu login ditemukan di KeePassXC!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "Site Preferences",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Tentang",
"description": "About page header."
@ -726,6 +750,10 @@
"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."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "Halaman URL",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Abaikan",
"optionsColumnFeatures": {
"message": "Features",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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 di debug negli appunti",
"message": "Copia informazioni 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 corrente non è connesso.",
"message": "Il database attuale non è connesso.",
"description": "Error notification shown when current database is not connected during action."
},
"passwordGeneratorErrorTooLong": {
@ -322,6 +322,10 @@
"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."
@ -338,9 +342,13 @@
"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 altri campi come campi stringa.",
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
"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."
},
"defineKeyboardText": {
"message": "Puoi anche usare i numeri per scegliere i campi di input dalla tastiera.",
@ -362,6 +370,10 @@
"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."
@ -370,12 +382,16 @@
"message": "Errore:\nNessun accesso trovato.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Riempimento delle credenziali bloccato in iframe non attendibile.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "In KeePassXC è stato trovato più di un accesso!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
},
"credentialsMultipleFoundSecondLine": {
"message": "Premi l'icona di KeePassXC-Browser per ulteriori opzioni.",
"message": "Per ulteriori opzioni seelziona l'icona di KeePassXC-Browser.",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
},
"credentialsNoUsernameFound": {
@ -390,6 +406,10 @@
"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."
@ -483,7 +503,7 @@
"description": "Checking status message in popup."
},
"popupNotConfigured": {
"message": "KeePassXC-Browser non è stato configurato. Premi il pulsante connetti per associarlo a KeePassXC.",
"message": "KeePassXC-Browser non è stato configurato. \nPer associarlo a KeePassXC seleziona il pulsante 'Connetti'.",
"description": "A popup message shown when the extension has not been connected to KeePassXC."
},
"popupNeedReconfigure": {
@ -491,7 +511,7 @@
"description": "A popup message shown when the extension has been disconnected from KeePassXC."
},
"popupNeedReconfigureMessage": {
"message": "Premi il pulsante riconnetti per stabilire una nuova connessione.",
"message": "Per stabilire una nuova connessione seleziona il pulsante 'Riconnetti'.",
"description": "A popup message shown when reconnect is needed."
},
"popupConfiguredNotAssociated": {
@ -531,7 +551,7 @@
"description": "A message shown choosing what credentials user wants to update."
},
"rememberChooseGroup": {
"message": "Scegli un gruppo per memorizzare le nuove credenziali.",
"message": "Per memorizzare le nuove credenziali scegli un gruppo.",
"description": "A message shown choosing what group user wants to use for new credentials."
},
"rememberInfoDefaultGroupNotFound": {
@ -674,6 +694,10 @@
"message": "Preferenze sito",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Impostazioni",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Info",
"description": "About page header."
@ -726,6 +750,10 @@
"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."
@ -735,7 +763,7 @@
"description": "Default group help text."
},
"optionsLabelDefaultPasskeyGroup": {
"message": "Gruppo predefinito per il salvataggio delle nuove passkeys:",
"message": "Gruppo predefinito salvataggio nuove passkey:",
"description": "Default passkey group options text."
},
"optionsLabelDefaultGroupCheckboxText": {
@ -827,11 +855,11 @@
"description": "Show login notifications checkbox text."
},
"optionsDefaultPasswordManager": {
"message": "Imposta come password manager di default",
"message": "Imposta come password manager predefinito",
"description": "Default password manager checkbox text."
},
"optionsDefaultPasswordManagerHelpText": {
"message": "Imposta KeePassXC-Browser come password manager di default per il browser.",
"message": "Imposta KeePassXC-Browser come password manager predefinito per il browser.",
"description": "Default password manager help text."
},
"optionsRedirectAllowance": {
@ -931,7 +959,7 @@
"description": "OTP field icon option help text."
},
"optionsHideIconsAlertText": {
"message": "Puoi nascondere le icone manualmente quando occorre con Shift+Click",
"message": "Puoi nascondere le icone manualmente quando occorre con Maiusc+Clic",
"description": "Hide icons alert text text."
},
"optionsAutoRetrieveCredentialsHelpText": {
@ -947,7 +975,7 @@
"description": "Auto-Fill Single Entry option help text."
},
"optionsAutoFillRelevantCredentialHelpText": {
"message": "Compila automaticamente le credenziali pertinenti se è necessario un secondo riempimento.",
"message": "Se è necessario un secondo riempimento compila automaticamente le credenziali pertinenti.",
"description": "Auto-Fill Relevant Credential option help text."
},
"optionsAutoFillSingleEntryWarning": {
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1079,7 +1115,7 @@
"description": "Confirmation text when removing database from the list."
},
"optionsDatabasesRemoveIdentifierConfirmFirst": {
"message": "Vuoi davvero rimuovere l'identificatore $1 dall'elenco del database? ",
"message": "Vuoi rimuovere l'identificatore $1 dall'elenco database? ",
"description": "Confirmation text when removing database from the list."
},
"optionsDatabasesRemoveIdentifierConfirmSecond": {
@ -1110,8 +1146,8 @@
"message": "URL pagina",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Ignora",
"optionsColumnFeatures": {
"message": "Funzionalità",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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."
@ -1395,23 +1435,23 @@
"description": "Lock database button title text."
},
"welcomeText": {
"message": "Benvenuto su KeePassXC-Browser!",
"message": "Benvenuto in 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? La nostra Guida per l'utente è qui per aiutarti.",
"message": "Cerchi una documentazione più completa? \nLa 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 su KeePassXC-Browser, l'estensione ufficiale del browser per KeePassXC.",
"message": "Benvenuti in KeePassXC-Browser, l'estensione ufficiale del browser per KeePassXC.",
"description": "Welcome to KeePassXC-Browser, the official browser extension for KeePassXC."
},
"gettingStartedSecondWelcomeText": {
@ -1419,7 +1459,7 @@
"description": "Please go through the default settings and check all your preferred options."
},
"gettingStartedNewUser": {
"message": "Sei un nuovo utente? Consulta i link alla nostra documentazione.",
"message": "Sei un nuovo utente? \nConsulta i collegamenti 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": "Edit URL for this Site Preferences.",
"message": "このサイト設定の URL を編集します。",
"description": "Edit button title text on Site Preferences tab."
},
"allowIframeButtonTitle": {
@ -20,7 +20,7 @@
"description": "Connect button title text."
},
"copyDebugInfoButtonTitle": {
"message": "Copy debug info to clipboard",
"message": "クリップボードにデバッグ情報をコピー",
"description": "Copy debug info button title text on About tab."
},
"dismissHttpAuthButtonTitle": {
@ -319,9 +319,13 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "文字列フィールド",
"message": "String Field",
"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."
@ -338,9 +342,13 @@
"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": "選択したものを確認するか、文字列フィールドとして別のフィールドを追加で選択してください。",
"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": "キーボードの数字キーで入力フィールドを選択できます。",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "ログイン情報が見つかりません。",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "KeePassXC に複数のログイン情報が見つかりました!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "サイト設定",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "情報",
"description": "About page header."
@ -699,7 +723,7 @@
"description": "Add button text."
},
"optionsButtonEdit": {
"message": "Edit",
"message": "編集",
"description": "Edit button text."
},
"optionsButtonUpdate": {
@ -726,6 +750,10 @@
"message": "設定をエクスポート",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Reset all settings",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "新しいパスワードを保存するデフォルトのグループ:",
"description": "Default group options text."
@ -783,7 +811,7 @@
"description": "Automatically fill-in single credential entry checkbox text."
},
"optionsCheckboxAutoFillRelevantCredential": {
"message": "Automatically fill in relevant credential entries",
"message": "関連する資格情報を自動的に補完する",
"description": "Automatically fill-in relevant credential entry checkbox text."
},
"optionsCheckboxAutoFillSingleTotp": {
@ -827,11 +855,11 @@
"description": "Show login notifications checkbox text."
},
"optionsDefaultPasswordManager": {
"message": "Set as default password manager",
"message": "デフォルトのパスワードマネージャーに設定する",
"description": "Default password manager checkbox text."
},
"optionsDefaultPasswordManagerHelpText": {
"message": "Sets KeePassXC-Browser as the default password manager for the browser.",
"message": "KeePassXC-Browser をブラウザーのデフォルトのパスワードマネージャーに設定します",
"description": "Default password manager help text."
},
"optionsRedirectAllowance": {
@ -843,7 +871,7 @@
"description": "Redirect allowance help text."
},
"optionsCheckboxAutoFillAndSend": {
"message": "HTTP Basic 認証の資格情報の入力を許可する",
"message": "HTTP ベーシック認証への資格情報の補完を許可する",
"description": "Allow filling HTTP Basic Auth credentials checkbox text."
},
"optionsDebugLogging": {
@ -931,7 +959,7 @@
"description": "OTP field icon option help text."
},
"optionsHideIconsAlertText": {
"message": "You can also hide icons manually when needed with shift+click.",
"message": "必要に応じて Shift + クリックでアイコンを隠すこともできます。",
"description": "Hide icons alert text text."
},
"optionsAutoRetrieveCredentialsHelpText": {
@ -947,7 +975,7 @@
"description": "Auto-Fill Single Entry option help text."
},
"optionsAutoFillRelevantCredentialHelpText": {
"message": "Automatically fill in the relevant credential if a second fill is needed.",
"message": "2 回目の入力が必要な場合、関連する資格情報を自動的に補完します。",
"description": "Auto-Fill Relevant Credential option help text."
},
"optionsAutoFillSingleEntryWarning": {
@ -1027,9 +1055,13 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "ページの URL",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "無視",
"optionsColumnFeatures": {
"message": "Features",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"message": "自動送信を無効にする",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Disable passkeys",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "すべての機能を無効にする",
"description": "Site Preferences option selection."
@ -1395,7 +1435,7 @@
"description": "Lock database button title text."
},
"welcomeText": {
"message": "Welcome to KeePassXC-Browser!",
"message": "KeePassXC-Browser にようこそ!",
"description": "Main title of Getting Started page."
},
"documentationGettingStarted": {

View file

@ -319,9 +319,13 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "문자열 필드",
"message": "String Field",
"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."
@ -338,9 +342,13 @@
"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": "선택 사항을 확인하거나 더 많은 <em>문자열 필드</em>를 선택하십시오.",
"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": "키보드에서 숫자를 눌러서 입력 필드를 선택할 수 있습니다.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "오류:\n로그인을 찾을 수 없습니다.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "오류:\nKeePassXC에 하나 이상의 로그인 정보가 있습니다!\nKeePassXC-브라우저 아이콘을 눌러서 더 많은 옵션을 보십시오.",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "Site Preferences",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "정보",
"description": "About page header."
@ -726,6 +750,10 @@
"message": "설정 내보내기",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Reset all settings",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "새 암호를 저장할 기본 그룹:",
"description": "Default group options text."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "페이지 URL",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "무시",
"optionsColumnFeatures": {
"message": "Features",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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,9 +319,13 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "Eilutės laukas",
"message": "String Field",
"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."
@ -338,9 +342,13 @@
"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": "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."
"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": "Taip pat galite naudoti skaičius, kad pasirinktumėte įvesties laukus iš klaviatūros.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Nerasta jokių prisijungimų.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "KeePassXC buvo rastas daugiau nei vienas prisijungimas!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"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."
@ -726,6 +750,10 @@
"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."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "Puslapio URL",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Nepaisyti",
"optionsColumnFeatures": {
"message": "Features",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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,9 +319,13 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "Tekstfelt",
"message": "String Field",
"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."
@ -338,9 +342,13 @@
"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": "Bekreft utvalget, eller velg flere felt som tekstfelt.",
"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": "Du kan også bruke tallene til å velge innputtfeltene fra tastatur.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Fant ingen pålogginger.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "Mer enn én pålogging ble funnet i KeePassXC!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "Site Preferences",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Om",
"description": "About page header."
@ -726,6 +750,10 @@
"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."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "Nettside-URL",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Ignorere",
"optionsColumnFeatures": {
"message": "Features",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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,6 +322,10 @@
"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."
@ -338,9 +342,13 @@
"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 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."
"message": "Bevestig de selectie of kies meer velden als tekenreeksvelden.",
"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.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Geen inloggegevens aangetroffen.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Invulling van de inloggegevens geblokkeerd in niet-vertrouwd iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "Er zijn meerdere inloggegevens aangetroffen in KeePassXC!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "Websitevoorkeuren",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Instellingen",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Over",
"description": "About page header."
@ -726,6 +750,10 @@
"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."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "Pagina-url",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Negeren",
"optionsColumnFeatures": {
"message": "Functies",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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,6 +322,10 @@
"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."
@ -338,9 +342,13 @@
"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.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Nie znaleziono danych logowania.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Wypełnienie danych uwierzytelniających zablokowane w niezaufanej ramce iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "Znaleziono więcej niż jedne dane logowania w KeePassXC!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "Ustawienia witryn",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Ustawienia",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Informacje",
"description": "About page header."
@ -726,6 +750,10 @@
"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."
@ -935,7 +963,7 @@
"description": "Hide icons alert text text."
},
"optionsAutoRetrieveCredentialsHelpText": {
"message": "KeePassXC-Browser natychmiast odbierze dane uwierzytelniające po aktywowaniu karty.",
"message": "KeePassXC-Browser natychmiast pobierze dane uwierzytelniające po aktywowaniu karty.",
"description": "Auto-Retrive Credentials option help text."
},
"optionsAutomaticReconnectHelpText": {
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "Adres URL strony",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Ignorowanie",
"optionsColumnFeatures": {
"message": "Funkcje",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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,9 +319,13 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "Campo para texto",
"message": "Campo do 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."
@ -338,9 +342,13 @@
"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": "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."
"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."
},
"defineKeyboardText": {
"message": "Você também pode usar os números para escolher os campos de entrada do teclado.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Nenhum login foi encontrado.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Preenchimento de credenciais bloqueado em iframe não confiável.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "Mais de um login foi encontrado no KeePassXC!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"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."
@ -726,6 +750,10 @@
"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."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "URL da página",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Ignorar",
"optionsColumnFeatures": {
"message": "Características",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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,9 +319,13 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "Campo de cadeia",
"message": "String Field",
"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."
@ -338,9 +342,13 @@
"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": "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."
"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": "Também pode usar os números para escolher os campos com o teclado",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Não foram encontradas credenciais",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "Existe mais do que uma credencial guardada no KeePassXC!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"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."
@ -726,6 +750,10 @@
"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."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "URL da página",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Ignorar",
"optionsColumnFeatures": {
"message": "Features",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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,9 +319,13 @@
"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."
@ -338,9 +342,13 @@
"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": "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."
"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": "De asemenea, puteți utiliza numerele pentru a alege câmpurile de intrare de la tastatură.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Nu s-au găsit autentificări.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Credential fill blocked in untrusted iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "În KeePassXC s-au găsit mai multe autentificări!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "Site Preferences",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Settings",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Despre",
"description": "About page header."
@ -726,6 +750,10 @@
"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."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "URL Site",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Dezactivare funcții",
"optionsColumnFeatures": {
"message": "Features",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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": "Edit URL for this Site Preferences.",
"message": "Изменить URL-адрес для этих настроек сайта.",
"description": "Edit button title text on Site Preferences tab."
},
"allowIframeButtonTitle": {
@ -20,7 +20,7 @@
"description": "Connect button title text."
},
"copyDebugInfoButtonTitle": {
"message": "Copy debug info to clipboard",
"message": "Копировать отладочную информацию в буфер обмена",
"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": "Current database is not connected.",
"message": "Текущая база данных не подключена.",
"description": "Error notification shown when current database is not connected during action."
},
"passwordGeneratorErrorTooLong": {
@ -319,9 +319,13 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "Строковое поле",
"message": "String Field",
"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."
@ -338,9 +342,13 @@
"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": "Пожалуйста, подтвердите свой выбор или выберите больше строковых полей.",
"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": "Вы также можете использовать числа для выбора полей ввода с клавиатуры.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Не найдены логины.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Заполнение учётных данных заблокировано в ненадежном iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "В KeePassXC обнаружено более одного логина!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -627,7 +647,7 @@
"description": "Dismiss button text when in HTTP Authentication popup."
},
"optionsDefaultSettingsTitle": {
"message": "Default settings",
"message": "Настройки по умолчанию",
"description": "Default settings title for Getting Started page."
},
"optionsWelcomeTitle": {
@ -674,6 +694,10 @@
"message": "Настройки сайта",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Настройки",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Сведения",
"description": "About page header."
@ -699,7 +723,7 @@
"description": "Add button text."
},
"optionsButtonEdit": {
"message": "Edit",
"message": "Изменить",
"description": "Edit button text."
},
"optionsButtonUpdate": {
@ -726,6 +750,10 @@
"message": "Экспорт настроек",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Сбросить все настройки",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Группа по умолчанию для сохранения новых паролей:",
"description": "Default group options text."
@ -783,7 +811,7 @@
"description": "Automatically fill-in single credential entry checkbox text."
},
"optionsCheckboxAutoFillRelevantCredential": {
"message": "Automatically fill in relevant credential entries",
"message": "Автоматически заполнять соответствующие записи учётных данных",
"description": "Automatically fill-in relevant credential entry checkbox text."
},
"optionsCheckboxAutoFillSingleTotp": {
@ -827,11 +855,11 @@
"description": "Show login notifications checkbox text."
},
"optionsDefaultPasswordManager": {
"message": "Set as default password manager",
"message": "Установить как менеджер паролей по умолчанию",
"description": "Default password manager checkbox text."
},
"optionsDefaultPasswordManagerHelpText": {
"message": "Sets KeePassXC-Browser as the default password manager for the browser.",
"message": "Устанавливает KeePassXC-Browser в качестве менеджера паролей по умолчанию для браузера.",
"description": "Default password manager help text."
},
"optionsRedirectAllowance": {
@ -931,7 +959,7 @@
"description": "OTP field icon option help text."
},
"optionsHideIconsAlertText": {
"message": "You can also hide icons manually when needed with shift+click.",
"message": "При необходимости вы также можете скрыть значки вручную, нажав Shift+клик.",
"description": "Hide icons alert text text."
},
"optionsAutoRetrieveCredentialsHelpText": {
@ -947,7 +975,7 @@
"description": "Auto-Fill Single Entry option help text."
},
"optionsAutoFillRelevantCredentialHelpText": {
"message": "Automatically fill in the relevant credential if a second fill is needed.",
"message": "Автоматически заполнить соответствующие учётные данные, если требуется повторное заполнение.",
"description": "Auto-Fill Relevant Credential option help text."
},
"optionsAutoFillSingleEntryWarning": {
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "Адрес страницы",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Игнорировать",
"optionsColumnFeatures": {
"message": "Функции",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"message": "Выключить автоподстановку",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Отключить ключи доступа",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Отключить все функции",
"description": "Site Preferences option selection."
@ -1395,31 +1435,31 @@
"description": "Lock database button title text."
},
"welcomeText": {
"message": "Welcome to KeePassXC-Browser!",
"message": "Добро пожаловать в KeePassXC-Browser!",
"description": "Main title of Getting Started page."
},
"documentationGettingStarted": {
"message": "Our Getting Started Guide will get you up and running quickly.",
"message": "Наше руководство по началу работы поможет вам быстро приступить к работе.",
"description": "Getting Started document text."
},
"documentationUserGuide": {
"message": "Looking for more comprehensive documentation? Our User Guide is there to help.",
"message": "Ищете более подробную документацию? Наше руководство пользователя вам поможет.",
"description": "User Guide text."
},
"documentationTroubleshootingGuide": {
"message": "Need help troubleshooting the browser integration? Check the Troubleshooting Guide.",
"message": "Нужна помощь в устранении неполадок интеграции с браузером? Ознакомьтесь с руководством по устранению неполадок.",
"description": "Troubleshooting Guide text."
},
"gettingStartedWelcomeText": {
"message": "Welcome to KeePassXC-Browser, the official browser extension for KeePassXC.",
"message": "Добро пожаловать в KeePassXC-Browser, официальное расширение браузера для KeePassXC.",
"description": "Welcome to KeePassXC-Browser, the official browser extension for KeePassXC."
},
"gettingStartedSecondWelcomeText": {
"message": "Please go through the default settings and check all your preferred options.",
"message": "Пожалуйста, проверьте настройки по умолчанию и выберите все предпочтительные параметры.",
"description": "Please go through the default settings and check all your preferred options."
},
"gettingStartedNewUser": {
"message": "Are you a new user? Check links to our documentation.",
"message": "Вы новый пользователь? Посмотрите ссылки на нашу документацию.",
"description": "Are you a new user? Check links to our documentation."
}
}

View file

@ -322,6 +322,10 @@
"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."
@ -338,9 +342,13 @@
"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": "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."
"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."
},
"defineKeyboardText": {
"message": "You can also use the numbers to choose the input fields from keyboard.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Fel:\nInga inloggningar hittades.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Ifyllning av inloggningsuppgifter blockerad av otillförlitlig iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "Fel:\nMer än en inloggning hittades i KeePassXC!\nTryck på ikonen för KeePassXC-Browser för fler alternativ.",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"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."
@ -726,6 +750,10 @@
"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."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "Sid-URL",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Ignorera",
"optionsColumnFeatures": {
"message": "Funktioner",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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şmesi",
"message": "Modern tarayıcılar için KeePassXC bütünleştirmesi",
"description": "Name of the extension."
},
"addSitePreferenceButtonTitle": {
@ -319,9 +319,13 @@
"description": "Clear save data button text when choosing Custom Login Fields."
},
"defineStringField": {
"message": "Metin alanı",
"message": "Yazı 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."
@ -335,12 +339,16 @@
"description": "Choosing a TOTP field text when choosing Custom Login Fields."
},
"defineChooseStringFields": {
"message": "Metin alanlarını seçin",
"message": "Yazı 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 dizge 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 yazı 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.",
@ -359,9 +367,13 @@
"description": "General text for password."
},
"stringFields": {
"message": "Metin alanları",
"message": "Yazı 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."
@ -370,6 +382,10 @@
"message": "Herhangi bir oturum açma bulunamadı.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Güvenilmeyen bir iFrame nedeniyle kimlik doğrulama bilgilerinin doldurulması engellendi.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "KeePassXC üzerinde birden fazla oturum açma bulundu!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -399,7 +419,7 @@
"description": "Message shown when no password fields are found."
},
"fieldsPasswordFillNotAccepted": {
"message": "Parolanın bir düz metin alanına yazılması engellenir.",
"message": "Parolanın bir düz yazı alanına yazılması engellenir.",
"description": "Message shown when password fill to a plain text field is prevented."
},
"rememberNothingChanged": {
@ -674,6 +694,10 @@
"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."
@ -726,6 +750,10 @@
"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."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "Sayfa adresi",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Yok say",
"optionsColumnFeatures": {
"message": "Özellikler",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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": "Запит на Passkeys-ключі скасовано.",
"message": "Запит на використання ключів доступу скасовано.",
"description": "Passkeys request canceled."
},
"errorMessagePasskeysInvalidUserVerification": {
@ -224,7 +224,7 @@
"description": "Wait for timer to expire."
},
"errorMessagePasskeysUnknownError": {
"message": "Невідома помилка паролів.",
"message": "Невідома помилка ключів доступу.",
"description": "Unknown passkeys error."
},
"errorMessagePasskeysInvalidChallenge": {
@ -322,6 +322,10 @@
"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."
@ -338,9 +342,13 @@
"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": "Ви також можете використовувати числа для вибору полів введення з клавіатури.",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "Не знайдено облікових даних.",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "Заповнення облікових даних заблоковано в ненадійному iframe.",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "Знайдено більше ніж один запис у KeePassXC!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "Налаштування сайту",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "Налаштування",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "Про розширення",
"description": "About page header."
@ -726,6 +750,10 @@
"message": "Експортувати налаштування",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "Скинути всі налаштування",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "Типова група для збереження нових паролів:",
"description": "Default group options text."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "URL-адреса сторінки",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "Ігнорувати",
"optionsColumnFeatures": {
"message": "Особливості",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"message": "Вимкнути автовідправлення",
"description": "Site Preferences option selection."
},
"optionsSelectionPasskeys": {
"message": "Вимкнути ключі доступу",
"description": "Site Preferences option selection."
},
"optionsSelectionFull": {
"message": "Вимкнути всі можливості",
"description": "Site Preferences option selection."
@ -1359,11 +1399,11 @@
"description": "Extension title in settings page"
},
"optionsPasskeysTitle": {
"message": "Passkey-ключі",
"message": "Ключі доступу",
"description": "Passkeys settings title in settings page."
},
"optionsPasskeysEnable": {
"message": "Увімкнути паролі",
"message": "Увімкнути ключі доступу",
"description": "Enabled passkeys option text."
},
"optionsPasskeysEnableHelpText": {
@ -1371,11 +1411,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,6 +322,10 @@
"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."
@ -338,9 +342,13 @@
"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": "您也可以使用键盘的数字键选择输入字段。",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "未找到登录信息。",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "凭据填充在不受信任的 iframe 中被阻止。",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "在 KeePassXC 中找到多条登录信息!",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "网站设置",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "设置",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "关于",
"description": "About page header."
@ -726,6 +750,10 @@
"message": "导出配置",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "重置所有设置",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "保存新密码的默认群组:",
"description": "Default group options text."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "页面 URL",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "忽略",
"optionsColumnFeatures": {
"message": "功能",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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,6 +322,10 @@
"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."
@ -338,9 +342,13 @@
"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": "請確認您的選擇,或是選擇更多欄位作為<em>字串欄位</em>。",
"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": "您也可以使用鍵盤的數字鍵選擇輸入欄位。",
@ -362,6 +370,10 @@
"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."
@ -370,6 +382,10 @@
"message": "錯誤:\n找不到登錄資訊。",
"description": "Shown when no credentials are found for the current page."
},
"credentialsBlockedInIframe": {
"message": "憑證填入在不可信 iframe 而被阻止。",
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
},
"credentialsMultipleFound": {
"message": "錯誤:\n在 KeePassXC 中找到多個登錄資訊!\n點選 KeePassXC-Browser 圖示來檢視更多選項。",
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
@ -390,6 +406,10 @@
"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."
@ -674,6 +694,10 @@
"message": "網站設定",
"description": "Site Preferences page header."
},
"optionsSitePreferencesSettings": {
"message": "設定",
"description": "Site Preferences settings button text."
},
"optionsMenuAbout": {
"message": "關於",
"description": "About page header."
@ -726,6 +750,10 @@
"message": "導出配置",
"description": "Export settings button text."
},
"optionsButtonResetSettings": {
"message": "重設所有設定",
"description": "Reset all settings button text."
},
"optionsLabelDefaultGroup": {
"message": "儲存新密碼的預設群組:",
"description": "Default group options text."
@ -1030,6 +1058,10 @@
"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."
@ -1066,6 +1098,10 @@
"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."
@ -1110,8 +1146,8 @@
"message": "網頁 URL",
"description": "Site Preferences list column title."
},
"optionsColumnIgnore": {
"message": "忽略",
"optionsColumnFeatures": {
"message": "特性",
"description": "Site Preferences list column title."
},
"optionsColumnUsernameOnly": {
@ -1142,6 +1178,10 @@
"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 = (isFirefox() ? 'svg' : 'png');
const filetype = (page.isFirefox || page.isSafari) ? '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

@ -13,14 +13,14 @@ kpxcEvent.onMessage = async function(request, sender) {
}
};
kpxcEvent.showStatus = async function(tab, configured, internalPoll) {
kpxcEvent.showStatus = async function(tab, configured, internalPoll, forceShowDefault = false) {
let keyId = null;
if (configured && keepass.databaseHash !== ''
&& Object.hasOwn(keepass.keyRing, keepass.databaseHash)) {
keyId = keepass.keyRing[keepass.databaseHash].id;
}
if (!internalPoll) {
if (!internalPoll || forceShowDefault) {
browserAction.showDefault(tab);
}
@ -76,7 +76,7 @@ kpxcEvent.onSaveSettings = async function(tab, settings) {
kpxcEvent.onGetStatus = async function(tab, args = []) {
// When internalPoll is true the event is triggered from content script in intervals -> don't poll KeePassXC
try {
const [ internalPoll = false, triggerUnlock = false ] = args;
const [ internalPoll = false, triggerUnlock = false, forceShowDefault ] = args;
if (!internalPoll) {
const response = await keepass.testAssociation(tab, [ true, triggerUnlock ]);
if (!response) {
@ -85,7 +85,7 @@ kpxcEvent.onGetStatus = async function(tab, args = []) {
}
const configured = await keepass.isConfigured();
return kpxcEvent.showStatus(tab, configured, internalPoll);
return kpxcEvent.showStatus(tab, configured, internalPoll, forceShowDefault);
} catch (err) {
logError('No status shown: ' + err);
return Promise.reject();
@ -241,6 +241,10 @@ 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,
@ -249,7 +253,6 @@ 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,
@ -261,6 +264,7 @@ 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,
@ -296,6 +300,7 @@ 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,10 +6,15 @@ 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 (!isFirefox()) {
if (!page.isFirefox) {
handleReq = httpAuth.handleRequestCallback;
reqType = 'asyncBlocking';
}

View file

@ -10,12 +10,6 @@ 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
@ -115,7 +109,8 @@ const initListeners = async function() {
if (contextMenuItems.some(e => e.action === command)
|| command === 'redetect_fields'
|| command === 'choose_credential_fields'
|| command === 'retrive_credentials_forced'
|| command === 'retrieve_credentials_forced'
|| command === 'reopen_database'
|| command === 'reload_extension') {
const tab = await getCurrentTab();
if (tab?.id) {
@ -160,26 +155,32 @@ const initListeners = async function() {
});
};
const initContextMenuItems = async function() {
const initContextMenuItems = async function() {
page.menuContexts = [ 'editable' ];
if (page.isFirefox) {
page.menuContexts.push('password');
}
// Create context menu items
await browser.contextMenus.removeAll();
for (const item of contextMenuItems) {
try {
await browser.contextMenus.create({
title: item.title,
contexts: menuContexts,
contexts: page.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();
@ -189,7 +190,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,20 +2,31 @@
const keepass = {};
keepass.associated = { 'value': false, 'hash': null };
keepass.keyPair = { publicKey: null, secretKey: null };
keepass.serverPublicKey = '';
keepass.featuresList = {
downloadFaviconAfterSave: false,
newTotp: false,
passwordGenerator: false,
passkeys: false,
passkeysDefaultGroup: false,
requiredKeePassXCVersionFound: false,
};
keepass.cacheTimeout = 30 * 1000; // Milliseconds
keepass.clientID = '';
keepass.currentKeePassXC = '';
keepass.databaseHash = '';
keepass.isConnected = false;
keepass.isDatabaseClosed = true;
keepass.isKeePassXCAvailable = false;
keepass.isEncryptionKeyUnrecognized = false;
keepass.currentKeePassXC = '';
keepass.requiredKeePassXC = '2.3.1';
keepass.isKeePassXCAvailable = false;
keepass.keyPair = { publicKey: null, secretKey: null };
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',
@ -172,7 +183,7 @@ keepass.generatePassword = async function(tab) {
return '';
}
if (!compareVersion(keepass.requiredKeePassXC, keepass.currentKeePassXC)) {
if (!keepass.featuresList.passwordGenerator) {
return '';
}
@ -229,9 +240,7 @@ keepass.associate = async function(tab) {
const response = await keepassClient.sendMessage(kpAction, tab, messageData, nonce, false, true);
if (response) {
// 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.setCryptoKey(response.id, idKey);
keepass.associated.value = true;
keepass.associated.hash = response.hash || 0;
@ -335,7 +344,13 @@ 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);
@ -412,6 +427,7 @@ 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]) {
@ -544,7 +560,7 @@ keepass.createNewGroup = async function(tab, args = []) {
keepass.getTotp = async function(tab, args = []) {
const [ uuid, oldTotp ] = args;
if (!compareVersion('2.6.1', keepass.currentKeePassXC, true)) {
if (!keepass.featuresList.newTotpSupported) {
return oldTotp;
}
@ -609,11 +625,14 @@ 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: JSON.parse(JSON.stringify(publicKey)),
publicKey: passkeyPublicKey,
origin: origin,
relatedOrigins: relatedOrigins,
groupName: page?.settings?.defaultPasskeyGroup,
keys: keepass.getCryptoKeys()
};
@ -641,13 +660,15 @@ keepass.passkeysGet = async function(tab, args = []) {
const kpAction = kpActions.PASSKEYS_GET;
const nonce = keepassClient.getNonce();
const publicKey = args[0];
const origin = args[1];
const [ publicKey, origin ] = args;
const passkeyPublicKey = JSON.parse(JSON.stringify(publicKey));
const relatedOrigins = await keepass.getPasskeysRelatedOrigins(passkeyPublicKey?.rp?.id);
const messageData = {
action: kpAction,
publicKey: JSON.parse(JSON.stringify(publicKey)),
publicKey: passkeyPublicKey,
origin: origin,
relatedOrigins: relatedOrigins,
keys: keepass.getCryptoKeys()
};
@ -783,13 +804,9 @@ keepass.getCryptoKeys = function() {
keepass.enableAutomaticReconnect = async function() {
// Disable for Windows if KeePassXC is older than 2.3.4
if (!page.settings.autoReconnect
|| (navigator.platform.toLowerCase().includes('win')
&& keepass.currentKeePassXC
&& !compareVersion('2.3.4', keepass.currentKeePassXC))) {
if (!page.settings.autoReconnect) {
return;
}
if (keepass.reconnectLoop === null) {
keepass.reconnectLoop = setInterval(async () => {
if (!keepass.isKeePassXCAvailable) {
@ -807,7 +824,9 @@ 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) {
@ -863,7 +882,9 @@ 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();
@ -879,7 +900,7 @@ keepass.checkForNewKeePassXCVersion = async function() {
let version = -1;
try {
const response = await fetch(keepass.latestVersionUrl);
const response = await fetch(keepass.latestVersionUrl, { signal: AbortSignal.timeout(DEFAULT_FETCH_TIMEOUT) });
const jsonData = await response.json();
if (jsonData?.tag_name && jsonData?.prerelease === false) {
version = jsonData.tag_name;
@ -891,6 +912,44 @@ 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;
@ -945,6 +1004,25 @@ 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: 1,
redirectAllowance: 3,
saveDomainOnly: true,
showGettingStartedGuideAlert: true,
showGroupNameInAutocomplete: true,
@ -49,7 +49,10 @@ 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;
@ -61,12 +64,17 @@ 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 (isFirefox() && typeof(browser.storage.managed) === 'object') {
if (page.isFirefox && typeof(browser.storage.managed) === 'object') {
try {
const managedSettings = await browser.storage.managed.get('settings');
if (managedSettings?.settings) {
@ -74,16 +82,16 @@ page.initSettings = async function() {
item.settings = managedSettings.settings;
}
} catch (err) {
logError('page.initSettings: ' + err);
debugLogMessage('page.initSettings: ' + err);
}
} else if (typeof(chrome.storage.managed) === 'object') {
} else if (!page.isSafari && 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) => {
logError('page.initSettings: ' + err);
debugLogMessage('page.initSettings: ' + err);
});
}
@ -138,6 +146,16 @@ 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);
@ -257,11 +275,11 @@ page.retrieveCredentials = async function(tab, args = []) {
return credentials;
};
page.getLoginId = async function(tab) {
page.getLoginId = async function(tab, returnSingle = true) {
const currentTab = page.tabs[tab.id];
// If there's only one credential available and loginId is not set
if (currentTab && !currentTab.loginId && currentTab.credentials.length === 1) {
if (currentTab && returnSingle && !currentTab.loginId && currentTab.credentials.length === 1) {
return currentTab.credentials[0].uuid;
}
@ -335,14 +353,15 @@ page.fillHttpAuth = async function(tab, credentials) {
}
};
page.isSiteIgnored = async function(tab, currentLocation) {
page.isSiteIgnored = async function(tab, args = []) {
const [ currentLocation, checkPasskeys ] = args;
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) {
if (site.ignore === IGNORE_FULL || (checkPasskeys && site.ignore === IGNORE_PASSKEYS)) {
return true;
}
}
@ -465,7 +484,7 @@ page.getTopLevelDomainFromUrl = async function(domain, url) {
url: url
});
}
} catch (e) {
} catch (_e) {
return domain;
}
}
@ -498,7 +517,7 @@ page.getBaseDomainFromUrl = async function(hostname, url) {
const createContextMenuItem = function({ action, args, ...options }) {
return browser.contextMenus.create({
contexts: menuContexts,
contexts: page.menuContexts,
id: action,
...options
});

File diff suppressed because one or more lines are too long

View file

@ -1,11 +1,13 @@
'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
@ -26,23 +28,6 @@ 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,
@ -61,6 +46,47 @@ 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;
@ -157,10 +183,10 @@ const trimURL = function(url) {
};
const debugLogMessage = function(message, extra) {
console.log(`[Debug ${getFileAndLine()}] ${EXTENSION_NAME} - ${message}`);
console.debug(`[Debug ${getFileAndLine()}] ${EXTENSION_NAME} - ${message}`);
if (extra) {
console.log(extra);
console.debug(extra);
}
};
@ -182,10 +208,20 @@ 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 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);
};
// Exports for tests
if (typeof module === 'object') {
module.exports = {
compareVersion,
elementsOverlap,
matchesWithNodeName,
siteMatch,
slashNeededForUrl,

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) {
if ((!identifier || identifier.length === 0) && !field) {
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,6 +100,42 @@ 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;
@ -214,6 +250,8 @@ 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;
@ -232,6 +270,20 @@ 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,7 +100,9 @@ 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) {
@ -117,6 +119,7 @@ class Autocomplete {
this.container.append(this.list);
this.shadowRoot.append(this.container);
document.body.append(this.wrapper);
kpxcUI.observeWrapper(this.wrapper);
// Add a footer message for auto-submit
if (this.autoSubmit) {
@ -127,6 +130,9 @@ class Autocomplete {
this.updateList();
this.container.classList.add('kpxcAutocomplete-container--visible');
if (kpxcFields.popoverSupported) {
this.container.showPopover({ source: inputField });
}
this.updatePosition();
}
@ -234,6 +240,9 @@ class Autocomplete {
}
this.container.classList.remove('kpxcAutocomplete-container--visible');
if (kpxcFields.popoverSupported) {
this.container.hidePopover();
}
}
getAllItems() {
@ -337,7 +346,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 = (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon');
const className = getIconClass('kpxc-banner-icon');
const icon = kpxcUI.createElement('span', className, { 'alt': 'logo' });
const infoText = kpxcUI.createElement('span', '', {}, tr('rememberInfoText'));
const infoText = kpxcUI.createElement('span', 'banner-info-text', {}, tr('rememberInfoText'));
const usernameText = kpxcUI.createElement('span', 'small', {}, tr('popupUsername') + ' ');
const usernameSpan = kpxcUI.createElement('span', 'small info information-username', {}, credentials.username);
@ -165,6 +165,7 @@ kpxcBanner.create = async function(credentials = {}) {
if (window.self === window.top && !kpxcBanner.created) {
window.parent.document.body.appendChild(wrapper);
kpxcUI.observeWrapper(wrapper);
kpxcBanner.created = true;
}
};

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,9 +19,10 @@ 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 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 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 kpxcCustomLoginFieldsBanner = {};
kpxcCustomLoginFieldsBanner.banner = undefined;
@ -30,8 +31,9 @@ kpxcCustomLoginFieldsBanner.created = false;
kpxcCustomLoginFieldsBanner.dataStep = STEP_NONE;
kpxcCustomLoginFieldsBanner.infoText = undefined;
kpxcCustomLoginFieldsBanner.wrapper = undefined;
kpxcCustomLoginFieldsBanner.inputQueryPatternNormal = inputQueryPatternStart + inputQueryPatternNotCheckbox + inputQueryPattern;
kpxcCustomLoginFieldsBanner.inputQueryPatternStringFields = inputQueryPatternStart + inputQueryPattern;
kpxcCustomLoginFieldsBanner.inputQueryPatternNormal =
INPUT_QUERY_PATTERNS_START + INPUT_QUERY_PATTERN_NOT_CHECKBOX + INPUT_QUERY_PATTERN;
kpxcCustomLoginFieldsBanner.inputQueryPatternStringFields = INPUT_QUERY_PATTERNS_START + INPUT_QUERY_PATTERN;
kpxcCustomLoginFieldsBanner.markedFields = [];
kpxcCustomLoginFieldsBanner.nonSelectedElementsPattern = `div.${FIXED_FIELD_CLASS}:not(.${USERNAME_FIELD_CLASS}):not(.${PASSWORD_FIELD_CLASS}):not(.${TOTP_FIELD_CLASS}):not(.${STRING_FIELD_CLASS})`;
@ -44,6 +46,7 @@ kpxcCustomLoginFieldsBanner.selection = {
totpElement: undefined,
fields: [],
fieldElements: [],
submitButton: undefined,
};
kpxcCustomLoginFieldsBanner.buttons = {
@ -90,9 +93,9 @@ kpxcCustomLoginFieldsBanner.create = async function() {
const bannerInfo = kpxcUI.createElement('div', 'banner-info');
const bannerButtons = kpxcUI.createElement('div', 'banner-buttons');
const iconClassName = isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon';
const iconClassName = getIconClass('kpxc-banner-icon');
const icon = kpxcUI.createElement('span', iconClassName);
const infoText = kpxcUI.createElement('span', '', {}, tr('defineChooseCustomLoginFieldText'));
const infoText = kpxcUI.createElement('span', 'banner-info-text', {}, tr('defineChooseCustomLoginFieldText'));
const separator = kpxcUI.createElement('div', 'kpxc-separator');
const secondSeparator = kpxcUI.createElement('div', 'kpxc-separator');
@ -101,6 +104,7 @@ 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);
@ -117,10 +121,11 @@ 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, secondSeparator, clearDataButton, confirmButton, closeButton);
bannerButtons.appendMultiple(resetButton, separator, usernameButton, passwordButton, totpButton,
stringFieldsButton, submitButton, secondSeparator, clearDataButton, confirmButton, closeButton);
banner.appendMultiple(bannerInfo, bannerButtons);
kpxcUI.makeBannerDraggable(banner);
@ -159,6 +164,7 @@ kpxcCustomLoginFieldsBanner.create = async function() {
if (!kpxcCustomLoginFieldsBanner.created) {
window.self.document.body.appendChild(wrapper);
kpxcUI.observeWrapper(wrapper);
kpxcCustomLoginFieldsBanner.created = true;
}
@ -189,7 +195,8 @@ 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;
}
@ -207,7 +214,8 @@ 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;
}
@ -225,7 +233,8 @@ 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;
}
@ -256,6 +265,25 @@ 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;
@ -282,6 +310,8 @@ 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();
}
};
@ -314,6 +344,8 @@ 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;
}
};
@ -321,10 +353,11 @@ 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) {
if (usernamePath || passwordPath || totpPath || stringFieldsPaths.length > 0 || submitButtonPath) {
if (currentSettings) {
// Update the single selection to current settings
if (usernamePath) {
@ -345,13 +378,19 @@ 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
fields: stringFieldsPaths,
submitButton: submitButtonPath,
};
}
@ -381,7 +420,8 @@ kpxcCustomLoginFieldsBanner.resetSelection = function() {
username: undefined,
password: undefined,
totp: undefined,
fields: []
fields: [],
submitButton: undefined,
};
kpxcCustomLoginFieldsBanner.removeMarkedFields();
@ -423,15 +463,24 @@ 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 (
(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])
(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])
);
}
@ -460,7 +509,7 @@ kpxcCustomLoginFieldsBanner.setSelectedField = function(elem) {
kpxcCustomLoginFieldsBanner.buttons.close.textContent = tr('optionsButtonCancel');
};
// Expects 'username', 'password' or 'totp'
// Expects 'username', 'password', 'totp' or 'submitButton'
kpxcCustomLoginFieldsBanner.selectField = function(fieldType) {
kpxcCustomLoginFieldsBanner.eventFieldClick = function(e) {
const field = kpxcCustomLoginFieldsBanner.getSelectedField(e);
@ -516,9 +565,10 @@ kpxcCustomLoginFieldsBanner.selectStringFields = function() {
kpxcCustomLoginFieldsBanner.markFields = function() {
let firstInput;
const inputs = document.querySelectorAll(
kpxcCustomLoginFieldsBanner.dataStep === STEP_SELECT_STRING_FIELDS
? kpxcCustomLoginFieldsBanner.inputQueryPatternStringFields
: kpxcCustomLoginFieldsBanner.inputQueryPatternNormal);
kpxcCustomLoginFieldsBanner.dataStep === STEP_SELECT_SUBMIT_BUTTON ? INPUT_BUTTON_QUERY_PATTERN :
(STEP_SELECT_STRING_FIELDS
? kpxcCustomLoginFieldsBanner.inputQueryPatternStringFields
: kpxcCustomLoginFieldsBanner.inputQueryPatternNormal));
const zoom = kpxcUI.bodyStyle.zoom || 1;
for (const i of inputs) {
@ -544,7 +594,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);
@ -674,7 +724,10 @@ kpxcCustomLoginFieldsBanner.handleTopWindowMessage = function(args) {
kpxcCustomLoginFieldsBanner.selection.totp = selection;
kpxcCustomLoginFieldsBanner.setSelectedField();
} else if (message === 'string_field_selected') {
kpxcCustomLoginFieldsBanner.selection.stringFields = selection;
kpxcCustomLoginFieldsBanner.selection.fields = selection;
kpxcCustomLoginFieldsBanner.setSelectedField();
} else if (message === 'submitButton_selected') {
kpxcCustomLoginFieldsBanner.selection.submitButton = selection;
kpxcCustomLoginFieldsBanner.setSelectedField();
} else if (message === 'enable_clear_data_button') {
kpxcCustomLoginFieldsBanner.buttons.clearData.style.display = 'inline-block';
@ -699,6 +752,8 @@ 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') {
@ -766,5 +821,7 @@ 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,6 +9,7 @@ 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.
@ -29,7 +30,7 @@ kpxcFields.getAllCombinations = async function(inputs) {
form: input.form
};
combinations.push(combination);
combinations.push(kpxcFields.getExistingCombination(combination));
usernameField = null;
} else if (kpxcTOTPIcons.isValid(input)) {
// Dynamically added TOTP field
@ -93,6 +94,32 @@ 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) {
@ -411,6 +438,97 @@ 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
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
].some((e) => e !== elem)) {
return false;
}
// Check if element has an overlay
if (kpxcFields.isOverlayOnTop(rect)) {
return false;
}
return true;
};
// Returns true if element is visible on the page
kpxcFields.isVisible = function(elem) {
// Returns true if opacity is not set, otherwise check the limits
@ -430,6 +548,10 @@ kpxcFields.isVisible = function(elem) {
return false;
}
if (!kpxcFields.isTopElement(elem, rect)) {
return false;
}
// Check CSS visibility
const elemStyle = getComputedStyle(elem);
if (elemStyle.visibility && (elemStyle.visibility === 'hidden' || elemStyle.visibility === 'collapse')
@ -472,14 +594,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) {
if (!creds.username && !creds.password && !creds.totp && creds.fields.length === 0 && !creds.submitButton) {
return;
}
// Finds the input field based on the stored ID
const findInputField = async function(inputFields, idArray) {
// Finds the element based on the stored ID
const findElement = async function(fields, idArray) {
if (idArray) {
const input = inputFields.find(e => e === kpxcFields.getId(idArray, e));
const input = fields.find(e => e === kpxcFields.getId(idArray, e));
if (input) {
return input;
}
@ -496,16 +618,24 @@ kpxcFields.useCustomLoginFields = async function() {
}
});
const [ username, password, totp ] = await Promise.all([
await findInputField(inputFields, creds.username),
await findInputField(inputFields, creds.password),
await findInputField(inputFields, creds.totp)
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),
]);
// Handle StringFields
const stringFields = [];
for (const sf of creds.fields) {
const field = await findInputField(inputFields, sf);
const field = await findElement(inputFields, sf);
if (field) {
stringFields.push(field);
}
@ -517,13 +647,19 @@ 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
fields: stringFields,
submitButton: submitButton
});
return combinations;

View file

@ -18,7 +18,7 @@ kpxcFill.fillAttributeToActiveElementWith = async function(attr) {
return;
}
kpxc.setValue(el, value[0]);
await kpxcFill.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;
}
kpxcFill.setTOTPValue(el, totp);
await 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) {
kpxcFill.setTOTPValue(el, val);
await kpxcFill.setTOTPValue(el, val);
}
}
}
};
// Set normal or segmented TOTP value
kpxcFill.setTOTPValue = function(elem, val) {
kpxcFill.setTOTPValue = async function(elem, val) {
if (kpxc.credentials.length === 0) {
logDebug('Error: Credential list is empty.');
return;
@ -173,22 +173,22 @@ kpxcFill.setTOTPValue = function(elem, val) {
for (const comb of kpxc.combinations) {
if (comb.totpInputs?.length > 0) {
kpxcFill.fillSegmentedTotp(elem, val, comb.totpInputs);
await kpxcFill.fillSegmentedTotp(elem, val, comb.totpInputs);
return;
}
}
kpxc.setValue(elem, val);
await kpxcFill.setValue(elem, val);
};
// Fill TOTP in parts
kpxcFill.fillSegmentedTotp = function(elem, val, totpInputs) {
kpxcFill.fillSegmentedTotp = async function(elem, val, totpInputs) {
if (!totpInputs.includes(elem) || val.length < totpInputs.length) {
return;
}
for (let i = 0; i < totpInputs.length; ++i) {
kpxc.setValue(totpInputs[i], val[i]);
await kpxcFill.setValue(totpInputs[i], val[i]);
}
};
@ -217,7 +217,7 @@ kpxcFill.fillFromUsernameIcon = async function(combination) {
*/
kpxcFill.fillInCredentials = async function(combination, predefinedUsername, uuid, passOnly = false) {
if (kpxc.credentials.length === 0) {
kpxcUI.createNotification('error', tr('credentialsNoLoginsFound'));
showErrorNotification(`${tr('credentialsNoLoginsFound')} ${document.location.origin}`);
return;
}
@ -245,6 +245,17 @@ 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
@ -260,28 +271,28 @@ kpxcFill.fillInCredentials = async function(combination, predefinedUsername, uui
return;
}
kpxc.setValueWithChange(combination.password, selectedCredentials.password);
await kpxcFill.setValueWithChange(combination.password, selectedCredentials.password);
await kpxc.setPasswordFilled(true);
}
// Fill username
if (combination.username && usernameValue &&
if (combination.username && usernameValue && combination.username !== combination.password &&
(!combination.username.value || combination.username.value !== usernameValue)) {
if (!passOnly) {
kpxc.setValueWithChange(combination.username, usernameValue);
await kpxcFill.setValueWithChange(combination.username, usernameValue);
}
}
// Fill StringFields
if (selectedCredentials.stringFields?.length > 0) {
kpxcFill.fillInStringFields(combination.fields, selectedCredentials.stringFields);
await 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) {
kpxcFill.fillTOTPFromUuid(totpCombination.totp, selectedCredentials.uuid);
await kpxcFill.fillTOTPFromUuid(totpCombination.totp, selectedCredentials.uuid);
}
}
@ -295,7 +306,7 @@ kpxcFill.fillInCredentials = async function(combination, predefinedUsername, uui
};
// Fills StringFields defined in Custom Fields
kpxcFill.fillInStringFields = function(fields, stringFields) {
kpxcFill.fillInStringFields = async function(fields, stringFields) {
const filledInFields = [];
if (fields && stringFields && fields?.length > 0 && stringFields?.length > 0) {
for (let i = 0; i < fields.length; i++) {
@ -307,7 +318,7 @@ kpxcFill.fillInStringFields = function(fields, stringFields) {
const currentField = fields[i];
if (currentField && stringFieldValue[0]) {
kpxc.setValue(currentField, stringFieldValue[0], true);
await kpxcFill.setValue(currentField, stringFieldValue[0], true);
filledInFields.push(currentField);
}
}
@ -329,7 +340,8 @@ kpxcFill.performAutoSubmit = async function(combination, skipAutoSubmit) {
if (!skipAutoSubmit && !autoSubmitIgnoredForSite) {
await sendMessage('page_set_autosubmit_performed');
const submitButton = kpxcForm.getFormSubmitButton(combination.form);
// Use submit button from Custom Login Fields or detect it from the form
const submitButton = combination?.submitButton ?? kpxcForm.getFormSubmitButton(combination.form);
if (submitButton !== undefined) {
submitButton.click();
} else if (combination.form) {
@ -340,6 +352,79 @@ 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 =
@ -356,9 +441,35 @@ 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) {
if (!connectedDatabase?.identifier && kpxc.databaseState === DatabaseState.UNLOCKED) {
kpxcUI.createNotification('error', tr('errorCurrentDatabaseNotConnected'));
} else if (!await isIframeAllowed()) {
// Special error if we are blocking due to iframe
kpxcUI.createNotification('error', tr('credentialsBlockedInIframe'));
} else {
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) {
if (passwordField && kpxcFields.isVisible(passwordField)) {
await kpxc.setPasswordFilled(true);
}
@ -66,6 +66,9 @@ 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
@ -88,13 +91,19 @@ kpxcForm.getFormSubmitButton = function(form) {
b => !b.getAttribute('formAction')
);
if (buttons.length > 0) {
return buttons.at(-1);
const lastButton = buttons.at(-1);
// Accept button if it has no indication for password
if (!hasPasswordClassOrId(lastButton)) {
return buttons.at(-1);
}
}
// Try to find similar buttons outside the form which are added via 'form' property
for (const e of form.elements) {
if ((matchesWithNodeName(e, 'BUTTON') && (e.type === 'button' || e.type === 'submit' || e.type === ''))
|| (matchesWithNodeName(e, 'INPUT') && (e.type === 'button' || e.type === 'submit'))) {
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)) {
return e;
}
}
@ -116,9 +125,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)) {
@ -166,6 +175,8 @@ kpxcForm.initForm = function(form, credentialFields) {
if (submitButton) {
submitButton.addEventListener('click', kpxcForm.onSubmit);
}
kpxcUI.pageObserver.observe(form, OBSERVER_OPTIONS);
}
};

View file

@ -0,0 +1,248 @@
'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

@ -0,0 +1,67 @@
'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

@ -1,116 +0,0 @@
'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 === 'allowIframes') {
if (optionName === SitePreferences.ALLOW_IFRAMES) {
await sendMessage('page_set_allow_iframes', [ true, site ]);
await sendMessage('iframe_detected', false);
} else if (optionName === 'usernameOnly') {
} else if (optionName === SitePreferences.USERNAME_ONLY) {
await sendMessage('username_field_detected', false);
}
};
@ -81,8 +81,9 @@ kpxc.clearAllFromPage = function() {
kpxcUserAutocomplete.closeList();
}
// Switch back to default popup
sendMessage('get_status', [ true ]); // This is an internal function call
// Clear logins from background and switch back to default popup
sendMessage('page_clear_logins');
sendMessage('get_status', [ true, false, true ]); // This is an internal function call, forceShowDefault
};
// Creates a new combination manually from active element
@ -207,6 +208,8 @@ 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)) {
@ -358,7 +361,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 && !kpxcFields.isCustomLoginFieldsUsed()) {
if (formInputs.length === 0 && pageInputs.length === 0) {
// Run 'redetect_credentials' manually if no fields are found after a page load
setTimeout(async function() {
if (_called.automaticRedetectCompleted) {
@ -516,7 +519,7 @@ kpxc.prepareCredentials = async function() {
kpxc.initAutocomplete();
if (kpxc.settings.autoFillRelevantCredential) {
const pageUuid = await sendMessage('page_get_login_id');
const pageUuid = await sendMessage('page_get_login_id', false);
if (pageUuid) {
const relevantCredential = kpxc.credentials.find(c => c.uuid === pageUuid);
const combination = kpxc.combinations?.at(-1);
@ -646,11 +649,14 @@ kpxc.rememberCredentialsFromContextMenu = async function() {
// Credential Banner can force the retrieval for reloading new/modified credentials.
kpxc.retrieveCredentials = async function(force = false) {
if (!await isIframeAllowed()) {
return [];
return;
}
kpxc.url = document.location.href;
kpxc.submitUrl = kpxc.getFormActionUrl(kpxc.combinations[0]);
// 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);
if (kpxc.settings.autoRetrieveCredentials && kpxc.url && kpxc.submitUrl) {
await kpxc.retrieveCredentialsCallback(
@ -715,52 +721,6 @@ 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);
@ -772,7 +732,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.
@ -893,6 +853,7 @@ 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.');
@ -949,9 +910,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('allowIframes');
kpxc.addToSitePreferences(SitePreferences.ALLOW_IFRAMES);
} else if (req.action === 'add_username_only_option') {
kpxc.addToSitePreferences('usernameOnly', true);
kpxc.addToSitePreferences(SitePreferences.USERNAME_ONLY, true);
} else if (req.action === 'check_database_hash' && 'hash' in req) {
kpxc.detectDatabaseChange(req);
} else if (req.action === 'choose_credential_fields') {
@ -980,9 +941,14 @@ 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);
@ -996,9 +962,14 @@ 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 === 'retrive_credentials_forced') {
} else if (req.action === 'retrieve_credentials_forced') {
await kpxc.retrieveCredentials(true);
} else if (req.action === 'show_password_generator') {
kpxcPasswordGenerator.showPasswordGenerator();
@ -1028,11 +999,18 @@ kpxc.reconnect = async function() {
const isIframeAllowed = async function() {
sendMessage('iframe_detected', false);
// Don't allow sandboxed iframes
if (self.origin === null || self.origin === 'null') {
logDebug('Error: Sandboxed iframes are not allowed');
return false;
}
try {
// 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,11 +18,18 @@ kpxcObserverHelper.ignoredNodeNames = [
'A',
'HEAD',
'HTML',
'IMG',
'LINK',
'SCRIPT',
'VIDEO',
];
kpxcObserverHelper.ignoredPartialNodeNames = [
'REDDIT-PDP',
'RENDER-TEMPLATE',
'SHREDDIT',
];
kpxcObserverHelper.ignoredNodeTypes = [
Node.ATTRIBUTE_NODE,
Node.TEXT_NODE,
@ -72,15 +79,22 @@ 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') {
if (mut.addedNodes.length > 0) {
kpxcObserverHelper.handleObserverAdd(mut.addedNodes[0]);
} else if (mut.removedNodes.length > 0) {
kpxcObserverHelper.handleObserverRemove(mut.removedNodes[0]);
}
mut.addedNodes.forEach(function (node) {
kpxcObserverHelper.handleObserverAdd(node);
});
mut.removedNodes.forEach(function (node) {
kpxcObserverHelper.handleObserverRemove(node);
});
} else if (mut.type === 'attributes' && (mut.attributeName === 'class' || mut.attributeName === 'style')) {
// Only accept targets with forms
const forms = matchesWithNodeName(mut.target, 'FORM')
@ -230,6 +244,31 @@ 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)) {
@ -242,6 +281,8 @@ kpxcObserverHelper.handleObserverAdd = async function(target) {
return;
}
kpxcObserverHelper.handleTransitions(target);
const inputs = kpxcObserverHelper.getInputs(target);
if (inputs.length === 0) {
return;
@ -259,7 +300,7 @@ kpxcObserverHelper.handleObserverAdd = async function(target) {
kpxc.prepareCredentials();
}
kpxcIcons.deleteHiddenIcons();
kpxcIcons.deleteAllHiddenIcons();
};
// Removes monitored elements
@ -273,7 +314,7 @@ kpxcObserverHelper.handleObserverRemove = function(target) {
return;
}
kpxcIcons.deleteHiddenIcons();
kpxcIcons.deleteAllHiddenIcons();
};
// Handles CSS transitionend event
@ -305,6 +346,7 @@ 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;
@ -321,7 +363,7 @@ const getShadowDOM = function(elem) {
try {
return elem.openOrClosedShadowRoot ? elem.openOrClosedShadowRoot : browser.dom.openOrClosedShadowRoot(elem);
} catch (e) {
} catch (_e) {
return elem.shadowRoot;
}
};
@ -330,7 +372,9 @@ const getShadowDOM = function(elem) {
const treeWalkerFilter = function(node) {
return !node ||
node?.disabled ||
(typeof node?.getAttribute !== 'undefined' && node?.getLowerCaseAttribute('type') === 'hidden')
(node instanceof Element
&& typeof node?.getAttribute === 'function'
&& node?.getLowerCaseAttribute('type') === 'hidden')
? NodeFilter.FILTER_REJECT
: NodeFilter.FILTER_ACCEPT;
};

View file

@ -8,13 +8,14 @@ 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(() => {
@ -62,6 +63,14 @@ 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'));
@ -71,14 +80,14 @@ const enablePasskeys = async function() {
if (ev.detail.action === 'passkeys_create') {
const publicKey = kpxcPasskeysUtils.buildCredentialCreationOptions(
ev.detail.publicKey,
ev.detail.sameOriginWithAncestors,
isSameOriginWithAncestors(),
);
passkeysLogDebug('Passkey request', publicKey);
await sendResponse('passkeys_register', publicKey);
} else if (ev.detail.action === 'passkeys_get') {
const publicKey = kpxcPasskeysUtils.buildCredentialRequestOptions(
ev.detail.publicKey,
ev.detail.sameOriginWithAncestors,
isSameOriginWithAncestors(),
);
passkeysLogDebug('Passkey request', publicKey);
await sendResponse('passkeys_get', publicKey);
@ -99,7 +108,7 @@ const initContent = async () => {
return;
}
if (await chrome.runtime.sendMessage({ action: 'is_site_ignored', args: window.self.location.href })) {
if (await chrome.runtime.sendMessage({ action: 'is_site_ignored', args: [ window.self.location.href, true ] })) {
console.log('This site is ignored in Site Preferences.');
return;
}

View file

@ -26,13 +26,58 @@
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: () => null,
getPublicKey: () => publicKey.response?.publicKey ? publicKey.response?.publicKey : null,
getPublicKeyAlgorithm: () => publicKey.response?.publicKeyAlgorithm,
getTransports: () => [ 'internal' ]
};
@ -63,7 +108,8 @@
response: authenticatorResponse,
type: publicKey.type,
clientExtensionResults: () => publicKey?.response?.clientExtensionResults || {},
getClientExtensionResults: () => publicKey?.response?.clientExtensionResults || {}
getClientExtensionResults: () => publicKey?.response?.clientExtensionResults || {},
toJSON: () => kpxcPublicKeyCredentialJson(publicKeyCredential, publicKey)
};
return Object.setPrototypeOf(publicKeyCredential, PublicKeyCredential.prototype);
@ -91,14 +137,6 @@
});
};
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) {
@ -147,11 +185,9 @@
return null;
}
const sameOriginWithAncestors = isSameOriginWithAncestors();
const response = await postMessageToExtension({
action: 'passkeys_create',
publicKey: options.publicKey,
sameOriginWithAncestors: sameOriginWithAncestors,
});
if (!response.publicKey) {
@ -172,11 +208,9 @@
return originalCredentials.get(options);
}
const sameOriginWithAncestors = isSameOriginWithAncestors();
const response = await postMessageToExtension({
action: 'passkeys_get',
publicKey: options.publicKey,
sameOriginWithAncestors: sameOriginWithAncestors,
});
if (!response.publicKey) {
@ -201,12 +235,14 @@
// select a software authenticator. This could be removed in the future.
try {
Object.defineProperty(navigator, 'credentials', { value: passkeysCredentials });
Object.defineProperty(window.PublicKeyCredential, 'isConditionalMediationAvailable', {
value: isConditionalMediationAvailable,
});
Object.defineProperty(window.PublicKeyCredential, 'isUserVerifyingPlatformAuthenticatorAvailable', {
value: isUserVerifyingPlatformAuthenticatorAvailable,
});
if (window.PublicKeyCredential) {
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,10 +11,6 @@ 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
@ -34,7 +30,7 @@ class PasswordIcon extends Icon {
this.nextFieldExists = false;
this.initField(field);
kpxcUI.monitorIconPosition(this);
kpxcIcons.monitorIconPosition(this);
}
}
@ -49,19 +45,22 @@ PasswordIcon.prototype.initField = function(field) {
};
PasswordIcon.prototype.createIcon = function(field) {
const className = (isFirefox() ? 'key-moz' : 'key');
const size = (field.offsetHeight > 28) ? 24 : 16;
const offset = kpxcUI.calculateIconOffset(field, size);
const className = getIconClass('key');
const size = this.calculateIconSize(field);
const icon = kpxcUI.createElement('div', 'kpxc kpxc-pwgen-icon ' + className,
{
'title': tr('passwordGeneratorGenerateText'),
'size': size,
'offset': offset,
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id') // Needed?
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id'),
'popover': 'manual'
});
icon.style.zIndex = '10000000';
if (kpxcFields.popoverSupported) {
icon.style.margin = 0;
} else {
icon.style.zIndex = '10000000';
}
icon.style.width = Pixels(size);
icon.style.height = Pixels(size);
@ -75,7 +74,11 @@ PasswordIcon.prototype.createIcon = function(field) {
}
if (e.shiftKey) {
icon.style.display = 'none';
if (kpxcFields.popoverSupported) {
icon.hidePopover();
} else {
icon.style.display = 'none';
}
return;
}
@ -86,9 +89,12 @@ PasswordIcon.prototype.createIcon = function(field) {
icon.addEventListener('mousedown', ev => ev.stopPropagation());
icon.addEventListener('mouseup', ev => ev.stopPropagation());
kpxcUI.setIconPosition(icon, field, this.rtl);
kpxcIcons.setIconPosition(icon, field, this.rtl);
this.icon = icon;
this.createWrapper('css/pwgen.css');
if (kpxcFields.popoverSupported) {
icon.showPopover();
}
};
@ -151,13 +157,7 @@ kpxcPasswordGenerator.fill = function(elem, password) {
const isPasswordGeneratorSupported = async function() {
const response = await browser.runtime.sendMessage({
action: 'get_keepassxc_versions'
action: 'get_features_list'
});
const result = await browser.runtime.sendMessage({
action: 'compare_versions',
args: [ [ '2.7.0' ], response.current ]
});
return result['2.7.0'] || false;
return response?.passwordGenerator;
};

View file

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

View file

@ -13,6 +13,14 @@ 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 = {
DISCONNECTED: 0,
LOCKED: 1,
@ -27,73 +35,15 @@ 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);
}
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) {
kpxcUI.bodyRect = document.body.getBoundingClientRect();
const bodyRect = document.body.getBoundingClientRect();
kpxcUI.bodyRect = {
left: bodyRect.left + window.pageXOffset,
top: bodyRect.top + window.pageYOffset
};
kpxcUI.bodyStyle = getComputedStyle(document.body);
}
@ -121,63 +71,6 @@ 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;
};
@ -194,32 +87,6 @@ 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;
@ -290,48 +157,29 @@ 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 = function(type, message) {
kpxcUI.createNotification = async 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;
}
@ -353,7 +201,7 @@ kpxcUI.createNotification = function(type, message) {
const notification = kpxcUI.createElement('div', 'kpxc-notification kpxc-notification-' + type, {});
type = type.charAt(0).toUpperCase() + type.slice(1) + '!';
const className = (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon');
const className = getIconClass('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);
@ -394,8 +242,39 @@ kpxcUI.createButton = function(color, textContent, callback) {
return button;
};
const DOMRectToArray = function(domRect) {
return [ domRect.bottom, domRect.height, domRect.left, domRect.right, domRect.top, domRect.width, domRect.x, domRect.y ];
// Observe and prevent style changes to wrapper div elements
kpxcUI.createWrapperObserver = function() {
kpxcUI.wrapperObserver = new MutationObserver(function(mutations, obs) {
for (const mut of mutations) {
if (mut?.target && mut.target.style?.cssText !== 'all: unset;') {
mut.target.removeAttribute('style');
mut.target.style.all = 'unset';
}
}
});
};
kpxcUI.observeWrapper = function(elem) {
kpxcUI.wrapperObserver?.observe(elem, OBSERVER_OPTIONS);
};
// Observer <html> and <body> style changes
kpxcUI.createPageObserver = function() {
kpxcUI.pageObserver = new MutationObserver(function(mutations, obs) {
for (const mut of mutations) {
const currentStyle = getComputedStyle(mut?.target);
if (currentStyle.opacity && currentStyle.opacity < MIN_OPACITY) {
kpxc.clearAllFromPage();
}
}
});
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);
}
};
const initColorTheme = function(elem) {
@ -423,6 +302,11 @@ const logDebug = function(message, extra) {
}
};
const initObservers = function() {
kpxcUI.createWrapperObserver();
kpxcUI.createPageObserver();
};
document.addEventListener('mousedown', function(e) {
if (!e.isTrusted) {
return;
@ -439,6 +323,12 @@ document.addEventListener('mouseup', function(e) {
kpxcUI.mouseDown = false;
});
if (document.readyState === 'complete' || (document.readyState !== 'loading' && !document.documentElement.doScroll)) {
initObservers();
} else {
document.addEventListener('DOMContentLoaded', initObservers);
}
HTMLDivElement.prototype.appendMultiple = function(...args) {
for (const a of args) {
this.append(a);

View file

@ -12,10 +12,6 @@ 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
@ -34,7 +30,7 @@ class UsernameFieldIcon extends Icon {
super(field, databaseState);
this.initField(field);
kpxcUI.monitorIconPosition(this);
kpxcIcons.monitorIconPosition(this);
}
switchIcon(state) {
@ -68,9 +64,7 @@ UsernameFieldIcon.prototype.initField = function(field) {
UsernameFieldIcon.prototype.createIcon = function(field) {
const className = getIconClassName(this.databaseState);
// 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 size = this.calculateIconSize(field);
// Don't create the icon if the input field is too small
if (field.offsetWidth < (size * 1.5) || field.offsetHeight < size) {
@ -78,16 +72,19 @@ 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,
'offset': offset,
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id')
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id'),
'popover': 'manual'
});
icon.style.zIndex = '10000000';
if (kpxcFields.popoverSupported) {
icon.style.margin = 0;
} else {
icon.style.zIndex = '10000000';
}
icon.style.width = Pixels(size);
icon.style.height = Pixels(size);
@ -97,7 +94,11 @@ UsernameFieldIcon.prototype.createIcon = function(field) {
}
if (e.shiftKey) {
icon.style.display = 'none';
if (kpxcFields.popoverSupported) {
icon.hidePopover();
} else {
icon.style.display = 'none';
}
return;
}
@ -108,9 +109,12 @@ UsernameFieldIcon.prototype.createIcon = function(field) {
icon.addEventListener('mousedown', ev => ev.stopPropagation());
icon.addEventListener('mouseup', ev => ev.stopPropagation());
kpxcUI.setIconPosition(icon, field, this.rtl);
kpxcIcons.setIconPosition(icon, field, this.rtl);
this.icon = icon;
this.createWrapper('css/username.css');
if (kpxcFields.popoverSupported) {
icon.showPopover();
}
};
const iconClicked = async function(field, icon) {
@ -139,12 +143,12 @@ const iconClicked = async function(field, icon) {
const getIconClassName = function(state = DatabaseState.UNLOCKED) {
if (state === DatabaseState.LOCKED) {
return (isFirefox() ? 'lock-moz' : 'lock');
return getIconClass('lock');
} else if (state === DatabaseState.DISCONNECTED) {
return (isFirefox() ? 'disconnected-moz' : 'disconnected');
return getIconClass('disconnected');
}
return (isFirefox() ? 'unlock-moz' : 'unlock');
return getIconClass('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: hidden !important;
overflow-x: auto !important;
padding: 4px !important;
position: fixed !important;
width: 100% !important;
@ -49,6 +49,10 @@ 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;
@ -77,6 +81,14 @@ 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;
@ -93,6 +105,14 @@ 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;
@ -113,6 +133,7 @@ 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,6 +8,7 @@
height: 28px !important;
margin: .2em !important;
padding: 1px 7px 2px !important;
text-wrap: nowrap;
}
.kpxc-white-button {

View file

@ -4,6 +4,7 @@
--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;
@ -29,7 +30,6 @@
}
@media (prefers-color-scheme: dark) {
:root,
:host {
--kpxc-autocomplete-footer-color: #2b2a2a;
@ -114,4 +114,3 @@
--kpxc-lighter-text-color: rgba(0, 0, 0, 0.8);
--kpxc-light-text-color: rgba(0, 0, 0, 0.5);
}
}

View file

@ -43,6 +43,16 @@
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,5 +1,7 @@
.kpxc-pwgen-icon {
border: 0;
cursor: pointer;
padding: 0 !important;
position: absolute;
}
@ -12,3 +14,8 @@
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,14 +1,21 @@
.kpxc-totp-icon {
position: absolute;
border: 0;
cursor: pointer;
padding: 0 !important;
position: absolute;
}
.kpxc-totp-icon.default {
.kpxc-totp-icon {
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,6 +1,8 @@
.kpxc-username-icon {
position: absolute;
border: 0;
cursor: pointer;
padding: 0 !important;
position: absolute;
}
.kpxc-username-icon.disconnected {
@ -13,6 +15,11 @@
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;
@ -23,6 +30,11 @@
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;
@ -32,3 +44,8 @@
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.9.9.2",
"version_name": "1.9.9.2",
"minimum_chrome_version": "93",
"version": "1.10.0.1",
"version_name": "1.10.0.1",
"minimum_chrome_version": "124",
"description": "__MSG_extensionDescription__",
"author": "KeePassXC Team",
"icons": {
@ -53,7 +53,8 @@
"content/fields.js",
"content/fill.js",
"content/form.js",
"content/icons.js",
"content/icon.js",
"content/icon-handler.js",
"content/keepassxc-browser.js",
"content/observer-helper.js",
"content/pwgen.js",
@ -118,12 +119,15 @@
"choose_credential_fields": {
"description": "__MSG_popupChooseCredentialsText__"
},
"retrive_credentials_forced": {
"description": "__MSG_popupReopenButton__"
"retrieve_credentials_forced": {
"description": "__MSG_credentialsRetrieveButton__"
},
"request_autotype": {
"description": "__MSG_contextMenuRequestGlobalAutoType__"
},
"reopen_database": {
"description": "__MSG_popupReopenButton__"
},
"reload_extension": {
"description": "__MSG_popupReloadButton__"
}
@ -166,8 +170,7 @@
"tabs",
"webNavigation",
"webRequest",
"webRequestAuthProvider",
"webRequestBlocking"
"webRequestAuthProvider"
],
"optional_permissions": [
"privacy"

View file

@ -82,6 +82,10 @@ pre {
white-space: pre-line;
}
.no-transitions .form-check-input {
transition: none !important;
}
.table > caption {
color: var(--kpxc-text-color);
}
@ -105,12 +109,6 @@ 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;
}
@ -136,14 +134,14 @@ table tbody tr.empty:not(:nth-last-child(2)) {
display: none;
}
#tab-site-preferences td:nth-of-type(2),
#tab-site-preferences td:nth-of-type(2) select {
#tab-site-preferences td:nth-of-type(3),
#tab-site-preferences td:nth-of-type(3) 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;
}
@ -233,6 +231,26 @@ 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">
<body class="pt-3 pb-5 no-transitions">
<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-ellipsis-h fa-lg pe-2" aria-hidden="true"></i>
<i class="fa fa-cog 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-wrench fa-lg pe-2" aria-hidden="true"></i>
<i class="fa fa-list-alt fa-lg pe-2" aria-hidden="true"></i>
<span class="menu-item" data-i18n="optionsMenuSitePreferences"></span>
</a>
</li>
@ -81,6 +81,12 @@
</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>
@ -154,7 +160,7 @@
</div>
<!-- Keyboard shortcuts -->
<div class="card my-4 shadow">
<div class="card my-4 shadow" id="keyboardShortcuts">
<div class="card-header h6 rounded-0">
<i class="fa fa-keyboard-o" aria-hidden="true"></i>
<span data-i18n="optionsKeyboardShortcutsHeader"></span>
@ -260,7 +266,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>
@ -278,7 +284,7 @@
</div>
<!-- Autofill HTTP Auth dialogs -->
<div class="form-group pb-1">
<div class="form-group pb-1" id="autoFillHttpAuth">
<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>
@ -352,8 +358,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="1"></label>
<input type="range" class="form-range" id="redirectAllowance" name="redirectAllowance" min="1" max="11" step="1" value="1">
<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">
<div class="form-text help-text" data-i18n="optionsRedirectAllowanceHelpText"></div>
</div>
@ -559,6 +565,10 @@
<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>
@ -586,6 +596,30 @@
</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 -->
@ -761,16 +795,14 @@
<thead>
<tr>
<th scope="col"><span data-i18n="optionsColumnPageURL"></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>
<th scope="col"><span data-i18n="optionsColumnFeatures"></span></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr class="empty">
<td colspan="5"><span data-i18n="optionsSitePreferencesNotFound"></span></td>
<td colspan="4"><span data-i18n="optionsSitePreferencesNotFound"></span></td>
</tr>
<tr class="clone d-none">
<td class="text-nowrap">
@ -790,17 +822,20 @@
</button>
</div>
</td>
<td>
<select class="form-select form-select-sm" name="ignore" data-i18n="[title]optionsSitePreferencesSelect">
<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">
<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>
@ -838,6 +873,32 @@
</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,6 +1,8 @@
'use strict';
const options = {};
options.dropdownButton = null;
options.isFirefox = false;
const $ = function(elem) {
return document.querySelector(elem);
@ -206,7 +208,7 @@ options.initGeneralSettings = async function() {
});
$('#configureCommands').addEventListener('click', function() {
if (isFirefox()) {
if (options.isFirefox) {
if (typeof(browser.commands.openShortcutSettings) === 'function') {
browser.commands.openShortcutSettings();
} else {
@ -285,7 +287,7 @@ options.initGeneralSettings = async function() {
// Verify the import
temporarySettings = contents;
dialogImportSettingsModal.show();
} catch (err) {
} catch (_err) {
console.log('Error loading JSON settings file.');
}
};
@ -311,6 +313,24 @@ 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);
@ -346,38 +366,23 @@ options.showKeePassXCVersions = async function(response) {
$('#tab-about span.kpxcVersion').textContent = response.current;
$('#tab-general-settings button.checkUpdateKeePassXC').disabled = false;
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']) {
const featureList = await browser.runtime.sendMessage({ action: 'get_features_list' });
if (featureList?.requiredKeePassXCVersionFound) {
$('#tab-general-settings #versionRequiredAlert').hide();
} else {
$('#tab-general-settings #showGroupNameInAutocomplete').disabled = true;
$('#tab-general-settings #minimumVersionAlert').show();
}
// Hide certain options with older KeePassXC versions than 2.7.0
if (!versionResults['2.7.0']) {
if (!featureList?.downloadFaviconAfterSave) {
$('#tab-general-settings #downloadFaviconAfterSaveFormGroup').hide();
}
// Hide certain options with older KeePassXC versions than 2.7.7
if (!versionResults['2.7.7']) {
if (!featureList?.passkeys) {
$('#tab-general-settings #passkeysOptionsCard').hide();
}
// Hide passkeys default group option with KeePassXC version < 2.7.10
if (!versionResults['2.7.10']) {
if (!featureList?.passkeysDefaultGroup) {
$('#tab-general-settings #passkeysDefaultGroup').hide();
}
};
@ -536,6 +541,43 @@ 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();
@ -601,18 +643,18 @@ options.initSitePreferences = function() {
}
};
const checkboxClicked = function() {
const closestTr = this.closest('tr');
const checkboxClicked = async function(e) {
const closestTr = options?.dropdownButton?.closest('tr');
const url = closestTr.getAttribute('url');
for (const site of options.settings['sitePreferences']) {
if (site.url === url) {
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;
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;
}
}
}
@ -633,6 +675,11 @@ 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);
@ -662,19 +709,25 @@ 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)
);
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);
// 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);
$('#tab-site-preferences table tbody').append(row);
};
@ -738,11 +791,11 @@ options.initSitePreferences = function() {
$('#tab-site-preferences table tbody tr.empty').hide();
options.settings['sitePreferences'].push({
url: value,
ignore: IGNORE_NOTHING,
usernameOnly: false,
improvedFieldDetection: false,
allowIframes: false,
ignore: IGNORE_NOTHING,
improvedFieldDetection: false,
url: value,
usernameOnly: false,
});
options.saveSettings();
manualUrl.value = '';
@ -777,7 +830,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();
$('#tab-about span.kpxcbrBrowser').textContent = getBrowserId(navigator.userAgent);
};
options.updateTheme = function() {
@ -805,24 +858,110 @@ options.createWarning = function(elem, text) {
}, 5000);
};
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;
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)}`;
}
}
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
@ -837,12 +976,20 @@ const getBrowserId = 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,6 +153,15 @@ 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('retrive_credentials_forced');
await sendMessageToTab('retrieve_credentials_forced');
close();
});

View file

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

1213
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,13 @@
{
"name": "keepassxc-browser",
"version": "1.9.9.2",
"version": "1.10.0.1",
"description": "KeePassXC-Browser",
"main": "build.js",
"devDependencies": {
"@playwright/test": "^1.45.1",
"@eslint/js": "^9.39.1",
"@playwright/test": "^1.56.1",
"@types/node": "^20.14.10",
"eslint": "^8.49.0"
"eslint": "^9.39.1"
},
"dependencies": {
"@npmcli/fs": "^2.1.0"
@ -15,7 +16,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 keepassxc-browser/**/*.js",
"lint": "npx eslint --no-warn-ignored keepassxc-browser/**/*.js",
"tests": "npx playwright test"
},
"repository": {

View file

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

View file

@ -1,6 +1,7 @@
import { test, expect } from '@playwright/test';
import {
compareVersion,
elementsOverlap,
matchesWithNodeName,
siteMatch,
slashNeededForUrl,
@ -89,3 +90,48 @@ test('Test trimURL()', async ({ page }) => {
expect(trimURL('https://example.com/path/')).toBe('https://example.com/path/');
expect(trimURL('https://example.com/path/#extra')).toBe('https://example.com/path/#extra');
});
// Check if different popups/overlays partially covers or touches the input field
test('Test elementsOverlap()', async ({ page }) => {
const inputRect = { left: 0, top: 5, right: 200, bottom: 28 }
// Fully covered
expect(elementsOverlap(inputRect, { left: -2, top: 0, right: 220, bottom: 40 })).toBe(true);
// Top side is covered
expect(elementsOverlap(inputRect, { left: 0, top: 0, right: 220, bottom: 20 })).toBe(true);
// Bottom side is covered
expect(elementsOverlap(inputRect, { left: -2, top: 25, right: 220, bottom: 40 })).toBe(true);
// Left side is covered
expect(elementsOverlap(inputRect, { left: -2, top: 0, right: 100, bottom: 40 })).toBe(true);
// Right side is covered
expect(elementsOverlap(inputRect, { left: 100, top: 0, right: 220, bottom: 40 })).toBe(true);
// Top-left corner is covered
expect(elementsOverlap(inputRect, { left: -2, top: 0, right: 40, bottom: 10 })).toBe(true);
// Top-right corner is covered
expect(elementsOverlap(inputRect, { left: 180, top: 0, right: 220, bottom: 10 })).toBe(true);
// Bottom-left corner is covered
expect(elementsOverlap(inputRect, { left: -2, top: 10, right: 100, bottom: 40 })).toBe(true);
// Bottom-right corner is covered
expect(elementsOverlap(inputRect, { left: 180, top: 10, right: 220, bottom: 40 })).toBe(true);
// Input field is covered with identical size
expect(elementsOverlap(inputRect, { left: 0, top: 5, right: 200, bottom: 28 })).toBe(true);
// Overlay is inside the input field
expect(elementsOverlap(inputRect, { left: 2, top: 10, right: 180, bottom: 26 })).toBe(true);
// Overlay is partially inside the input field, comes outside from the left
expect(elementsOverlap(inputRect, { left: -2, top: 10, right: 180, bottom: 26 })).toBe(true);
// Overlay is outside the input field
expect(elementsOverlap(inputRect, { left: 210, top: 0, right: 240, bottom: 40 })).toBe(false);
});

View file

@ -26,10 +26,10 @@ async function testInputFields() {
[ 'basic4', 3 ], // Username/passwd/TOTP fields
[ 'div1', 2, '#toggle1' ], // Fields are behind a button that must be pressed
[ 'div2', 2, '#toggle2' ], // Fields are behind a button that must be pressed behind a JavaScript
[ 'div3', 2, '#toggle3' ], // Fields are behind a button that must be pressed
[ 'div4', 2, '#toggle4' ], // Fields are behind a button that must be pressed
//[ 'div3', 2, '#toggle3' ], // Fields are behind a button that must be pressed
//[ 'div4', 2, '#toggle4' ], // Fields are behind a button that must be pressed
[ 'hiddenFields1', 0 ], // Two hidden fields
[ 'hiddenFields2', 1 ], // Two hidden fields with one visible
//[ 'hiddenFields2', 1 ], // Two hidden fields with one visible
];
for (const div of testDivs) {
@ -60,6 +60,10 @@ 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 ],