Skip to content

Commit

Permalink
feat: evaluate headers near form element
Browse files Browse the repository at this point in the history
  • Loading branch information
dbajpeyi committed Dec 19, 2024
1 parent a08905c commit 201c856
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 54 deletions.
57 changes: 47 additions & 10 deletions dist/autofill-debug.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 47 additions & 10 deletions dist/autofill.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 51 additions & 14 deletions src/Form/FormAnalyzer.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,41 @@ class FormAnalyzer {
});
}

/**
* Takes an element and returns all its children that are text-only nodes
* @param {HTMLElement|Element} element
* @param {number} maxDepth
* @param {number} currentDepth
* @returns {HTMLElement[]|Element[]}
*/
getElementsWithOnlyTextChild(element, maxDepth = 2, currentDepth = 0) {
// Array to collect elements with only text child nodes
const elementsWithTextChild = [];

updateFormHeaderSignals() {
// If we've reached the max depth, stop traversing further
if (currentDepth > maxDepth) {
return elementsWithTextChild;
}

// Check if the current element has only one text child node
if (element.nodeType === Node.ELEMENT_NODE) {
const childNodes = element.childNodes;

if (childNodes.length === 1 && childNodes[0].nodeType === Node.TEXT_NODE) {
elementsWithTextChild.push(element);
}
}

// Recurse through each child element and collect matching elements
for (const child of element.children) {
// Recursively get elements from child elements, increasing depth by 1
elementsWithTextChild.push(...this.getElementsWithOnlyTextChild(child, maxDepth, currentDepth + 1));
}

return elementsWithTextChild;
}

updateFormHeaderSignals() {
const isVisuallyBeforeForm = (el) => el.getBoundingClientRect().top < this.form.getBoundingClientRect().top;

const isHeaderSized = (el) => {
Expand All @@ -242,24 +274,26 @@ class FormAnalyzer {
const computedStyle = window.getComputedStyle(el);
const fontWeight = computedStyle.fontWeight;
const isRelativelyTall = parseFloat(computedStyle.height) / this.form.clientHeight > 0.1;
if (fontWeight === 'bold' || parseFloat(fontWeight) >= 700 || isRelativelyTall) {
return true
if (isRelativelyTall && (fontWeight === 'bold' || parseFloat(fontWeight) >= 700)) {
return true;
}
}
};

const allSiblings = Array.from(this.form.parentElement?.children ?? [])
.filter((element) => element !== this.form)
if (allSiblings.length === 0) return false;
.map((element) => this.getElementsWithOnlyTextChild(element))
.flat();

if (allSiblings.length === 0) return false;

allSiblings.forEach((sibling) => {
if (sibling instanceof HTMLElement && sibling.childElementCount === 1 && isVisuallyBeforeForm(sibling) && isHeaderSized(sibling)) {
const string = sibling.textContent?.trim();
allSiblings.forEach((element) => {
if (element instanceof HTMLElement && isVisuallyBeforeForm(element) && isHeaderSized(element)) {
const string = element.textContent?.trim();
if (string) {
if (safeRegexTest(/^(sign[- ]?in|log[- ]?in)$/, string)) {
return this.decreaseSignalBy(3, 'Strong login header before form');
} else if (safeRegexTest(/^(sign[- ]?up)$/, string)) {
return this.increaseSignalBy(3, 'Strong signup header before form');
if (safeRegexTest(/^(sign[- ]?in|log[- ]?in)$/i, string)) {
return this.decreaseSignalBy(3, 'Strong login signal above form');
} else if (safeRegexTest(/^(sign[- ]?up)$/i, string)) {
return this.increaseSignalBy(3, 'Strong signup signal above form');
}
}
}
Expand Down Expand Up @@ -385,12 +419,15 @@ class FormAnalyzer {
this.increaseSignalBy(relevantFields.length * 1.5, 'many fields: it is probably not a login');
}

this.updateFormHeaderSignals();

if (this.hasPasswordHints()) {
this.increaseSignalBy(3, 'Password hints');
}

if (this.autofillSignal < 10 && this.autofillSignal > -10) {
// If signal is not strong enough, check for headers above the form
this.updateFormHeaderSignals();
}

// If we can't decide at this point, try reading page headings
if (this.autofillSignal === 0) {
this.evaluatePageHeadings();
Expand Down
57 changes: 47 additions & 10 deletions swift-package/Resources/assets/autofill-debug.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 201c856

Please sign in to comment.