cpfgen/extension/background.js
Lucas F. 7632cfde2c feat: extensão de navegador para gerar CPF/CNPJ no menu de contexto
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.
2026-09-03 17:57:50 -03:00

87 lines
2.3 KiB
JavaScript

import {
generateCPF,
formatCPF,
generateCNPJ,
generateCNPJAlpha,
formatCNPJ,
} from "./generator.js";
// Firefox expõe `browser`, Chromium expõe `chrome`. As APIs usadas aqui são
// compatíveis nos dois, então basta escolher o objeto disponível.
const api = typeof browser !== "undefined" ? browser : chrome;
const MENU_ITEMS = [
{ id: "cpf-formatted", parentId: "cpf", title: "Formatado (000.000.000-00)" },
{ id: "cpf-raw", parentId: "cpf", title: "Só números" },
{
id: "cnpj-formatted",
parentId: "cnpj",
title: "Formatado (00.000.000/0000-00)",
},
{ id: "cnpj-raw", parentId: "cnpj", title: "Só números" },
{ id: "cnpj-alpha-formatted", parentId: "cnpj", title: "Alfanumérico" },
{
id: "cnpj-alpha-raw",
parentId: "cnpj",
title: "Alfanumérico, só números",
},
];
function buildMenus() {
api.contextMenus.removeAll(() => {
// contexts: "editable" faz o menu aparecer apenas em input, textarea e
// elementos contenteditable — nunca no resto da página.
api.contextMenus.create({
id: "cpf",
title: "Gerar CPF",
contexts: ["editable"],
});
api.contextMenus.create({
id: "cnpj",
title: "Gerar CNPJ",
contexts: ["editable"],
});
for (const item of MENU_ITEMS) {
api.contextMenus.create({ ...item, contexts: ["editable"] });
}
});
}
api.runtime.onInstalled.addListener(buildMenus);
api.runtime.onStartup.addListener(buildMenus);
function valueFor(menuItemId) {
switch (menuItemId) {
case "cpf-formatted":
return formatCPF(generateCPF());
case "cpf-raw":
return generateCPF();
case "cnpj-formatted":
return formatCNPJ(generateCNPJ());
case "cnpj-raw":
return generateCNPJ();
case "cnpj-alpha-formatted":
return formatCNPJ(generateCNPJAlpha());
case "cnpj-alpha-raw":
return generateCNPJAlpha();
default:
return null;
}
}
api.contextMenus.onClicked.addListener((info, tab) => {
const value = valueFor(info.menuItemId);
if (value === null || !tab?.id) return;
api.scripting.executeScript({
target: { tabId: tab.id, frameIds: [info.frameId ?? 0] },
files: ["insert.js"],
});
api.scripting.executeScript({
target: { tabId: tab.id, frameIds: [info.frameId ?? 0] },
func: (text) => insertIntoFocused(text),
args: [value],
});
});