first commit
This commit is contained in:
205
app/chrome-extension/inject-scripts/fill-helper.js
Normal file
205
app/chrome-extension/inject-scripts/fill-helper.js
Normal file
@@ -0,0 +1,205 @@
|
||||
/* eslint-disable */
|
||||
// fill-helper.js
|
||||
// This script is injected into the page to handle form filling operations
|
||||
|
||||
if (window.__FILL_HELPER_INITIALIZED__) {
|
||||
// Already initialized, skip
|
||||
} else {
|
||||
window.__FILL_HELPER_INITIALIZED__ = true;
|
||||
/**
|
||||
* Fill an input element with the specified value
|
||||
* @param {string} selector - CSS selector for the element to fill
|
||||
* @param {string} value - Value to fill into the element
|
||||
* @returns {Promise<Object>} - Result of the fill operation
|
||||
*/
|
||||
async function fillElement(selector, value) {
|
||||
try {
|
||||
// Find the element
|
||||
const element = document.querySelector(selector);
|
||||
if (!element) {
|
||||
return {
|
||||
error: `Element with selector "${selector}" not found`,
|
||||
};
|
||||
}
|
||||
|
||||
// Get element information
|
||||
const rect = element.getBoundingClientRect();
|
||||
const elementInfo = {
|
||||
tagName: element.tagName,
|
||||
id: element.id,
|
||||
className: element.className,
|
||||
type: element.type || null,
|
||||
isVisible: isElementVisible(element),
|
||||
rect: {
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
top: rect.top,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
left: rect.left,
|
||||
},
|
||||
};
|
||||
|
||||
// Check if element is visible
|
||||
if (!elementInfo.isVisible) {
|
||||
return {
|
||||
error: `Element with selector "${selector}" is not visible`,
|
||||
elementInfo,
|
||||
};
|
||||
}
|
||||
|
||||
// Check if element is an input, textarea, or select
|
||||
const validTags = ['INPUT', 'TEXTAREA', 'SELECT'];
|
||||
const validInputTypes = [
|
||||
'text',
|
||||
'email',
|
||||
'password',
|
||||
'number',
|
||||
'search',
|
||||
'tel',
|
||||
'url',
|
||||
'date',
|
||||
'datetime-local',
|
||||
'month',
|
||||
'time',
|
||||
'week',
|
||||
'color',
|
||||
];
|
||||
|
||||
if (!validTags.includes(element.tagName)) {
|
||||
return {
|
||||
error: `Element with selector "${selector}" is not a fillable element (must be INPUT, TEXTAREA, or SELECT)`,
|
||||
elementInfo,
|
||||
};
|
||||
}
|
||||
|
||||
// For input elements, check if the type is valid
|
||||
if (
|
||||
element.tagName === 'INPUT' &&
|
||||
!validInputTypes.includes(element.type) &&
|
||||
element.type !== null
|
||||
) {
|
||||
return {
|
||||
error: `Input element with selector "${selector}" has type "${element.type}" which is not fillable`,
|
||||
elementInfo,
|
||||
};
|
||||
}
|
||||
|
||||
// Scroll element into view
|
||||
element.scrollIntoView({ behavior: 'auto', block: 'center', inline: 'center' });
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
// Focus the element
|
||||
element.focus();
|
||||
|
||||
// Fill the element based on its type
|
||||
if (element.tagName === 'SELECT') {
|
||||
// For select elements, find the option with matching value or text
|
||||
let optionFound = false;
|
||||
for (const option of element.options) {
|
||||
if (option.value === value || option.text === value) {
|
||||
element.value = option.value;
|
||||
optionFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!optionFound) {
|
||||
return {
|
||||
error: `No option with value or text "${value}" found in select element`,
|
||||
elementInfo,
|
||||
};
|
||||
}
|
||||
|
||||
// Trigger change event
|
||||
element.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
} else {
|
||||
// For input and textarea elements
|
||||
|
||||
// Clear the current value
|
||||
element.value = '';
|
||||
element.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
|
||||
// Set the new value
|
||||
element.value = value;
|
||||
|
||||
// Trigger input and change events
|
||||
element.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
element.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
|
||||
// Blur the element
|
||||
element.blur();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Element filled successfully',
|
||||
elementInfo: {
|
||||
...elementInfo,
|
||||
value: element.value, // Include the final value in the response
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
error: `Error filling element: ${error.message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an element is visible
|
||||
* @param {Element} element - The element to check
|
||||
* @returns {boolean} - Whether the element is visible
|
||||
*/
|
||||
function isElementVisible(element) {
|
||||
if (!element) return false;
|
||||
|
||||
const style = window.getComputedStyle(element);
|
||||
if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const rect = element.getBoundingClientRect();
|
||||
if (rect.width === 0 || rect.height === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if element is within viewport
|
||||
if (
|
||||
rect.bottom < 0 ||
|
||||
rect.top > window.innerHeight ||
|
||||
rect.right < 0 ||
|
||||
rect.left > window.innerWidth
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if element is actually visible at its center point
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
|
||||
const elementAtPoint = document.elementFromPoint(centerX, centerY);
|
||||
if (!elementAtPoint) return false;
|
||||
|
||||
return element === elementAtPoint || element.contains(elementAtPoint);
|
||||
}
|
||||
|
||||
// Listen for messages from the extension
|
||||
chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => {
|
||||
if (request.action === 'fillElement') {
|
||||
fillElement(request.selector, request.value)
|
||||
.then(sendResponse)
|
||||
.catch((error) => {
|
||||
sendResponse({
|
||||
error: `Unexpected error: ${error.message}`,
|
||||
});
|
||||
});
|
||||
return true; // Indicates async response
|
||||
} else if (request.action === 'chrome_fill_or_select_ping') {
|
||||
sendResponse({ status: 'pong' });
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user