APIs e Microsserviços

11 mar, 2013

Conduzindo o Google Chrome via API WebSocket

Publicidade

A instrumentação de um navegador não é para os fracos de coração: meia dúzia de APIs diferentes, mecanismos IPC diferentes e  recursos diferentes de cada fornecedor. Projetos como o WebDriver tentam abstrair essa complexidade para nós e você também pode encontrar dezenas de outros drivers “headless” alavancando oWebKit ou os mecanismos semelhantes. Agora existe mesmo uma especificação W3C WebDriver.

Instrumentação o Google Chrome

No entanto, enquanto criar uma solução genérica é uma tarefa difícil, instrumentar o Chrome acaba sendo um moleza – como eu descobri recentemente ao investigar algumas questões de latência de rede. A partir da versão 18, o Chrome suporta v1.0 do Protocolo Remote Debugging, que expõe todas as capacidades do navegador através de um WebSocket regular!

/Applications/Path To/Google Chrome --remote-debugging-port=9222 # OSX
gt; curl localhost:9222/json
[ {
   "devtoolsFrontendUrl": "/devtools/devtools.html?host=localhost:9222&page=1",
   "faviconUrl": "",
   "thumbnailUrl": "/thumb/chrome://newtab/",
   "title": "New Tab",
   "url": "chrome://newtab/",
   "webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/1"
} ]

Primeiro, nós habilitamos a depuração remota no Chrome (desabilitada por padrão). A partir daí, o Chrome expõe um handler HTTP, o qual nos permite inspecionar todas as abas abertas. Cada guia é um processo isolado e, portanto, herda seu próprio WebSocket, o caminho para o qual a chave é webSocketDebuggerUrl. Com isso, vamos colocar tudo isso junto:

require 'em-http'
require 'faye/websocket'
require 'json'

EM.run do
  # Chrome runs an HTTP handler listing available tabs
  conn = EM::HttpRequest.new('http://localhost:9222/json').get
  conn.callback do
    resp = JSON.parse(conn.response)
    puts "#{resp.size} available tabs, Chrome response: \n#{resp}"

    # connect to first tab via the WS debug URL
    ws = Faye::WebSocket::Client.new(resp.first['webSocketDebuggerUrl'])
    ws.onopen = lambda do |event|
      # once connected, enable network tracking
      ws.send JSON.dump({id: 1, method: 'Network.enable'})

      # tell Chrome to navigate to twitter.com and look for "chrome" tweets
      ws.send JSON.dump({
        id: 2,
        method: 'Page.navigate',
        params: {url: 'http://twitter.com/#!/search/chrome?q=chrome&' + rand(100).to_s}
      })
    end

    ws.onmessage = lambda do |event|
      # print event notifications from Chrome to the console
      p [:new_message, JSON.parse(event.data)]
    end
  end
end

Neste exemplo, vamos dizer para o Chrome habilitar o rastreamento de rede e as notificações, e depois realizar uma busca no Twitter. Com isso, o Chrome vai nos transmitir dezenas de notificações da rede: página inicial de busca, notificações para cada recurso, XHRs, e assim por diante (ex: evento Network.responseReceived). Na verdade, se você deixar a página rodando, você também vai ver os eventos de longa votação disparando para buscar os últimos tweets. Toneladas de informação! Todas à sua disposição.

Depuração remota (e muito mais) com o Chrome

O exemplo acima ilustra uma interação muito simples com a API de rede, mas o protocolo expõe muito mais. Você pode conduzir o depurador JS, controlar a VM V8, modificar e inspecionar o DOM, e acompanhar os eventos da timeline entre meia dúzia de outras capacidades. Finalmente, enquanto a condução de um navegador de desktop é legal, a condução de um navegador no seu telefone é ainda melhor: O Chrome para Android fornece todas as mesmas capacidades.

***

Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em: http://www.igvita.com/2012/04/09/driving-google-chrome-via-websocket-api/