From 362448eebca2643ebf7c1e635b20f5a3cec00c9b Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Tue, 9 Sep 2025 11:41:17 -0400 Subject: [PATCH] Cache overlay discovery prior to checking top-level elements (#2670) Cache overlay discovery prior to checking top-level elements --- .eslintrc | 1 + keepassxc-browser/content/fields.js | 49 ++++++++++++++----- .../content/keepassxc-browser.js | 2 + keepassxc-browser/content/observer-helper.js | 6 +++ 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/.eslintrc b/.eslintrc index c194a97..ccad657 100644 --- a/.eslintrc +++ b/.eslintrc @@ -126,6 +126,7 @@ "isEdge": "readonly", "isElementInside": "readonly", "isFirefox": "readonly", + "isIframeAllowed": "readonly", "keepass": "readonly", "keepassClient": "readonly", "kpActions": "readonly", diff --git a/keepassxc-browser/content/fields.js b/keepassxc-browser/content/fields.js index 47ffe0d..731d604 100644 --- a/keepassxc-browser/content/fields.js +++ b/keepassxc-browser/content/fields.js @@ -411,6 +411,38 @@ 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)) { + 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)) { + logError(e); + } + } +}; + +// Check the visibility of existing fields +kpxcFields.checkExistingFields = function() { + if (kpxc.inputs?.some(input => !kpxcFields.isVisible(input))) { + kpxc.clearAllFromPage(); + kpxc.combinations = []; + } +}; + kpxcFields.isTopElement = function(elem, rect) { if (!elem || !rect) { return false; @@ -439,19 +471,10 @@ kpxcFields.isTopElement = function(elem, rect) { } // Check for popup overlays - try { - // :popover-open selector is supported only with Firefox >= 125 and Chrome >= 114 - const overlays = document.querySelectorAll(':popover-open, [popover]'); - for (const overlay of overlays) { - const overlayRect = overlay?.getBoundingClientRect(); - if (overlayRect && elementsOverlap(rect, overlayRect)) { - return false; - } - } - } catch (e) { - // Ignore SyntaxError (e.g., unsupported selector) - if (!(e instanceof SyntaxError)) { - logError(e); + for (const overlay of kpxcFields.overlays ?? []) { + const overlayRect = overlay?.getBoundingClientRect(); + if (overlayRect && elementsOverlap(rect, overlayRect)) { + return false; } } diff --git a/keepassxc-browser/content/keepassxc-browser.js b/keepassxc-browser/content/keepassxc-browser.js index edfe1ac..6953c8e 100755 --- a/keepassxc-browser/content/keepassxc-browser.js +++ b/keepassxc-browser/content/keepassxc-browser.js @@ -208,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)) { diff --git a/keepassxc-browser/content/observer-helper.js b/keepassxc-browser/content/observer-helper.js index 72c6fcf..53e825b 100644 --- a/keepassxc-browser/content/observer-helper.js +++ b/keepassxc-browser/content/observer-helper.js @@ -72,6 +72,12 @@ 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);