Atualização do modo headless do Chrome: lançamento do recurso --headless=new

O modo headless do Chrome ficou muito melhor!

Peter Kvitek
Peter Kvitek

O modo headless do Chrome ficou muito melhor! Este artigo apresenta uma visão geral dos esforços recentes de engenharia para tornar o Headless mais útil para desenvolvedores, aproximando o Headless do modo "headful" normal do Chrome.

Contexto

Em 2017, o Chrome 59 lançou o chamado modo headless, que permite executar o navegador em um ambiente autônomo sem nenhuma interface visível. Basicamente, executar o Chrome sem o navegador.

O modo headless é uma opção muito usada para automação do navegador com projetos como o Puppeteer ou o ChromeDriver. Confira um exemplo mínimo de linha de comando que mostra como usar o modo headless para criar um arquivo PDF de um determinado URL:

chrome --headless --print-to-pdf https://developer.chrome.com/

Quais são as novidades do Headless?

Antes de nos aprofundarmos nas recentes melhorias do Headless, é importante entender como o "antigo" Headless funciona. O snippet de linha de comando mostrado anteriormente usa a flag de linha de comando --headless, sugerindo que o Headless é apenas um modo de operação do navegador Chrome normal. Surpreendentemente, isso não era realmente verdade. Tecnicamente, o antigo Headless era uma implementação de navegador alternativa e separada que foi enviada como parte do mesmo binário do Chrome. Ele não compartilha o código do navegador Chrome no //chrome.

Como você pode imaginar, implementar e manter esse navegador headless separado gerou muitos custos de engenharia, mas esse não era o único problema. Como a versão headless era uma implementação separada, ela tinha os próprios bugs e recursos que não estavam presentes na versão headless do Chrome. Isso criava uma situação confusa em que qualquer teste automatizado de navegador poderia passar no modo headful, mas falhar no modo headless ou vice-versa, um grande problema para os engenheiros de automação. Ela também excluiu todos os testes automatizados que dependiam da instalação de uma extensão do navegador, por exemplo. O mesmo vale para qualquer outra funcionalidade do navegador: a menos que o Headless tenha uma implementação própria, ela não era compatível.

Em 2021, a equipe do Chrome tentou resolver esse problema e unificar os modos headless e headful de uma vez por todas.

O novo Chrome Headless não é mais uma implementação de navegador separada. Agora, ele compartilha código com o Chrome.

Estamos felizes em anunciar que o novo modo headless já está disponível no Chrome 112. Nesse modo, o Chrome cria, mas não exibe, janelas da plataforma. Todas as outras funcionalidades, atuais e futuras, estão disponíveis sem limitações.

Conheça o novo Headless

Para testar o novo modo headless, transmita a flag de linha de comando --headless=new:

chrome --headless=new

Por enquanto, o antigo modo headless ainda está disponível pelo:

chrome --headless=old

Atualmente, transmitir a flag de linha de comando --headless sem um valor explícito ainda ativa o antigo modo headless, mas planejamos mudar esse padrão para o novo headless ao longo do tempo.

Planejamos remover completamente o antigo Headless do binário do Chrome e parar de oferecer suporte a esse modo no Puppeteer ainda este ano. Como parte dessa remoção, disponibilizaremos o antigo Headless como um binário autônomo separado para os usuários que ainda não podem fazer upgrade.

Novo headless no Puppeteer

Para ativar o novo modo Headless no Puppeteer:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({
  headless: 'new',
  // `headless: true` (default) enables old Headless;
  // `headless: 'new'` enables new Headless;
  // `headless: false` enables "headful" mode.
});

const page = await browser.newPage();
await page.goto('https://developer.chrome.com/');

// …

await browser.close();

Novo headless no Selenium-WebDriver

Para usar o novo modo headless no Selenium-WebDriver:

const driver = await env
  .builder()
  .setChromeOptions(options.addArguments('--headless=new'))
  .build();

await driver.get('https://developer.chrome.com/');

// …

await driver.quit();

Consulte a postagem do blog da equipe do Selenium para mais informações, incluindo exemplos de uso de outras vinculações de linguagem.

Sinalizações de linha de comando específicas sem comando

As sinalizações de linha de comando a seguir estão disponíveis para o novo modo headless.

--dump-dom

A sinalização --dump-dom imprime o DOM serializado da página de destino em stdout. Veja um exemplo:

chrome --headless=new --dump-dom https://developer.chrome.com/

Isso é diferente de simplesmente exibir o código-fonte HTML (o que pode ser feito com curl). Para gerar a saída de --dump-dom, o Chrome primeiro analisa o código HTML em um DOM, executa qualquer <script> que possa alterar o DOM e, em seguida, transforma esse DOM novamente em uma string serializada de HTML.

--screenshot

A flag --screenshot faz uma captura de tela da página de destino e a salva como screenshot.png no diretório de trabalho atual. É especialmente útil em combinação com a flag --window-size. Veja um exemplo:

chrome --headless=new --screenshot --window-size=412,892 https://developer.chrome.com/

--print-to-pdf

A flag --print-to-pdf salva a página de destino como um PDF com o nome output.pdf no diretório de trabalho atual. Veja um exemplo:

chrome --headless=new --print-to-pdf https://developer.chrome.com/

Também é possível adicionar a sinalização --no-pdf-header-footer para omitir o cabeçalho de impressão (com a data e a hora atuais) e o rodapé (com o URL e o número da página).

chrome --headless=new --print-to-pdf --no-pdf-header-footer https://developer.chrome.com/

--timeout

A sinalização --timeout define o tempo máximo de espera (em milissegundos) após o qual o conteúdo da página é capturado por --dump-dom, --screenshot e --print-to-pdf, mesmo que a página ainda esteja sendo carregada.

chrome --headless=new --print-to-pdf --timeout=5000 https://developer.chrome.com/

A flag --timeout=5000 instrui o Chrome a esperar até cinco segundos antes de imprimir o PDF. Assim, esse processo leva no máximo cinco segundos para ser executado.

--virtual-time-budget

Com o --virtual-time-budget, é possível viajar no tempo. Até certo ponto. O Tempo virtual funciona como um "avanço" para qualquer código que depende de tempo (por exemplo, setTimeout/setInterval). Ele força o navegador a executar qualquer código da página o mais rápido possível, fazendo com que a página acredite que o tempo realmente passou.

Para ilustrar o uso, considere esta página de demonstração que incrementa, registra e exibe um contador a cada segundo usando setTimeout(fn, 1000). Confira o código relevante:

<output>0</output>
<script>
  const element = document.querySelector('output');
  let counter = 0;
  setInterval(() => {
    counter++;
    console.log(counter);
    element.textContent = counter;
  }, 1_000);
</script>

Após um segundo, a página conterá "1"; após dois segundos, "2" e assim por diante. Veja como capturar o estado da página após 42 segundos e salvá-lo como PDF:

chrome --headless=new --print-to-pdf --virtual-time-budget=42000 https://mathiasbynens.be/demo/time

--allow-chrome-scheme-url

A sinalização --allow-chrome-scheme-url é necessária para acessar URLs chrome://. Ela está disponível a partir do Chrome 123. Veja um exemplo:

chrome --headless=new --print-to-pdf --allow-chrome-scheme-url chrome://gpu

Depuração

Como o Chrome é efetivamente invisível no modo headless, pode parecer complicado descobrir o que está acontecendo em caso de problemas. Felizmente, é possível depurar a versão headless do Chrome de uma forma muito semelhante à versão headful do Chrome. O truque é iniciar o Chrome no modo headless com a sinalização de linha de comando --remote-debugging-port.

chrome --headless=new --remote-debugging-port=0 https://developer.chrome.com/

Isso imprime um URL exclusivo do WebSocket em stdout, por exemplo:

DevTools listening on ws://127.0.0.1:60926/devtools/browser/b4bd6eaa-b7c8-4319-8212-225097472fd9

Em uma instância principal normal do Chrome, podemos usar a depuração remota do Chrome DevTools para se conectar ao destino headless e inspecioná-lo. Para fazer isso, acesse chrome://inspect, clique no botão Configurar... e insira o endereço IP e o número da porta do URL do WebSocket. No exemplo acima, eu inseri 127.0.0.1:60926. Clique em Concluído e um destino remoto será exibido com todas as guias e outros destinos listados abaixo. Clique em inspect para ter acesso ao Chrome DevTools inspecionando o destino headless remoto, inspect.

O Chrome DevTools pode inspecionar uma página de destino headless remota

Feedback

Queremos receber seu feedback sobre o novo modo headless. Se você tiver algum problema, informe aqui.