Dois flags aparecem com frequência em exemplos de automação de extensões:
--disable-extensions-except=/path/to/ext --load-extension=/path/to/ext
Tutoriais mais antigos costumam apontar o Puppeteer ou o Playwright para um Chrome instalado e passar esses dois flags.
Na versão padrão do Google Chrome 150 testada aqui, o navegador abriu sem erro na linha de comando, mas o serviço de extensões simplesmente ignorou os dois flags. A automação conectou normalmente e a extensão não apareceu; o sintoma que veio depois foi um timeout de seletor neste harness.
O Chrome registrou a recusa em uma única linha, mas só depois que o logging em stderr foi ativado.
A principal conclusão vem da comparação entre builds do navegador. As duas seções seguintes são observações específicas do harness: uma trata de downloads acionados por extensão e a outra desse caminho de instalação pela linha de comando em fixtures file://. O Thunderbit é, por si só, uma extensão do Chrome, então temos interesse direto nesse problema; o Thunderbit não foi o alvo do teste.
A linha que entrega tudo
Adicione --enable-logging=stderr e abra o Chrome padrão com esses flags:
Referência oficial: código-fonte do serviço de extensões do Chromium.
WARNING:chrome/browser/extensions/extension_service.cc:442]
--disable-extensions-except is not allowed in Google Chrome, ignoring.
Se você usar apenas --load-extension, aparece um aviso próprio, vindo de outra linha do mesmo arquivo:
WARNING:chrome/browser/extensions/extension_service.cc:420]
--load-extension is not allowed in Google Chrome, ignoring.
O Chrome for Testing, com os mesmos flags, não mostra nenhum dos dois.
"Não é permitido no Google Chrome." Esse aviso faz com que a leitura mais natural seja a de uma regra específica do build com marca. Ele prova que este build padrão do Google Chrome 150 ignorou o flag e mostra a origem da mensagem. Mas, por si só, não prova que a versão seja irrelevante nem revela qual é o critério na implementação.
Tudo o que vem a seguir é confirmação e consequência.
Confirmando pelo comportamento
Uma extensão MV3 mínima, criada para este teste e não baixada de lugar nenhum, usa content script, popup, ida e volta de mensagens, extração de DOM e exportação via chrome.downloads contra um fixture local de três produtos. O harness registra seis verificações numeradas: service worker registrado; marcador da extensão com ID; marcador do content script corresponde; botão do popup visível; popup retorna três linhas; CSV capturado contém a linha esperada. Uma asserção separada de concordância compara os sinais independentes do service worker e do marcador na página.
Três builds usaram os mesmos flags explícitos da extensão, o mesmo diretório de extensão, o mesmo fixture HTTP, o modo de contexto persistente com interface visível e um perfil novo para cada braço. O braço do Chrome padrão foi resolvido por channel: 'chrome'; os dois braços que passaram usaram executablePath explícito. As strings de versão foram lidas de volta via CDP.
| Build | Versão informada pelo navegador | Service worker da extensão | Content script injetado | Execução completa com seis verificações |
|---|---|---|---|---|
| Chrome for Testing | Chrome/149.0.7827.55 | ✅ | ✅ | PASSOU 6/6 |
| Google Chrome padrão | Chrome/150.0.7871.187 | ❌ nunca foi registrado | ❌ | FALHOU no passo 0 |
| Chrome for Testing | Chrome/151.0.7922.10 | ✅ | ✅ | PASSOU 6/6 |
Resumos brutos: Chrome for Testing 149, Chrome padrão 150, Chrome for Testing 151 e avisos em stderr.
O Chrome for Testing 149 está incluído de propósito: ele é mais antigo do que o build padrão com o qual está sendo comparado. Se a capacidade tivesse sido removida por causa de um salto de versão, um build mais antigo deveria ficar do lado que funciona e o build com falha deveria ser o mais novo. Em vez disso, o build com falha fica entre dois que funcionam, o que descarta uma progressão simples de versão.
Essa tabela, sozinha, não prova que a trava depende do build, e vale ser preciso sobre o motivo. A única célula com falha é, ao mesmo tempo, a única célula de Chrome padrão e a única célula da versão 150 — build e versão continuam perfeitamente confundidos nesse desenho. Três braços descartam remoção monotônica; não descartam “regrediu no 150 e foi restaurado no 151”. Para concluir isso só pelo comportamento, seria preciso uma célula que esta máquina não consegue produzir: Chrome for Testing 150, ou um build com marca em outra versão.
O aviso reforça a interpretação de build com marca porque nomeia explicitamente o Google Chrome, enquanto o comportamento com três braços descarta apenas uma remoção monotônica simples. Ainda seria necessária uma comparação entre builds na mesma versão ou uma citação de fonte/configuração para provar um mecanismo independente da versão.
Os artefatos por build linkados mostram a execução resumida de cada braço; este artigo não publica uma matriz linha a linha para a contagem adicional de lançamentos, então não usa essa contagem como evidência independente.
Um único sinal não basta para dizer “não carregou”
A primeira versão deste teste decidia se a extensão tinha carregado olhando um marcador que o content script escreve na página — e era assim também que decidia se o content script tinha rodado. Uma leitura, usada como duas medições. Se o marcador está ausente, você não sabe se “a extensão nunca carregou” ou se “ela carregou, mas o content script não injetou”, e essas duas situações pedem correções totalmente diferentes.
Extensões MV3 rodam um service worker em segundo plano, e o Playwright expõe service workers diretamente. Esse é um sinal independente: ele nunca toca a página, então não pode ser confundido com a injeção. O teste atual lê os dois sinais e verifica se batem.
Nas três execuções, eles batem. No Chrome padrão, nenhum service worker chegou a ser registrado — a forma forte da afirmação. Nos dois builds do Chrome for Testing, o worker apareceu em chrome-extension://<id>/background.js antes mesmo de a página ser aberta.
Use o Chrome for Testing, que você talvez já tenha
Os dois braços que passaram usaram executáveis explícitos do Chrome for Testing, reportando as versões 149.0.7827.55 e 151.0.7922.10. Aponte o executablePath do Playwright para o executável fixado, em vez de resolver o Chrome padrão por channel: 'chrome'. npx playwright install chromium instala um build Chromium gerenciado pelo Playwright; isso também pode ser útil para automação, mas não é o mesmo rótulo de distribuição dos dois braços do Chrome for Testing e não foi um quarto braço nesta comparação.
Referência oficial: anúncio do Chrome for Testing.
Leitura relacionada: auditoria de permissões da extensão Chrome Scraper.
Há um benefício extra que dura mais do que este problema específico. O Chrome padrão se atualiza sozinho por baixo de você, então uma suíte que passa hoje pode falhar na terça-feira por motivos que nenhum commit explica. O Chrome for Testing é fixado. Para qualquer coisa cujo resultado ainda precise fazer sentido daqui a três meses, isso importa mais do que conveniência.
Nota do harness: capturando a exportação da extensão
Para uma extensão de scraping, a exportação é o ponto central — é ali que campos se perdem, codificações se corrompem e dados aninhados são achatados de forma errada. Duas coisas sobre isso não estão documentadas e ambas causam problemas.
| O que a execução de exportação registrou | Valor |
|---|---|
playwright_download_event_fired | false |
suggestedFilename | null |
| Nome de arquivo pedido pela extensão | probe-export.csv |
| Nome de arquivo que chegou ao diretório | download.csv |
| Conteúdo do arquivo | o cabeçalho e as três linhas sobrevivem intactos |
Neste harness MV3/Playwright 1.56.0, chrome.downloads não disparou o evento de download do Playwright. Quando a extensão exportou um CSV pela API, waitForEvent('download') não resolveu. O harness capturou o arquivo abrindo uma sessão CDP, configurando o comportamento de download explicitamente e lendo o diretório de saída:
const cdp = await ctx.newCDPSession(page);
await cdp.send('Browser.setDownloadBehavior', {
behavior: 'allow', downloadPath: DIR, eventsEnabled: true,
});
No mesmo harness, esse caminho de captura via CDP não preservou o nome de arquivo solicitado. O cabeçalho e as três linhas permaneceram intactos, mas probe-export.csv acabou como download.csv. Esse é um comportamento observado para os builds do navegador e a configuração testados, não uma regra documentada para qualquer download de extensão. Faça asserções sobre o conteúdo separadamente do nome do arquivo.
Nota do harness: comportamento de file:// para este caminho de instalação
Ao verificar essa parte, uma afirmação muito repetida acabou se mostrando falsa, e ela até tinha entrado em uma versão anterior deste próprio artigo: a de que content scripts não rodam em páginas file:// porque extensões não têm acesso a arquivos por padrão.
Medido nos dois builds do Chrome for Testing, carregando a extensão pela linha de comando e navegando diretamente para file:///…/fixture/index.html: o content script injeta normalmente. Marcador presente, ID da extensão correto, em ambos os builds. Extensões descompactadas carregadas pela linha de comando têm acesso a arquivos; o botão “permitir acesso a arquivos” que muita gente lembra vale para outro caminho de instalação.
Servir fixtures via HTTP ainda é a melhor escolha por padrão, porque uma página file:// não se parece em nada com um alvo real. Mas isso é um argumento de realismo, não um requisito técnico, e o mecanismo normalmente citado para isso está errado.
O que isso não estabelece
- A implementação da trava é conhecida só até a string do aviso. O Chrome diz que esses flags não são permitidos neste build e nomeia o arquivo-fonte. Se isso vem de configuração de build, de plumbing de política ou de outra coisa, o código-fonte não deixou claro.
- Isso é só uma máquina. macOS em arm64, uma versão patch padrão e dois builds do Chrome for Testing. O Chrome muda rápido o bastante para que isso precise ser revalidado, não simplesmente citado.
- Isso vale apenas para o caminho
--load-extension. Instalação.crxempacotada, carregamento em modo desenvolvedor e políticas de allowlist corporativas não foram testados. Nada aqui sustenta a frase “o Chrome padrão não consegue rodar extensões”. - A extensão é um stub criado para esse propósito. Ela exercita a mecânica que toda extensão de scraping usa, mas uma extensão real é maior e pode falhar de maneiras que um stub não falha.
O que eu errei no caminho
Vale dizer sem rodeios, porque os dois erros são o tipo que passa por revisão quando o resultado final parece certo.
A primeira versão deste texto dizia que a falha era silenciosa, que não existia linha de log e que o mecanismo era impossível de saber — que qualquer pessoa dizendo saber estava chutando. O mecanismo estava a um flag de distância, e o Chrome vinha imprimindo isso em nível WARNING o tempo todo. “Não consegui encontrar” tinha sido escrito como “não pode ser encontrado”.
A segunda foi a afirmação sobre file:// acima: repetida a partir de anotações e escrita com um mecanismo confiante antes de ser testada. Uma execução a falsificou.
O padrão é o mesmo nos dois casos. Uma afirmação plausível que ninguém contestaria, levada adiante porque conferir parecia desnecessário. A correção não é ter mais cautela no texto, e sim uma regra sobre o que pode ser afirmado: uma afirmação que não seja rastreável a uma execução não é publicada.
Comandos usados no harness local
O probe versionado, o stub da extensão, o fixture e os resumos brutos estão linkados aqui, mas isso ainda não é um pacote público de reprodução independente. As fontes exatas de download e os checksums do Chrome for Testing 149 e 151 não estão registrados no artigo, e os caminhos dos executáveis abaixo eram entradas locais. Publique essas fontes do navegador junto com um commit estável do repositório antes de apresentar a comparação completa como reproduzível de forma independente.
Leitura relacionada: review do Playwright.
cd harness
npm install playwright@1.56.0
npx playwright install chromium # Chromium gerenciado pelo Playwright; não são os dois braços CfT abaixo
(cd fixture && python3 -m http.server 8731 &)
SP=$(pwd) OUT=cft-149.json LABEL=cft-149 EXE="<Chrome for Testing 149>" node probe_v2.mjs
SP=$(pwd) OUT=stock-150.json LABEL=stock-150 CHANNEL=chrome node probe_v2.mjs
SP=$(pwd) OUT=cft-151.json LABEL=cft-151 EXE="<Chrome for Testing 151>" node probe_v2.mjs
O mesmo script e os mesmos argumentos explícitos da extensão foram usados nos três braços, mas a resolução do executável foi diferente: CHANNEL=chrome para o Chrome padrão e EXE para os dois binários do Chrome for Testing. A versão em cada arquivo de saída é lida do navegador, não confiada a partir do rótulo.
Para a linha de aviso, não é necessário harness:
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--user-data-dir=/tmp/p --enable-logging=stderr \
--load-extension=/path/to/ext about:blank 2>&1 | grep "not allowed"
Em 2026-07-28.
Experimente o Thunderbit para extração de dados da web
Decisão e ressalvas
Na matriz testada, o Google Chrome 150 padrão recusou --disable-extensions-except junto com --load-extension, enquanto o Chrome for Testing 149 e 151 carregaram a extensão. O Chrome registrou a recusa em nível WARNING: "--load-extension is not allowed in Google Chrome, ignoring." A redação apoia uma interpretação de build com marca, e o “sanduíche” de versões descarta uma remoção monotônica simples, mas build e versão continuam confundidos sem um braço de comparação entre builds na mesma versão ou sem evidência de fonte/configuração.
Para um harness de extensão fixo, use um executável de navegador explicitamente versionado e verifique tanto o service worker quanto o marcador do content script. Nesta configuração do Playwright 1.56.0, a exportação da extensão exigiu captura do diretório via CDP e chegou com outro nome de arquivo. O stub carregado pela linha de comando também injetou no fixture file:// testado; outros caminhos de instalação não foram testados.
Experimente o Thunderbit para extração de dados da web Get Started Free
Perguntas frequentes
O --load-extension foi removido do Chrome de vez?
Não. O Chrome for Testing 149.0.7827.55 e 151.0.7922.10 carregaram o stub MV3 descompactado a partir desse flag e concluíram as seis verificações pontuadas. O Google Chrome 150.0.7871.187 padrão recusou e registrou "--load-extension is not allowed in Google Chrome, ignoring". Isso sugere, mas não prova, uma trava ligada ao build com marca: o comportamento descarta uma remoção monotônica simples, enquanto uma regressão específica do Chrome 150 e restaurada no 151 ainda é compatível com o desenho de três braços.
Por que não vejo erro nenhum, e como diferencio “nunca carregou” de “carregou e falhou em silêncio”?
O aviso não aparece com a verbosidade padrão. Inicie com --enable-logging=stderr e ele aparece na hora; sem isso, o Chrome inicia normalmente, mas o serviço de extensões ignora os flags, e o primeiro sintoma no harness é um timeout de seletor. Para separar “nunca carregou” de falha de injeção, use dois sinais independentes: o service worker de background do MV3 e um marcador de content script no DOM alvo. No braço testado do Chrome padrão, nenhum dos dois apareceu; nos dois braços do Chrome for Testing, ambos apareceram.
O que devo usar no lugar para automatizar extensões?
Os braços que passaram usaram executáveis fixados do Chrome for Testing via executablePath. O Chromium gerenciado pelo Playwright é outro binário possível para automação, mas não foi um braço deste teste e não deve ser descrito como a mesma distribuição sem verificar o executável resolvido.
Por que waitForEvent('download') nunca resolve quando a extensão exporta um arquivo?
Nesta configuração MV3/Playwright 1.56.0, o evento não resolveu e nenhum nome de arquivo sugerido foi fornecido. O harness abriu uma sessão CDP, chamou Browser.setDownloadBehavior com um diretório explícito e então leu o arquivo em disco. Nas execuções testadas, os bytes do CSV sobreviveram, mas probe-export.csv chegou como download.csv; combinações mais amplas de extensão e navegador não foram testadas.
O que isso não diz — sobre URLs file:// e sobre o Chrome padrão em geral?
Dois limites, em direções opostas. Content scripts funcionam em URLs file:// para extensões carregadas pela linha de comando — testado nos dois builds do Chrome for Testing, com o script injetando normalmente e o ID da extensão sendo reportado corretamente, então a afirmação comum de que não funcionam (com base na falta de acesso a arquivos por padrão) está errada para esse caminho de instalação. Servir fixtures via HTTP ainda é melhor prática porque se parece com um alvo real, e não porque file:// bloqueia a injeção. Na outra direção: nada aqui diz que o Chrome padrão não consegue rodar extensões. Apenas o caminho de linha de comando --load-extension foi medido. Instalação .crx empacotada, carregamento em modo desenvolvedor e políticas de allowlist corporativas não foram testados, e nenhuma afirmação é feita sobre eles. O escopo é o par de flags que os tutoriais de automação mandam usar.


