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:
var navigation = browser.navigation();
navigation.loadUrl("https://html5test.teamdev.com");
val navigation = browser.navigation
navigation.loadUrl("https://html5test.teamdev.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://html5test.teamdev.com", Duration.ofSeconds(45));
navigation.loadUrlAndWait("https://html5test.teamdev.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.
var data = TextData.of("post data");
var params = LoadUrlParams.newBuilder(url)
.uploadData(data)
.addExtraHeader(HttpHeader.of("Content-Type", "text/plain"))
.build();
navigation.loadUrl(params);
val params = LoadUrlParams(
url = url,
data = TextData("post data"),
extraHeaders = listOf(HttpHeader("Content-Type", "text/plain"))
)
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());
val indexPage = File("index.html")
navigation.loadUrl(indexPage.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:
var html = "<html><body>Hello</body></html>";
var base64Html = Base64.getEncoder().encodeToString(html.getBytes(UTF_8));
var 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:
var html = "<html><body>Hello</body></html>";
browser.mainFrame().ifPresent(frame -> frame.loadHtml(html));
val html = "<html><body>Hello</body></html>"
browser.mainFrame?.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")) {
var bytes = "<html><body>Hello</body></html>".getBytes();
var job = params.newUrlRequestJob(
UrlRequestJob.Options
.newBuilder(HttpStatus.OK)
.addHttpHeader(HttpHeader.of("Content-Type", "text/html"))
.build());
job.write(bytes);
job.complete();
return InterceptUrlRequestCallback.Response.intercept(job);
}
return InterceptUrlRequestCallback.Response.proceed();
};
var options = EngineOptions.newBuilder(renderingMode)
.addScheme(HTTP, interceptCallback)
.build();
var engine = Engine.newInstance(options);
var 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 options = UrlRequestJobOptions(
status = HttpStatus.OK,
headers = listOf(HttpHeader("Content-Type", "text/html"))
)
val job = params.newUrlRequestJob(options).apply {
write(bytes)
complete()
}
InterceptUrlRequestCallback.Response.intercept(job)
} else {
InterceptUrlRequestCallback.Response.proceed()
}
}
val engine = Engine(renderingMode) {
schemes.add(HTTP, interceptCallback)
}
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++) {
var 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.
var entryCount = navigation.entryCount();
// Remove navigation entries at index.
for (int i = entryCount - 2; i >= 0; i--) {
var 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 StartNavigationCallback.Response.ignore();
}
return StartNavigationCallback.Response.start();
});
navigation.register(StartNavigationCallback { params ->
// Ignore navigation requests to the URLs that start
// with "https://www.google".
if (params.url().startsWith("https://www.google")) {
StartNavigationCallback.Response.ignore()
} else {
StartNavigationCallback.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:
var 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.register(BeforeUrlRequestCallback { params ->
if (params.urlRequest().resourceType() === IMAGE) {
BeforeUrlRequestCallback.Response.cancel()
} else {
BeforeUrlRequestCallback.Response.proceed()
}
})
Navigation events
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:
Load started
To get notifications when content loading has started please use the LoadStarted
event. For example:
navigation.on(LoadStarted.class, event -> {});
navigation.subscribe<LoadStarted> { 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.subscribe<LoadStarted> { event -> }
This event corresponds to the moment when the spinner of the tab stops spinning.
Navigation started
To get notifications when navigation has started please use the NavigationStarted
event. For example:
navigation.on(NavigationStarted.class, event -> {
var url = event.url();
// Indicates whether the navigation will be performed
// in the scope of the same document.
var isSameDocument = event.isSameDocument();
});
navigation.subscribe<NavigationStarted> { event ->
val url = event.url()
// Indicates whether the navigation will be performed
// in the scope of the same document.
val isSameDocument = event.isSameDocument
}
Navigation stopped
To get notifications when navigation has stopped please use the NavigationStopped
event. For example:
navigation.on(NavigationStopped.class, event -> {});
navigation.subscribe<NavigationStopped> { event -> }
This event is fired when navigation is stopped via the Navigation.stop()
method.
Navigation redirected
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.
var url = event.destinationUrl();
});
navigation.subscribe<NavigationRedirected> { event ->
// The navigation redirect URL.
val url = event.destinationUrl()
}
Navigation finished
To get notifications when navigation has finished please use the NavigationFinished
event. For example:
navigation.on(NavigationFinished.class, event -> {
var url = event.url();
var frame = event.frame();
var hasCommitted = event.hasCommitted();
var isSameDocument = event.isSameDocument();
var isErrorPage = event.isErrorPage();
if (isErrorPage) {
var error = event.error();
}
});
navigation.subscribe<NavigationFinished> { 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 -> {
var url = event.url();
var frame = event.frame();
});
navigation.subscribe<FrameLoadFinished> { 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 -> {
var url = event.url();
var error = event.error();
});
navigation.subscribe<FrameLoadFailed> { 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 -> {
var frame = event.frame();
});
navigation.subscribe<FrameDocumentLoadFinished> { event ->
val frame = event.frame()
}
At this point, deferred scripts were executed, and the content scripts marked “document_end” get injected into the frame.