Navegação

Este guia descreve os eventos de navegação e mostra como carregar URLs e arquivos, filtrar requests de navegação, trabalhar com o histórico de navegação, etc.

Carregando URL

Para navegar para um recurso identificado por um URL, você pode utilizar um dos seguintes métodos:

  • Navigation.loadUrl(String url)
  • Navigation.loadUrl(LoadUrlParams params)

O exemplo a seguir mostra como navegar para https://www.google.com utilizando o método Navigation.loadUrl(String):

Navigation navigation = browser.navigation();
navigation.loadUrl("https://www.google.com");
val navigation = browser.navigation()
navigation.loadUrl("https://www.google.com")

O código acima solicita a navegação para o recurso fornecido e sai imediatamente. Ele não espera até que o recurso seja completamente carregado.

Se for necessário bloquear a execução do thread atual até que o recurso seja carregado completamente, utilize o método Navigation.loadUrlAndWait(String url, Duration timeout):

navigation.loadUrlAndWait("https://www.google.com", Duration.ofSeconds(45));
navigation.loadUrlAndWait("https://www.google.com", Duration.ofSeconds(45))

Este método bloqueia a execução do segmento atual até que o frame principal do recurso seja completamente carregado ou até que o tempo limite de 45 segundos seja atingido.

Se a navegação falhar, será lançada a exceção NavigationException.

Se o recurso não tiver sido carregado dentro de um período de tempo limite, será lançada a exceção TimeoutException.

Carregando URL com POST

Para carregar uma página da Web e enviar dados POST, use o método Navigation.loadUrl(LoadUrlParams). O seguinte código demonstra como formar os dados POST e enviá-los para o URL.

TextData data = TextData.of("post data");
LoadUrlParams params = LoadUrlParams.newBuilder(url)
        .uploadData(data)
        .addExtraHeader(HttpHeader.of("Content-Type", "text/plain"))
        .build();
navigation.loadUrl(params);
val data = TextData.of("post data")
val params = LoadUrlParams.newBuilder(url)
        .uploadData(data)
        .addExtraHeader(HttpHeader.of("Content-Type", "text/plain"))
        .build()
navigation.loadUrl(params)

Outros tipos de dados POST também estão disponíveis: MultipartFormData, FormData, ByteData.

Carregando Arquivo

Você pode utilizar os mesmos métodos para carregar arquivos HTML a partir do sistema de arquivos local. Você só precisa fornecer um caminho absoluto para o arquivo HTML em vez de um URL.

Por exemplo:

navigation.loadUrl(new File("index.html").getAbsolutePath());
navigation.loadUrl(File("index.html").absolutePath)

Carregando HTML

Esta seção descreve como carregar HTML num Frame.

Existem duas abordagens possíveis para fazê-lo:

Estas abordagens apresentam as seguintes diferenças:

Funcionalidade URL de Dados Esquema Personalizado
Suporte da ponte JavaScript-Java sim sim
Suporte de InjectJsCallback sim sim
Suporte de InjectCssCallback sim sim
Carregando <iframe> do HTTP sim sim
Carregando <iframe> do sistema de arquivos não não
Carregamento de imagens a partir de HTTP sim sim
Carregar imagens do sistema de arquivos não não
Produção de eventos em rede não sim
Produção de eventos de navegação sim sim
Mostrar PDF e pré-visualização de impressão sim sim
Mostrar PDF e pré-visualização de impressão em <iframe> sim sim

URL de Dados

A ideia desta abordagem é converter o HTML necessário numa string base64, gerar o URI de Dados com a string convertida e carregar este URL como mostrado abaixo:

String html = "<html><body>Hello</body></html>";
String base64Html = Base64.getEncoder().encodeToString(html.getBytes(UTF_8));
String dataUrl = "data:text/html;charset=utf-8;base64," + base64Html;
browser.navigation().loadUrl(dataUrl);
val html = "<html><body>Hello</body></html>"
val base64Html = Base64.getEncoder().encodeToString(html.toByteArray(UTF_8))
val dataUrl = "data:text/html;charset=utf-8;base64,$base64Html"
browser.navigation().loadUrl(dataUrl)

