Navigation

This guide describes the navigation events, and shows how to load URLs and files, filter navigation requests, work with navigation history, etc.

Loading URL

To navigate to a resource identified by a URL you can use one of the following methods:

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

The following example shows how to navigate to https://www.google.com using the Navigation.loadUrl(String) method:

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

The code above requests navigation to the given resource and exits immediately. It does not wait until the resource is loaded completely.

If you need to block the current thread execution until the resource is loaded completely, use the Navigation.loadUrlAndWait(String url, Duration timeout) method:

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

This method blocks the current thread execution until the main frame of the resource is loaded completely or until the given 45 seconds timeout is reached.

If navigation fails, the NavigationException exception will be thrown.

If the resource has not been loaded within a timeout, the TimeoutException exception will be thrown.

Loading URL with POST

To load a web page and send POST data, use the Navigation.loadUrl(LoadUrlParams) method. The following code demonstrates how to form the POST data send it to the 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)

Other types of POST data are also available: MultipartFormData, FormData, ByteData.

Loading file

You can use the same methods to load HTML files from the local file system. You just need to provide an absolute path to the HTML file instead of a URL.

For example:

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

Loading HTML

This section describes how to load HTML in a Frame.

There are two possible approaches to do it:

  • By generating and loading Data URL.
  • By registering a Custom Scheme and intercepting specific URL requests.

These approaches have the following differences:

Functionality Data URL Custom Scheme
Support of JavaScript-Java bridge yes yes
Support of InjectJsCallback yes yes
Support of InjectCssCallback yes yes
Loading <iframe> from HTTP yes yes
Loading <iframe> from the file system no no
Loading images from HTTP yes yes
Loading images from the file system no no
Producing network events no yes
Producing navigation events yes yes
Showing PDF and Print Preview yes yes
Showing PDF and Print Preview in <iframe> yes yes

Data URL

The idea of this approach is to convert the required HTML to a base64 string, generate the Data URI with the converted string, and load this URL as shown below:

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)

You can also use the Frame.loadHtml(String html) method, it automatically generates the data URI from the given HTML:

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) }

The URL string must not exceed the length of 2MB due to the Chromium limit. An attempt to load URL string that exceeds this limit will be ignored by Chromium.

Custom scheme

Another approach to load HTML from string is based on InterceptUrlRequestCallback. The idea is to register the callback and intercept a specific URL request to return the required HTML as an HTTP response. For example:

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")

URL request that ends up with ?hello will be intercepted and the <html><body>Hello</body></html> HTML will be loaded in the browser.

Reloading

There are several options to reload the currently loaded web page:

Reload using HTTP cache:

navigation.reload();
navigation.reload()

Reload ignoring HTTP cache:

navigation.reloadIgnoringCache();
navigation.reloadIgnoringCache()

Reload using HTTP cache and check for repost:

navigation.reloadAndCheckForRepost();
navigation.reloadAndCheckForRepost()

Reload ignoring HTTP cache and check for repost:

navigation.reloadIgnoringCacheAndCheckForRepost();
navigation.reloadIgnoringCacheAndCheckForRepost()

Stopping

Use the Navigation.stop() method to cancel any pending navigation or download operation, and stop any dynamic page elements, such as background sounds and animations. For example:

navigation.stop();
navigation.stop()

Back & forward

JxBrowser allows working with the navigation back-forward history list.

When you create a Browser instance it navigates to the about:blank web page by default, so there is always one entry in the navigation back-forward list.

To load the previous location in the back-forward list use the following approach:

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

To load the next location in the back-forward list use:

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

To navigate to the entry at a specific index in the back-forward list use:

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

You can go through the back-forward list and get the details about every navigation entry:

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()}")
}

You can modify the back-forward list by removing the entries:

// Returns the number of entries in the back/forward list.
int entryCount = navigation.entryCount();
// Remove navigation entries at index.
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);
}
// Returns the number of entries in the back/forward list.
val entryCount = navigation.entryCount()
// Remove navigation entries at index.
for (i in entryCount - 2 downTo 0) {
    val success = navigation.removeEntryAtIndex(i)
    println("Was the navigation entry at the index $i successfully removed? $success")
}

Filtering URLs

You can decide whether navigation request to a specific URL should be ignored or not.

The following code demonstrates how to ignore navigation requests to all URLs that start with 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()
    }
})

Filtering resources

Using the BeforeUrlRequestCallback callback you can determine whether the resources such as HTML, image, JavaScript or CSS file, favicon, etc. should be loaded. By default, all resources are loaded. To modify the default behavior register your own callback implementation where you decide what resources should be canceled or loaded.

The following example demonstrates how to suppress all images:

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()
    }
})

Loading a web page is a complex process during which different navigation events are fired. The following diagram shows the order in which the navigation events might be fired when loading a web page: Navigation Events Flow

Load started

To get notifications when content loading has started please use the LoadStarted event. For example:

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

This event corresponds to the moment when the spinner of the tab starts spinning.

Load finished

To get notifications when content loading has finished please use the LoadFinished event. For example:

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

This event corresponds to the moment when the spinner of the tab stops spinning.

To get notifications when navigation has started please use the NavigationStarted event. For example:

navigation.on(NavigationStarted.class, event -> {
    String url = event.url();
    // Indicates whether the navigation will be performed
    // in the scope of the same document.
    boolean isSameDocument = event.isSameDocument();
});
navigation.on(NavigationStarted::class.java) { event ->
    val url = event.url()
    // Indicates whether the navigation will be performed
    // in the scope of the same document.
    val isSameDocument = event.isSameDocument()
}

To get notifications when navigation has stopped please use the NavigationStopped event. For example:

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

This event is fired when navigation is stopped via the Navigation.stop() method.

To get notifications when navigation has been redirected to a new URL please use the NavigationRedirected event. For example:

navigation.on(NavigationRedirected.class, event -> {
    // The navigation redirect URL.
    String url = event.destinationUrl();
});
navigation.on(NavigationRedirected::class.java) { event -> 
    // The navigation redirect URL.
    val url = event.destinationUrl()
}

To get notifications when navigation has finished please use the NavigationFinished event. For example:

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()
    }
}

This event is fired when navigation is committed, aborted, or replaced by a new one. To know if the navigation has committed, use NavigationFinished.hasCommitted(); use NavigationFinished.isErrorPage() to know if the navigation resulted in an error page.

If the event is called because the navigation committed, the document load will still be ongoing.

The event is fired by same-document (in the scope of the same document) navigations, such as fragment navigations or window.history.pushState()/window.history.replaceState(), which will not result in a document change. Please use NavigationFinished.isSameDocument() to check if it is a same-document navigation.

Frame load finished

To get notifications when content loading in the Frame has finished please use the FrameLoadFinished event. For example:

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()
}

This event corresponds to the moment when the content in the Frame has been loaded completely.

Frame load failed

To get notifications when content loading in the Frame has failed for some reason, use the FrameLoadFailed event. For example:

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()
}

Frame document load finished

To get notifications when the document loading in the Frame has finished please use the FrameDocumentLoadFinished event. For example:

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

At this point, deferred scripts were executed, and the content scripts marked “document_end” get injected into the frame.

Go Top