Adiciona uma extensão MV3 (Firefox e Chromium/Brave) que insere CPF ou CNPJ
válido direto no campo focado, pelo menu de contexto de qualquer input,
textarea ou contenteditable.
Extensões rodam em sandbox sem acesso a processos, então não há como invocar
os binários cpfgen/cnpjgen; fazer isso exigiria um host de native messaging
registrado no SO. Como os algoritmos são curtos e puros, foram portados para
JS em extension/generator.js, a partir de main.go (CPF) e de
cnpj_generator/cnpj/generate.go (CNPJ), trocando math/rand por
crypto.getRandomValues. test_generator.mjs compara as duas implementações
para que não divirjam.
A inserção usa document.execCommand("insertText"), que emite os eventos
nativos e portanto funciona em campos controlados por React/Vue — atribuir
.value diretamente seria descartado por esses frameworks.
Somente desktop: o Firefox para Android não implementa a API menus.
Testes carregam a extensão de verdade nos dois navegadores e verificam que os
8 itens de menu são criados e que a inserção funciona nos três tipos de campo.
50 lines
1.8 KiB
JavaScript
50 lines
1.8 KiB
JavaScript
// Injetado na aba via chrome.scripting.executeScript. Roda isolado, sem imports:
|
|
// recebe o texto já gerado e o escreve no campo que abriu o menu de contexto.
|
|
function insertIntoFocused(text) {
|
|
const el = document.activeElement;
|
|
if (!el) return false;
|
|
|
|
const tag = el.tagName;
|
|
const isInput = tag === "INPUT" || tag === "TEXTAREA";
|
|
|
|
if (!isInput && !el.isContentEditable) return false;
|
|
|
|
el.focus();
|
|
|
|
// Caminho preferido: execCommand emite os eventos de input nativos, então
|
|
// campos controlados por React/Vue/Angular enxergam a mudança. Uma atribuição
|
|
// direta em .value seria silenciosamente descartada por esses frameworks.
|
|
if (document.execCommand("insertText", false, text)) return true;
|
|
|
|
// Fallback para quando execCommand é bloqueado ou não suportado.
|
|
if (isInput) {
|
|
const start = el.selectionStart ?? el.value.length;
|
|
const end = el.selectionEnd ?? el.value.length;
|
|
|
|
// Usa o setter nativo do prototype: React sobrescreve o setter da instância
|
|
// para rastrear o estado, e escrever por ele não notificaria o framework.
|
|
const proto =
|
|
tag === "TEXTAREA"
|
|
? HTMLTextAreaElement.prototype
|
|
: HTMLInputElement.prototype;
|
|
const setter = Object.getOwnPropertyDescriptor(proto, "value").set;
|
|
setter.call(el, el.value.slice(0, start) + text + el.value.slice(end));
|
|
|
|
const pos = start + text.length;
|
|
el.setSelectionRange(pos, pos);
|
|
} else {
|
|
const sel = window.getSelection();
|
|
if (sel && sel.rangeCount > 0) {
|
|
const range = sel.getRangeAt(0);
|
|
range.deleteContents();
|
|
range.insertNode(document.createTextNode(text));
|
|
sel.collapseToEnd();
|
|
} else {
|
|
el.textContent += text;
|
|
}
|
|
}
|
|
|
|
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
return true;
|
|
}
|