Também é possível utilizar o método Frame.loadHtml(String html), que gera automaticamente o URI de Dados a partir do HTML fornecido:

String html = "<html><body>Hello</body></html>";
browser.mainFrame().ifPresent(frame -> frame.loadHtml(html));
val html = "<html><body>Hello</body></html>"
browser.mainFrame().ifPresent { frame -> frame.loadHtml(html) }

A string URL não deve exceder o comprimento de 2MB devido ao limite do Chromium. Uma tentativa de carregar uma string URL que exceda este limite será ignorada pelo Chromium.

Esquema Personalizado

Outra abordagem para carregar HTML a partir de uma string é baseada em InterceptUrlRequestCallback. A ideia é registrar a chamada de retorno e interceptar um request de URL específico para devolver o HTML necessário como uma resposta HTTP. Por exemplo:

InterceptUrlRequestCallback interceptCallback = params -> {
    if (params.urlRequest().url().endsWith("?hello")) {
        byte[] bytes = "<html><body>Hello</body></html>".getBytes();
        UrlRequestJob job = params.newUrlRequestJob(
                UrlRequestJob.Options
                .newBuilder(HttpStatus.OK)
                .addHttpHeader(HttpHeader.of("Content-Type", "text/html"))
                .build());
        job.write(bytes);
        job.complete();
        return Response.intercept(job);
    }
    return Response.proceed();
};

EngineOptions options = EngineOptions.newBuilder(renderingMode)
        .addScheme(HTTP, interceptCallback)
        .build();
Engine engine = Engine.newInstance(options);
Browser browser = engine.newBrowser();
browser.navigation().loadUrl("http://load.html/?hello");
val interceptCallback = InterceptUrlRequestCallback { params ->
    if (params.urlRequest().url().endsWith("?hello")) {
        val bytes = "<html><body>Hello</body></html>".toByteArray()
        val job = params.newUrlRequestJob(
            UrlRequestJob.Options
                .newBuilder(HttpStatus.OK)
                .addHttpHeader(HttpHeader.of("Content-Type", "text/html"))
                .build()
        )
        job.write(bytes)
        job.complete()
        Response.intercept(job)
    } else {
        Response.proceed()
    }
}

val options = EngineOptions.newBuilder(renderingMode)
    .addScheme(HTTP, interceptCallback)
    .build()
val engine = Engine.newInstance(options)
val browser = engine.newBrowser()
browser.navigation().loadUrl("http://load.html/?hello")

O pedido de URL que termina com ?hello será interceptado e o HTML <html><body>Hello</body></html> será carregado no navegador.

Recarregando

Existem várias opções para recarregar a página Web atualmente carregada:

Recarregar utilizando a cache HTTP:

navigation.reload();
navigation.reload()

Recarregar ignorando a cache HTTP:

navigation.reloadIgnoringCache();
navigation.reloadIgnoringCache()

Recarregar utilizando a cache HTTP e verificar se há repostagem:

navigation.reloadAndCheckForRepost();
navigation.reloadAndCheckForRepost()

Recarregar ignorando a cache HTTP e verificar se há repostagem:

navigation.reloadIgnoringCacheAndCheckForRepost();
navigation.reloadIgnoringCacheAndCheckForRepost()

Parando

Utilize o método Navigation.stop() para cancelar qualquer operação de navegação ou download pendente e parar qualquer elemento dinâmico da página, como sons de fundo e animações. Por exemplo:

navigation.stop();
navigation.stop()

Voltar & Avançar

O JxBrowser permite trabalhar com a lista do histórico de navegação voltar e avançar.

Quando você cria uma instância Browser ele navega para a página web about:blank por padrão, então há sempre uma entrada na lista de navegação voltar e avançar.

Para carregar a localização anterior na lista de voltar e avançar, utilize a seguinte abordagem:

if (navigation.canGoBack()) {
    navigation.goBack();
}
if (navigation.canGoBack()) {
    navigation.goBack()
}

Para carregar a localização seguinte na lista voltar e avançar, utilize:

if (navigation.canGoForward()) {
    navigation.goForward();
}
if (navigation.canGoForward()) {
    navigation.goForward()
}

Para navegar para a entrada num índice específico na lista voltar e avançar, utilize:

