fintechdarkpatterns / extension /content-script.js
yujisium's picture
Fintech Dark Patterns NLP Detector - full project upload
5752a28 verified
Raw
History Blame Contribute Delete
5.85 kB
(() => {
if (globalThis.__darkPatternAuditorLoaded) {
return;
}
globalThis.__darkPatternAuditorLoaded = true;
const ATTRIBUTE = "data-dp-audit-id";
const HIGHLIGHT_CLASS = "dp-audit-highlight";
const STYLE_ID = "dp-audit-style";
const CANDIDATE_SELECTOR = [
"button",
"a",
"label",
"input",
"textarea",
"select",
"p",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"[role='button']",
"[aria-label]",
].join(",");
function installStyles() {
if (document.getElementById(STYLE_ID)) {
return;
}
const style = document.createElement("style");
style.id = STYLE_ID;
style.textContent = `
.${HIGHLIGHT_CLASS} {
outline: 3px solid #f97316 !important;
outline-offset: 3px !important;
box-shadow: 0 0 0 6px rgba(249, 115, 22, 0.2) !important;
}
.${HIGHLIGHT_CLASS}[data-dp-audit-severity="critical"] {
outline-color: #e11d48 !important;
box-shadow: 0 0 0 6px rgba(225, 29, 72, 0.22) !important;
}
.${HIGHLIGHT_CLASS}[data-dp-audit-severity="medium"] {
outline-color: #eab308 !important;
box-shadow: 0 0 0 6px rgba(234, 179, 8, 0.2) !important;
}
.${HIGHLIGHT_CLASS}[data-dp-audit-severity="low"] {
outline-color: #0ea5e9 !important;
box-shadow: 0 0 0 6px rgba(14, 165, 233, 0.18) !important;
}
.dp-audit-focus {
animation: dp-audit-pulse 0.8s ease-in-out 3 !important;
}
@keyframes dp-audit-pulse {
50% { filter: brightness(1.4); transform: scale(1.01); }
}
`;
document.documentElement.appendChild(style);
}
function normalizeText(value) {
return String(value || "").replace(/\s+/g, " ").trim();
}
function isVisible(element) {
const style = getComputedStyle(element);
const rect = element.getBoundingClientRect();
return (
style.display !== "none" &&
style.visibility !== "hidden" &&
Number(style.opacity) > 0 &&
rect.width > 1 &&
rect.height > 1
);
}
function extractText(element) {
if (element instanceof HTMLInputElement) {
if (["password", "email", "tel", "number"].includes(element.type)) {
return "";
}
return normalizeText(
element.placeholder || element.getAttribute("aria-label"),
);
}
if (element instanceof HTMLTextAreaElement) {
return normalizeText(
element.placeholder || element.getAttribute("aria-label"),
);
}
if (element instanceof HTMLSelectElement) {
return normalizeText(element.selectedOptions[0]?.textContent);
}
return normalizeText(
element.getAttribute("aria-label") || element.textContent,
);
}
function collectVisibleText(maxItems) {
const items = [];
const seenText = new Set();
const elements = document.querySelectorAll(CANDIDATE_SELECTOR);
let sequence = 0;
for (const element of elements) {
if (items.length >= maxItems) {
break;
}
if (!(element instanceof HTMLElement) || !isVisible(element)) {
continue;
}
const text = extractText(element);
if (text.length < 3 || text.length > 500 || seenText.has(text)) {
continue;
}
seenText.add(text);
const elementId =
element.getAttribute(ATTRIBUTE) ||
`dp-${Date.now().toString(36)}-${sequence++}`;
element.setAttribute(ATTRIBUTE, elementId);
items.push({
id: elementId,
text,
tag: element.tagName.toLowerCase(),
});
}
return items;
}
function clearResults() {
document.querySelectorAll(`.${HIGHLIGHT_CLASS}`).forEach((element) => {
element.classList.remove(HIGHLIGHT_CLASS, "dp-audit-focus");
element.removeAttribute("data-dp-audit-severity");
element.removeAttribute("data-dp-audit-original-title");
if (element.getAttribute("data-dp-audit-title-set") === "1") {
element.removeAttribute("title");
element.removeAttribute("data-dp-audit-title-set");
}
});
}
function applyResults(results) {
installStyles();
clearResults();
for (const result of results) {
const element = document.querySelector(`[${ATTRIBUTE}="${result.id}"]`);
if (!(element instanceof HTMLElement)) {
continue;
}
element.classList.add(HIGHLIGHT_CLASS);
element.setAttribute("data-dp-audit-severity", result.severity || "medium");
if (!element.hasAttribute("title")) {
element.setAttribute(
"title",
`${result.prediction}: ${result.confidence}% calibrated confidence`,
);
element.setAttribute("data-dp-audit-title-set", "1");
}
}
}
function focusResult(elementId) {
const element = document.querySelector(`[${ATTRIBUTE}="${elementId}"]`);
if (!(element instanceof HTMLElement)) {
return false;
}
element.scrollIntoView({ behavior: "smooth", block: "center" });
element.classList.remove("dp-audit-focus");
requestAnimationFrame(() => element.classList.add("dp-audit-focus"));
return true;
}
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
if (message?.type === "COLLECT_VISIBLE_TEXT") {
installStyles();
sendResponse({
items: collectVisibleText(Math.min(Number(message.maxItems) || 250, 300)),
});
return;
}
if (message?.type === "APPLY_AUDIT_RESULTS") {
applyResults(message.results || []);
sendResponse({ applied: true });
return;
}
if (message?.type === "CLEAR_AUDIT_RESULTS") {
clearResults();
sendResponse({ cleared: true });
return;
}
if (message?.type === "FOCUS_AUDIT_RESULT") {
sendResponse({ focused: focusResult(message.elementId) });
}
});
})();