if (index >= 0 && index < navigation.entryCount()) {
    navigation.goToIndex(index);
}
if (index >= 0 && index < navigation.entryCount()) {
    navigation.goToIndex(index)
}

Você pode percorrer a lista voltar e avançar e obter detalhes sobre cada entrada de navegação:

for (int index = 0; index < navigation.entryCount(); index++) {
    NavigationEntry navigationEntry = navigation.entryAtIndex(index);
    System.out.println("URL: " + navigationEntry.url());
    System.out.println("Title: " + navigationEntry.title());
}
for (index in 0 until navigation.entryCount()) {
    val navigationEntry = navigation.entryAtIndex(index)
    println("URL: ${navigationEntry.url()}")
    println("Title: ${navigationEntry.title()}")
}

Você pode modificar a lista voltar e avançar removendo as entradas:

// Devolve o número de entradas na lista de voltar/avançar.
int entryCount = navigation.entryCount();
// Remover entradas de navegação no índice.
for (int i = entryCount - 2; i >= 0; i--) {
    boolean success = navigation.removeEntryAtIndex(i);
    System.out.println("Was the navigation entry at the index " + i +
            " successfully removed? " + success);
}
// Retorna o número de entradas na lista de retrocesso/avanço.
val entryCount = navigation.entryCount()
// Remover entradas de navegação no índice.
for (i in entryCount - 2 downTo 0) {
    val success = navigation.removeEntryAtIndex(i)
    println("A entrada de navegação no índice $i foi removida com êxito? $success")
}

Filtragem de URLs

Você pode decidir se o request de navegação para um URL específico deve ser ignorado ou não.

O código seguinte demonstra como ignorar os requests de navegação para todos os URLs que começam por https://www.google:

navigation.set(StartNavigationCallback.class, params -> {
    // Ignore navigation requests to the URLs that start 
    // with "https://www.google"
    if (params.url().startsWith("https://www.google")) {
        return Response.ignore();
    } 
    return Response.start();
});
navigation.set(StartNavigationCallback::class.java, StartNavigationCallback { params ->
    // Ignore navigation requests to the URLs that start 
    // with "https://www.google"
    if (params.url().startsWith("https://www.google")) {
        Response.ignore()
    } else {
        Response.start()
    }
})

Filtragem de Recursos

Utilizando a chamada de retorno BeforeUrlRequestCallback você pode determinar se os recursos como HTML, imagem, arquivo JavaScript ou CSS, favicon, etc. devem ser carregados. Por padrão, todos os recursos são carregados. Para modificar o comportamento padrão, registre a sua própria implementação de retorno de chamada onde você decide quais recursos devem ser cancelados ou carregados.

O exemplo seguinte demonstra como suprimir todas as imagens:

Network network = engine.network();
network.set(BeforeUrlRequestCallback.class, params -> {
    if (params.urlRequest().resourceType() == IMAGE) {
        return BeforeUrlRequestCallback.Response.cancel();
    }
    return BeforeUrlRequestCallback.Response.proceed();
});
val network = engine.network()
network.set(BeforeUrlRequestCallback::class.java, BeforeUrlRequestCallback { params ->
    if (params.urlRequest().resourceType() === IMAGE) {
        BeforeUrlRequestCallback.Response.cancel()
    } else {
        BeforeUrlRequestCallback.Response.proceed()
    }
})

Eventos de Navegação

O carregamento de uma página Web é um processo complexo durante o qual são acionados diferentes eventos de navegação. O diagrama seguinte mostra a ordem pela qual os eventos de navegação podem ser acionados durante o carregamento de uma página Web: Fluxo de eventos de navegação

Carregamento Iniciado

Para receber notificações quando o carregamento do conteúdo tiver começado, utilize o evento LoadStarted. Por exemplo:

navigation.on(LoadStarted.class, event -> {});
navigation.on(LoadStarted::class.java) { event -> }

Este evento corresponde ao momento em que o spinner da aba começa a rodar.

Carregamento Concluído

Para receber notificações quando o carregamento do conteúdo tiver terminado, utilize o evento LoadFinished. Por exemplo:

navigation.on(LoadFinished.class, event -> {});
navigation.on(LoadFinished::class.java) { event -> }

Este evento corresponde ao momento em que o spinner do separador para de rodar.

Para receber notificações sobre o início da navegação, utilize o evento NavigationStarted. Por exemplo:

navigation.on(NavigationStarted.class, event -> {
    String url = event.url();
    // Indica se a navegação será realizada
    // no escopo do mesmo documento.
    boolean isSameDocument = event.isSameDocument();
});
navigation.on(NavigationStarted::class.java) { event ->
    val url = event.url()
    // Indica se a navegação será realizada
    // no escopo do mesmo documento.
    val isSameDocument = event.isSameDocument()
}

Para receber notificações quando a navegação for interrompida, utilize o evento NavigationStopped. Por exemplo:

navigation.on(NavigationStopped.class, event -> {});
navigation.on(NavigationStopped::class.java) { event -> }

Este evento é disparado quando a navegação é interrompida através do método Navigation.stop().

Para receber notificações quando a navegação tiver sido redirecionada para um novo URL, utilize o evento NavigationRedirected. Por exemplo:

navigation.on(NavigationRedirected.class, event -> {
    // O URL de redirecionamento da navegação.
    String url = event.destinationUrl();
});
navigation.on(NavigationRedirected::class.java) { event -> 
    // O URL de redirecionamento da navegação.
    val url = event.destinationUrl()
}

Para receber notificações quando a navegação tiver terminado, utilize o evento NavigationFinished. Por exemplo:

navigation.on(NavigationFinished.class, event -> {
    String url = event.url();
    Frame frame = event.frame();
    boolean hasCommitted = event.hasCommitted();
    boolean isSameDocument = event.isSameDocument();
    boolean isErrorPage = event.isErrorPage();
    if (isErrorPage) {
        NetError error = event.error();
    }
});
navigation.on(NavigationFinished::class.java) { event -> 
    val url = event.url()
    val frame = event.frame()
    val hasCommitted = event.hasCommitted()
    val isSameDocument = event.isSameDocument()
    val isErrorPage = event.isErrorPage()
    if (isErrorPage) {
        val error = event.error()
    }
}

Este evento é ativado quando a navegação é confirmada, abortada ou substituída por uma nova. Para saber se a navegação foi confirmada, utilize NavigationFinished.hasCommitted(); utilize NavigationFinished.isErrorPage() para saber se a navegação resultou em uma página de erro.

Se o evento for chamado porque a navegação foi comprometida, o carregamento do documento ainda estará em curso.

O evento é disparado por navegações do tipo mesmo documento (no escopo do mesmo documento), como navegações de fragmentos ou window.history.pushState()/window.history.replaceState(), que não resultarão em uma mudança de documento. Utilize NavigationFinished.isSameDocument() para verificar se a navegação é do mesmo documento.

Carregamento do Frame Concluído

Para receber notificações quando o conteúdo carregado no Frame tiver terminado, por favor use o evento FrameLoadFinished. Por exemplo:

navigation.on(FrameLoadFinished.class, event -> {
    String url = event.url();
    Frame frame = event.frame();
});
navigation.on(FrameLoadFinished::class.java) { event -> 
    val url = event.url()
    val frame = event.frame()
}

Este evento corresponde ao momento em que o conteúdo do Frame foi completamente carregado.

Falha no Carregamento do Frame

Para receber notificações quando o conteúdo carregado no Frame falhou por algum motivo, use o evento FrameLoadFailed. Por exemplo:

navigation.on(FrameLoadFailed.class, event -> {
    String url = event.url();
    NetError error = event.error();
});
navigation.on(FrameLoadFailed::class.java) { event -> 
    val url = event.url()
    val error = event.error()
}

Carregamento do Documento Frame Concluído

Para receber notificações quando o carregamento do documento Frame tiver terminado, por favor use o evento FrameDocumentLoadFinished. Por exemplo:

navigation.on(FrameDocumentLoadFinished.class, event -> {
    Frame frame = event.frame();
});
navigation.on(FrameDocumentLoadFinished::class.java) { event -> 
    val frame = event.frame()
}

Nesta altura, os scripts diferidos foram executados e os scripts de conteúdo marcados com “document_end” são injetados no frames.

Go Top